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])
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
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"
},
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"
}
}
npm install
npm run web