3

I have a React/Node/Express web app up and running with two separate servers. React is running on localhost:8080 and proxying API requests to my node server running on localhost:3000. This will be my setup for production, so if possible, I would like to leave this structure intact.

The problem I'm running into is part of my API needs to redirect the user to a page to grab a token from the Spotify API, and then redirect back to my correct page upon successful authentication.

The API call proxy is being done like this:

const axios = require('axios');

const axiosInstance = axios.create({
    baseURL: process.env.NODE === 'production' ? '' : 'http://localhost:3000'
});

module.exports = axiosInstance;

Up to this point, this works great, as I can keep my React and Node server completely separate.

As I mentioned above, I need to redirect my React front-end through an API call on my back-end, to a different page (Spotify authentication) to authenticate users, which then Spotify redirects back to my site after a user allows or disallows access to their Spotify accounts.

On my front-end - localhost:8080, the user clicks a button which calls this function.

authenticate = () => {
    axios.get('/login')
        .then(data => console.log(data));
}

Which calls this endpoint on localhost:3000.

router.get('/', (req, res) => {
    let scopes = 'user-read-private user-read-email';
    let client = process.env.SPOTIFY_CLIENT;
    let redirect = 'http://localhost:3000/login/redirect';

    res.redirect(`https://accounts.spotify.com/authorize?response_type=code'
                &client_id=${client}${scopes ? `&scope=${encodeURIComponent(scopes)}` : ''}
                &redirect_uri=${encodeURIComponent(redirect)}&state=${state}`);
});

When I click the login button on my front-end, I get this response, which I believe to be from Spotify as I can successfully make cross-origin requests to other endpoints in my API.

Failed to load... No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.

However, when I manually navigate to localhost:3000/login, the redirect works and I'm sent to the Spotify authentication page.

In addition, I tested trying to redirect my front-end to any url, and it doesn't work.

So my question is, how can I redirect my front-end through an API call from a server that doesn't also serve my static front-end content?

23k
  • 1,596
  • 3
  • 23
  • 52
  • have you tried using any cors packages? like https://www.npmjs.com/package/cors ? – Gavin Thomas Mar 26 '18 at 03:31
  • @GavinThomas Yes. I have CORS installed on my server. I should clarify, this response is from Spotify, not my API. I'm able to successfully make requests to my API. – 23k Mar 26 '18 at 03:32
  • And you registered your redirect url with the spotify account you have a token with? https://github.com/spotify/web-api-auth-examples ? – Gavin Thomas Mar 26 '18 at 03:39
  • @GavinThomas Yes. As I said it works correctly when I navigate directly to the API url. – 23k Mar 26 '18 at 03:44
  • Since direct requests work, try making a new request to Spotify and return the response, that is instead of redirecting the original 8080 request. Spotify might detect request URL (which should read 8080) instead of 3000 specified here ==> `redirect_uri=${encodeURIComponent(redirect)}`. If all requests to that API will certainly be proxies, then change the port in your queries to 8080 – I Want Answers Mar 28 '18 at 07:49
  • @IWantAnswers I'm not sure I completely follow what you're suggesting. – 23k Mar 28 '18 at 13:55
  • Instead of res.redirect, use http.get or fetch or any other method of your choice to make a request to the Spotify API. Then render the result or handle it as you please in the callback or promise depending on which method you use. – I Want Answers Mar 28 '18 at 14:14
  • @IWantAnswers Got it. I will give that a try after work. – 23k Mar 28 '18 at 14:24
  • @IWantAnswers I gave this a try. I do get a response from Spotify, but there's nothing useful contained in it. I do think that the redirect is necessary because I have to physically click the approve button for Spotify to send back the needed secret key to make requests. So, my question for now remains unanswered. – 23k Mar 28 '18 at 23:25
  • Surely there has to be a way to redirect my front-end even though my back-end isn't serving my static content? – 23k Mar 28 '18 at 23:27
  • You didn't mention the Spotify process involves more than one step. In that case, you might make the request straight to the URL their approve button is posting to. Or you can validate the response of your new request for the presence of the word "approve" and redirect to where you'd be given the secret key. – I Want Answers Mar 29 '18 at 04:21

1 Answers1

4

The reason this isn't working is because your axios.get('/login') is an AJAX request to your server which your server is then telling the AJAX request to be redirected to Spotify, but Spotify doesn't have your front-end server registered so you get the CORS error, since your request was initiated while on the front-end server URL.

Your AJAX request is following that redirect that was given, not trying to load your browser on http://localhost:3000, which would then redirect it to the Spotify authorization page.

Now, rather than doing:

authenticate = () => {
    axios.get('/login')
        .then(data => console.log(data));
}

You could do:

authenticate = () => {
    window.location.href = "http://localhost:3000"
}

This would direct your front-end application to visit the URL of your API server and follow the redirect immediately to Spotify and then you could authorize and be redirected back to your API server, which could then redirect back to your front-end application.

Truthfully, this is a bit overkill, since presumably your React application and node application look like they would be running on the same domain outside of development, given the relative URL's you're using. If you're using create-react-app, you should follow their instructions on configuring your proxy via this or manually via this.

tl;dr you need to tinker with how your proxy is configured or change from doing an AJAX request there to actually going directly to /login (not doing an AJAX request).

mootrichard
  • 3,581
  • 13
  • 25
  • This worked. I had looked at specifying a proxy inside of `package.json` but decided to give this a try (I'm not using CRA). I do see your point that it is a bit overkill. I mostly set up this project because I wanted the flexibility to write my back-end in any language. I had done some research before about querying different back-ends and that's how I found out about setting the axios base url. – 23k Mar 29 '18 at 00:12