2
votes

I am working on an app that uses Spotify Node web API and having trouble when multiple users login into my application. I am successfully able to go through authentication flow and get the tokens and user ID after a user logs in. I am using the Authorization Code to authorize user (since I would like to get refresh tokens after expiration). However, the current problem is that getUserPlaylists function described here (FYI, if the first argument is undefined, it will return the playlists of the authenticated user) returns playlists of the most recently authenticated user instead of the user currently using the app.

Example 1: if user A logins in to the application, it will get its playlists fine. If user B logins in to the application, it also sees its own playlists. BUT, if user A refreshes the page, user A sees the playlists of the user B (instead of its own, user A playlists).

Example 2: user A logs in, user B can see user A's playlists just by going to the app/myplaylists route.

My guess is, the problem is with this section of the code

    spotifyApi.setAccessToken(access_token);
    spotifyApi.setRefreshToken(refresh_token);

The latest user tokens override whatever user was before it and hence the previous user is losing grants to do actions such as viewing its own playlists.

  • Expected behavior: user A sees own playlists after user B logs in event after refreshing the page.
  • Actual behavior: user A sees user B's playlists after user B logged in and user A refreshes the page.

I am aware that I could use the tokens without using the Spotify Node API and just use the tokens to make requests and it should probably be fine, however, it would be great to still be able to use the Node API and to handle multiple users.

Here is the portion of code that most likely has problems:

export const createAuthorizeURL = (
    scopes = SCOPE_LIST,
    state = 'spotify-auth'
) => {
    const authUrl = spotifyApi.createAuthorizeURL(scopes, state);

    return {
        authUrl,
        ...arguments
    };
};

export async function authorizationCodeGrant(code) {
    let params = {
        clientAppURL: `${APP_CLIENT_URL || DEV_HOST}/app`
    };

    try {
        const payload = await spotifyApi.authorizationCodeGrant(code);
        const { body: { expires_in, access_token, refresh_token } } = payload;

        spotifyApi.setAccessToken(access_token);
        spotifyApi.setRefreshToken(refresh_token);

        params['accessToken'] = access_token;
        params['refreshToken'] = refresh_token;

        return params;
    } catch (error) {
        return error;
    }

    return params;
}

export async function getMyPlaylists(options = {}) {
    try {
        // if undefined, should return currently authenticated user
        return await spotifyApi.getUserPlaylists(undefined, options);
    } catch (error) {
         return error;
    }
}

Would appreciate any help on this. I am really excited about what I am making so it would mean a LOT if someone could help me find the issue...

1

1 Answers

3
votes

You're on the right track. When you set your access token and refresh token, though, you're setting it for your entire application, and all users who call your server will use it. Not ideal.

Here's a working example of the Authorization Code Flow in Node: https://glitch.com/edit/#!/spotify-authorization-code

As you can see, it uses a general instance of SpotifyWebApi to handle authentication, but it instantiates a new loggedInSpotifyApi for every request to user data, so you get the data for the user who's asking for it.

If you want to use the above example, you can just start editing to "remix" and create your own copy of the project.

Happy hacking!