title: Spotify API CORS error with React front-end and Node back-end
body: <p>I have a vanilla JavaScript web app that uses the Spotify API and it works fine. I'm in the process of porting it over to a React front-end and NodeJS/Express back-end but I'm receiving this issue.
  When the user clicks on the &quot;login&quot; button it shows this error in browser console (private info redacted):</p>
  <pre><code>Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at 
  https://accounts.spotify.com/authorize?response_type=code&amp;client_id=xxx&amp;
  scope=playlist-modify-public%20playlist-modify-private
  &amp;redirect_uri=http%3A%2F%2Flocalhost%3A8888%2Fcallback%2F&amp;state=xxxxx&amp;show_dialog=true. 
  (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
  </code></pre>
  <p>This is the button in the home page (React):</p>
  <pre><code>&lt;button
      onClick={login}
      type=&quot;button&quot;
      className=&quot;btn btn-primary login-btn col-sm-12 col-lg-6&quot;&gt;
      Login with Spotify
  &lt;/button&gt;
  
  async function login() {
      console.log(&quot;Sent Login Request&quot;);
      await fetch(`/api/login`, {
        method: &quot;GET&quot;,
        headers: { &quot;Content-Type&quot;: &quot;application/json&quot; },
      });
    }
  </code></pre>
  <p>In the front-end <code>package.json</code> I have set up proxy:</p>
  <p><code>&quot;proxy&quot;: &quot;http://localhost:8888&quot;</code></p>
  <p>This is the <code>app.js</code> (back-end) code that (I think) is relevant to this problem:</p>
  <pre><code>app
    .use(express.static(directoryPath))
    .use(helmet())
    .use(compression())
    .use(cors())
    .use(cookieParser());
  
  app.use(function (req, res, next) {
    res.header(&quot;Access-Control-Allow-Origin&quot;, &quot;localhost&quot;); // update to match the domain you will make the request from
    res.header(
      &quot;Access-Control-Allow-Headers&quot;,
      &quot;Origin, X-Requested-With, Content-Type, Accept&quot;
    );
    next();
  });
  
  app.get(&quot;/api/login&quot;, function (req, res) {
    console.log(&quot;Started LOGIN flow&quot;);
    let state = generateRandomString(16);
    res.clearCookie(stateKey);
    res.cookie(stateKey, state);
  
    let scope = &quot;playlist-modify-public playlist-modify-private&quot;;
    res.redirect(
      &quot;https://accounts.spotify.com/authorize?&quot; +
        querystring.stringify({
          response_type: &quot;code&quot;,
          client_id: _client_id,
          scope: scope,
          redirect_uri: _redirect_uri,
          state: state,
          show_dialog: true, // show dialog to allow users to log out
        })
    );
  });
  
  app.get(&quot;/callback&quot;, function (req, res) {
    let code = req.query.code || null;
    let state = req.query.state || null;
    let storedState = req.cookies ? req.cookies[stateKey] : null;
  
    if (state === null || state !== storedState) {
      res.redirect(
        &quot;/#&quot; +
          querystring.stringify({
            error: &quot;State mismatch&quot;,
          })
      );
    } else {
      res.clearCookie(stateKey);
      // request auth
      const params = {
        client_id: _client_id,
        client_secret: _client_secret,
        redirect_uri: _redirect_uri,
        code,
        grant_type: &quot;authorization_code&quot;,
      };
  
      ax({
        method: &quot;post&quot;,
        url: &quot;https://accounts.spotify.com/api/token&quot;,
        params,
        headers: {
          &quot;Content-Type&quot;: &quot;application/x-www-form-urlencoded&quot;,
        },
      })
        .then((response) =&gt; {
          let token = response.data.access_token;
          res.redirect(
            &quot;/#&quot; +
              querystring.stringify({
                authorized: &quot;access_granted&quot;,
                token: token,
              })
          );
        })
        .catch((error) =&gt; {
          console.log(&quot;Failed to login: &quot; + error);
          res.redirect(
            &quot;/#&quot; + querystring.stringify({ authorized: &quot;access_denied&quot; })
          );
        });
    }
  });
  </code></pre>