Spotify Auth access token givin error code 400

50 views Asked by At

I have been trying to get my spotify access tokens and refresh tokens for my react native application and its been giving me serious issues. https://docs.expo.dev/guides/authentication/#spotify I was able to get my code and state using the useAuthRequest. Now i have tried using the code to get my access tokens and refresh tokens for my application on the client but i keep encountering the error 400. So checking other stack overflow issues i realized handling it on the server would be better so i decided to create an express server to try and get the access code

router.post('/get-spotify-access-code',(req: Request, res: Response)=>{
  console.log(req.body)
  const accessCode = req.body;
  var authOptions:AuthOptions = {
    url: 'https://accounts.spotify.com/api/token',
    form: {
      code: accessCode,
      redirect_uri: redirectUri,
      grant_type: 'authorization_code'
    },
    headers: {
      'content-type': 'application/x-www-form-urlencoded',
      'Authorization': 'Basic ' + Buffer.from(clientID + ':' + clientSecret).toString('base64')
    },
    json: true
  };
  request.post(authOptions, (error: any, response:any, body:any)=>{
    console.log(error);
    console.log(response)
    if(!error && response.statusCode === 200){
      const access_token = body.access_token;
      const refresh_token = body.refresh_token;
      const expires_in = body.expires_in;
      res.json({
          'access_token': access_token,
          'refresh_token': refresh_token,
          'expires_in': expires_in
      });
    }
  })      
})

But i am still getting the error 400 and i can't seem to figure it out. Please i would really appreciate a response. Here is how i handled the code on my react native application

const [request2, response2, promptAsync2] = useAuthRequest({
        clientId: clientID,
        clientSecret: clientSecret,
        scopes: [
            'user-read-playback-state',
            'user-modify-playback-state',
            'user-read-currently-playing',
            'streaming',
            'playlist-read-private',
            'playlist-read-collaborative',
            'playlist-modify-private',
            'playlist-modify-public',
            'user-follow-modify',
            'user-follow-read',
            'user-read-playback-position',
            'user-library-modify',
            'user-library-read',
            'user-read-email',
            'user-read-private'
        ],
        usePKCE: false,
        redirectUri: makeRedirectUri({
            scheme: undefined
        })
    },
        discovery
    )
    useEffect(() => {
        if (response2?.type === 'success') {
            // get spotify access code
             const { code } = response2.params;
            const getSpotifyCode = async() =>{    
                const code2 = {
                    code
                }
                await axios.post('http://localhost:8005/get-spotify-access-code', code2).then(
                response =>{
                    console.log(response);
                }
               ).catch(error =>{
                console.log(error)
               })
            }
           getSpotifyCode()
        }
    }, [response2])

1

There are 1 answers

4
Bench Vue On BEST ANSWER

You should be match same port

Between running Expo port and Redirect URI of port in Spotify Developer Dashboard.

My redirect URL port is 3000

https://developer.spotify.com/dashboard

enter image description here

Expo running port is 3000

In package.json

  "scripts": {
    "start": "expo start --port 3000",
    "android": "expo start --android --port 3000",
    "ios": "expo start --ios --port 3000",
    "web": "expo start --web --port 3000"
  },

Demo code

App.js

import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
import { Button, View, Text, StyleSheet } from 'react-native';
import axios from 'axios';

WebBrowser.maybeCompleteAuthSession();

// Endpoint
const discovery = {
  authorizationEndpoint: 'https://accounts.spotify.com/authorize',
  tokenEndpoint: 'https://accounts.spotify.com/api/token'
};

const PORT = 3000; // Corrected: PORT should not be part of the config object
const CLIENT_ID = '<your client id>';
const CLIENT_SECRET = '<your client secret>';
const REDIRECT_URI = `http://localhost:${PORT}/callback`; // your redirect URI

export default function App() {
  const [request, response, promptAsync] = useAuthRequest(
    {
      clientId: CLIENT_ID,
      clientSecret: CLIENT_SECRET,
      scopes: ['user-read-email', 'playlist-modify-public'],
      usePKCE: false,
      redirectUri: REDIRECT_URI,
    },
    discovery
  );

  const [accessToken, setAccessToken] = React.useState("mockAccessToken");
  const [refreshToken, setRefreshToken] = React.useState("mockRefreshToken");

  React.useEffect(() => {
    if (response?.type === 'success') {
      const { code } = response.params;
      // Exchange code for access token and refresh token
      axios.post(
        'https://accounts.spotify.com/api/token',
        new URLSearchParams({
          'grant_type': 'authorization_code',
          'redirect_uri': REDIRECT_URI,
          'code': code
        }).toString(),
        {
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
          },
          auth: {
            username: CLIENT_ID,
            password: CLIENT_SECRET
          }
        }
      )
        .then((response) => {
          setAccessToken(response.data.access_token);
          setRefreshToken(response.data.refresh_token);
        })
        .catch((error) => {
          console.error('Error exchanging code for token:', error);
        });
    }
  }, [response]);


  return (
    <View style={styles.container}>
      <Button
        disabled={!request}
        title="Login"
        onPress={() => {
          promptAsync();
        }}
      />
      {accessToken && (
        <Text style={styles.tokenText}>Access Token: {accessToken}</Text>
      )}
      {refreshToken && (
        <Text style={styles.tokenText}>Refresh Token: {refreshToken}</Text>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#fff',
  },
  tokenText: {
    marginBottom: 20,
    fontSize: 16,
  },
});

package.json

{
  "main": "node_modules/expo/AppEntry.js",
  "scripts": {
    "start": "expo start --port 3000",
    "android": "expo start --android --port 3000",
    "ios": "expo start --ios --port 3000",
    "web": "expo start --web --port 3000"
  },
  "dependencies": {
    "@expo/metro-runtime": "~3.1.3",
    "@expo/vector-icons": "^14.0.0",
    "axios": "^1.6.8",
    "expo": "~50.0.14",
    "expo-auth-session": "~5.4.0",
    "expo-status-bar": "~1.11.1",
    "expo-web-browser": "~12.8.2",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-native": "0.73.6",
    "react-native-paper": "4.9.2",
    "react-native-web": "~0.19.6"
  }
}

Install dependencies

npm install

Run it

npm run web

Result

enter image description here