Setting up Spotify authentication with React and Docker Nginx proxy

I’m working on a React app with multiple Docker services and need help configuring Spotify OAuth flow

Hi everyone! I have a dockerized application that needs to handle Spotify authentication alongside my regular user auth. The project structure looks like this:

project/
  api/
  proxy/
       prod.conf   
       Dockerfile-prod  
  frontend/
        build/
        config/
        Dockerfile-prod
        node_modules/
        package.json
        public/
        src/
           Main.jsx   
           modules/ 
                     SpotifyAuth.jsx                  
 music-service/ 
                Dockerfile-prod
                node_modules
                package.json
                oauth/
                      server.js

The app requires two separate authentication steps:

  1. My internal user authentication (already working)
  2. Spotify OAuth which needs a redirect URL and returns an access token

For Spotify auth, users should visit a login page at http://localhost:9000, authorize the app, and get redirected back with tokens.

My Docker configuration:

  proxy:
    build:
      context: ./services/proxy
      dockerfile: Dockerfile-prod
    restart: always
    ports:
      - 80:80
    depends_on:
      - api
      - frontend
      - music-service

  frontend:
    build:
      context: ./services/frontend
      dockerfile: Dockerfile-prod
    volumes:
      - './services/frontend:/app'
      - '/app/node_modules'
    ports:
      - 3001:3000
    environment:
      - NODE_ENV=development
      - REACT_APP_API_URL=${REACT_APP_API_URL}
    depends_on:
      - api

  music-service:
    build:
      context: ./services/music-service
      dockerfile: Dockerfile-prod
    volumes:
      - './services/music-service:/app'
      - '/app/node_modules'
    ports:
      - 9000:3000
    environment:
      - NODE_ENV=development
    depends_on:
      - api
      - frontend

Nginx config:

server {
  listen 80;
  listen 9000;

  location / {
    proxy_pass        http://frontend:3000;
    proxy_redirect    default;
    proxy_set_header  Host $host;
    proxy_set_header  X-Real-IP $remote_addr;
  }

  location /api {
    proxy_pass        http://api:5000;
    proxy_redirect    default;
    proxy_set_header  Host $host;
  }
}

React component for Spotify login:

import React, { Component } from 'react';

class SpotifyAuth extends Component {
    render() {
        return (
            <div className='spotify-login'>
                <a href='http://localhost:9000'> Connect to Spotify </a>
            </div>
        );
    }
}

export default SpotifyAuth;

Express server for OAuth:

const express = require('express');
const request = require('request');
const querystring = require('querystring');

const CLIENT_ID = 'my_client_id';
const CLIENT_SECRET = 'my_secret';
const REDIRECT_URL = 'http://localhost:9000';

const app = express();

app.get('/authorize', function(req, res) {
  const state = Math.random().toString(36);
  
  const scope = 'user-read-private user-read-email';
  res.redirect('https://accounts.spotify.com/authorize?' +
    querystring.stringify({
      response_type: 'code',
      client_id: CLIENT_ID,
      scope: scope,
      redirect_uri: REDIRECT_URL,
      state: state
    }));
});

app.get('/callback', function(req, res) {
  const code = req.query.code || null;
  
  const authConfig = {
    url: 'https://accounts.spotify.com/api/token',
    form: {
      code: code,
      redirect_uri: REDIRECT_URL,
      grant_type: 'authorization_code'
    },
    headers: {
      'Authorization': 'Basic ' + (Buffer.from(CLIENT_ID + ':' + CLIENT_SECRET).toString('base64'))
    },
    json: true
  };

  request.post(authConfig, function(error, response, body) {
    if (!error && response.statusCode === 200) {
      const accessToken = body.access_token;
      const refreshToken = body.refresh_token;

      res.redirect('http://localhost:3001/#' +
        querystring.stringify({
          access_token: accessToken,
          refresh_token: refreshToken
        }));
    }
  });
});

app.listen(9000);

Right now when I click the Spotify login link, I get a connection refused error. How should I configure the nginx proxy to properly route between my main React app and the Spotify OAuth service? I need users to start at the main app, go through Spotify auth, and return back to the main app with the tokens.

This is a Docker networking issue with your OAuth callback flow. I ran into the same thing with Spotify auth in a microservices setup. Your music-service runs on port 9000, but nginx isn’t proxying those requests. The problem? Your React app hits localhost:9000 directly, but in Docker, localhost points to the container itself, not your host machine. Here’s what you need to fix: Change your REDIRECT_URL in Express to go through nginx instead of localhost:9000. Use something like ‘http://localhost/music/callback’. Update your nginx config with a location block for /music that proxies to http://music-service:3000. Make your SpotifyAuth component link to ‘http://localhost/music/authorize’ instead of hitting port 9000 directly. This routes everything through your nginx proxy, which can properly handle traffic between services. The callback will also redirect back to your frontend through the proxy instead of directly to port 3001.

Your nginx config is missing the routing for your music-service that handles Spotify OAuth. You need to add a location block that forwards requests to your music-service container on port 9000. I ran into this exact same issue last year - nginx wasn’t routing the OAuth requests properly. Try this nginx config: nginx server { listen 80; location /spotify { proxy_pass http://music-service:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location / { proxy_pass http://frontend:3000; proxy_set_header Host $host; } location /api { proxy_pass http://api:5000; proxy_set_header Host $host; } } Then change your React component to use http://localhost/spotify/authorize instead of http://localhost:9000. Make sure your music-service routes are prefixed with /spotify or adjust the proxy_pass to match. That connection refused error means nginx doesn’t know where to route the request.