javascriptreactjsspotify

Why am I getting 403 Forbidden Error in fetching API?


I'm using React and from getting the user's playlist id from Spotify, I received an error of

  1. The access token is working
  2. The refresh token is working
  3. The stored access and refresh token in local storage is working

My Code:

async function refreshAccessToken(refreshToken) {
  try {
    const response = await fetch("https://accounts.spotify.com/api/token", {
      method: "POST",
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
        Authorization: `Basic ${btoa("(client id):(client secret)")}`,
      },
      body: new URLSearchParams({
        grant_type: "refresh_token",
        refresh_token: refreshToken,
      }),
    });

    const data = await response.json();
    console.log("Spotify API Response:", data);

    const accessToken = data.access_token;

    const playlistResponse = await fetch("https://api.spotify.com/v1/me/playlists", {
        method: "GET",
        headers: {
          Authorization: `Bearer ${accessToken}`, 
        },
      });
          const playlists = await playlistResponse.json();
        console.log(playlists)
    if (data.access_token) {
      localStorage.setItem("spotifyAccessToken", data.access_token);
      return data.access_token;
    }
  } catch (error) {
    console.error("Error refreshing token:", error);
  }

}


function Token() { 
  const [accessToken, setAccessToken] = 
useState(localStorage.getItem("spotifyAccessToken") || "");
  const [refreshToken, setRefreshToken] = 
useState(localStorage.getItem("spotifyRefreshToken") || "");

  useEffect(() => {
    console.log("Updated access token:", accessToken);
  }, [accessToken]);

  useEffect(() => {
    const storedAccessToken = localStorage.getItem("spotifyAccessToken");
    const storedRefreshToken = localStorage.getItem("spotifyRefreshToken");
    console.log("Stored Access Token:", localStorage.getItem("spotifyAccessToken"));
    console.log("Stored Refresh Token:", localStorage.getItem("spotifyRefreshToken"));

    if (!storedAccessToken || !storedRefreshToken) {
       const token = "(my access token)";
       const refresh = "(my refresh token)"; 

      localStorage.setItem("spotifyAccessToken", token);
      localStorage.setItem("spotifyRefreshToken", refresh);
      setAccessToken(token);
      setRefreshToken(refresh);
    }

    if (storedRefreshToken) {
      const fetchNewToken = async () => {
        const newToken = await refreshAccessToken(storedRefreshToken);
        if (newToken) {
          setAccessToken(newToken);
          localStorage.setItem("spotifyAccessToken", newToken);
        }
      };
      fetchNewToken();
    }
  }, []);


  useEffect(() => {
    const interval = setInterval(async () => {
      const newToken = await refreshAccessToken(refreshToken);
      if (newToken) setAccessToken(newToken);
    }, 1000 * 60 * 50); 

    return () => clearInterval(interval);
  }, [refreshToken]);

  return null; 
}

export default Token;

My Home Component to access the data:

 const [playlists, setPlaylists] = useState([]);
  const [accessToken, setAccessToken] = 
useState(localStorage.getItem("spotifyAccessToken") || "");
  useEffect(() => {
    const handleStorageChange = () => {
      const newToken = localStorage.getItem("spotifyAccessToken");
      if (newToken) setAccessToken(newToken);
    };

    window.addEventListener("storage", handleStorageChange);
    return () => window.removeEventListener("storage", handleStorageChange);
  }, []);
  console.log("Access Token:", accessToken);


   const playlistIds = useMemo(() => [
   "2TA2qT9p2e91lGZEozyGlI",
   "4Ap2xIU37l1NAShncyota",
   "0Y44bc2Fd2IfbhqSPnNlTC",
   "6jA8JKtuPrsNCTbnvgYkBt",
   "4GX8ccKz938Q2BtF8NMdX2",
   "37i9dQZF1DZ06evO2yXXGB"
  ], []);


  useEffect(() => {
    if (!accessToken) {
      console.warn("No access token available. Cannot fetch playlists.");
      return;
    }

    const fetchPlaylists = async () => {
      try {
        const responses = await Promise.all(
          playlistIds.map(async (id) => {
            const response = await fetch(`https://api.spotify.com/v1/playlists/${id}`, 
{
              headers: { Authorization: `Bearer ${accessToken}` },
            });

            if (!response.ok) {
              console.warn(`Error fetching ${id}: ${response.status} 
${response.statusText}`);
              if (response.status === 403) return { error: "Forbidden: Check your 
Spotify scopes." };
              if (response.status === 401) return { error: "Unauthorized: Your token 
may be expired." };
              if (response.status === 429) return { error: "Rate limit exceeded. Try 
again later." };
              return { error: `HTTP ${response.status}` };
            }

            const contentType = response.headers.get("content-type");
            if (!contentType || !contentType.includes("application/json")) {
              console.error(`Invalid JSON response for ${id}:`, await 
response.text());
              return { error: "Invalid response format (Not JSON)" };
            }

            return await response.json();

          })
        );

        setPlaylists(responses);
      } catch (error) {
        console.error("Error fetching playlists:", error);
      }
    };

    console.log("Playlist IDs:", playlistIds);

    fetchPlaylists();
    setTimeout(() => fetchPlaylists(), 2000);
  }, [accessToken, playlistIds]);

Failed to load resource: the server responded with a status of 403 ()

Error fetching (the Spotify playlist id): 403


Solution

  • The 403 Forbidden error usually means that the server understands the request but refuses to authorize it. Missing or Invalid Spotify Scopes In your refreshAccessToken() function, you are refreshing the token but not specifying the required scopes. Without proper scopes, you might get a 403 error when trying to access playlist data.