Hello,
I am currently working on this project. But I have an issue retrieving the access token, I am using the authorisation code with PKCE flow and not the implicit grant flow. Yet, it is quite simple, I followed the instructions given here Authorization Code with PKCE Flow | Spotify for Developers. I think I have done everything as instructed but I still have this error in the console :
spotify.js:32
POST https://accounts.spotify.com/api/token 400 (Bad Request). Here is my code :
// spotify.js
const CLIENT_ID = "0697d4488dd2433d9bc4c8d3e4d9016d";
const REDIRECT_URI = "http://localhost:3000";
const API_URL = "https://accounts.spotify.com/api/token";
export async function getToken(code) {
const verifier = localStorage.getItem("verifier");
const params = new URLSearchParams();
params.set("grant_type", "authorization_code");
params.set("code", code);
params.set("redirect_uri", REDIRECT_URI);
params.set("client_id", CLIENT_ID);
params.set("code_verifier", verifier);
const payload = {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code,
redirect_uri: REDIRECT_URI,
client_id: CLIENT_ID,
code_verifier: verifier,
}),
}
try {
const response = await fetch(API_URL, payload);
if(response.ok) {
const data = await response.json();
return data;
} else {
console.error("Error exchanging code for token : ", await response.text());
return null;
}
} catch (error) {
console.error("Catch an error exchanging code for token : ", error);
return null;
}
}
export async function redirectToAuthCodeFlow() {
const verifier = generateCodeVerifier(128);
const challenge = await generateCodeChallenge(verifier);
localStorage.setItem("verifier", verifier);
const params = new URLSearchParams();
params.append("response_type", "code");
params.append("client_id", CLIENT_ID);
params.append("redirect_uri", REDIRECT_URI);
params.append("scope", "playlist-modify-private playlist-modify-public user-read-private");
params.append("code_challenge_method", "S256");
params.append("code_challenge", challenge);
window.location.href = `https://accounts.spotify.com/authorize?${params.toString()}`;
}
function generateCodeVerifier(length) {
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const values = crypto.getRandomValues(new Uint8Array(length));
return values.reduce((acc, x) => acc + possible[x % possible.length], "");
}
async function generateCodeChallenge(codeVerifier) {
const data = new TextEncoder().encode(codeVerifier);
const digest = await window.crypto.subtle.digest('SHA-256', data);
return btoa(String.fromCharCode(...new Uint8Array(digest)))
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_');
}
export async function getTracks(searchTerm) {
let accessToken = localStorage.getItem('access_token');
//let expiresAt = localStorage.getItem('expires_at');
const url = `https://api.spotify.com/v1/tracks/search?q=${encodeURIComponent(searchTerm)}&type=track`;
const headers = {'Authorization' : `Bearer ${accessToken}`};
try{
const response = await fetch(url, {headers});
if(response.ok){
const jsonResponse = await response.json();
const tracks = jsonResponse.map((item) => ({
id: item.id,
name: item.name,
artist: item.artists[0].name,
album: item.album.name,
uri: item.uri,
}));
return tracks;
}
return [];
}catch(error){
console.log("Error fetching tracks : " + error);
return [];
}
}
.
And
// App.js
import React, { useEffect, useState } from "react";
import './App.css';
import SearchBar from "./components/SearchBar";
import Results from "./components/Results";
import Playlist from "./components/Playlist";
import { getToken, redirectToAuthCodeFlow, getTracks } from "./spotify";
function App() {
const [searchResults, setSearchResults] = useState([]);
useEffect(() => {
async function getPermission(){
const params = new URLSearchParams(window.location.search);
const code = params.get("code") || localStorage.getItem("code");
if (!code) {
redirectToAuthCodeFlow();
} else {
localStorage.setItem("code", code);
const token = await getToken(code);
if(token){
localStorage.setItem("accessToken", token.access_token);
localStorage.setItem("expiresAt", Date.now() + token.expires_in);
}
}
}
getPermission();
}, []);
const onSearch = (searchTerm) => {
getTracks(searchTerm)
.then((tracks) => setSearchResults(tracks));
}
return (
<>
<header className="header">
<SearchBar onSearch={onSearch}/>
</header>
<main className="main">
<Results searchResults={searchResults}/>
<Playlist/>
</main>
</>
);
}
export default App;
It’s been days that I am trying to solve the problem. I would appreciate you help.