mandarinfactory
mandarinfactory

Reputation: 37

get Spotify Oauth in NextJS but doesn't work

I'm making some music website portfolio with Spotify, NextJS and I want to make play music in this website, and need Spotify authenication for use it. when I click Login-button and got 405 HTTP status. I checked on postman and got same error. I searched and it might be error on GET/POST issues but I can't handle where to fix it.

app/api/login code part

export async function LoginHandler (req: NextApiRequest, res: NextApiResponse) {
  const state = generateRandomString(16);
  const CLIENT_ID = process.env.NEXT_PUBLIC_SPOTIFY_CLIENT_ID;
  const scope =
    "user-read-private user-read-email user-read-playback-state user-modify-playback-state streaming";
  
  res.redirect(
    "https://accounts.spotify.com/authorize?"+
      qs.stringify({
        response_type: "code",
        client_id: CLIENT_ID,
        scope: scope,
        redirect_uri: `${REDIRECT_URL}/api/callback`,
        state: state,
      })
  );

I use useRouter(next/navigation) to route it.

<button
        onClick={() => {
          router.push("/api/login");
        }}
      >

and this one is get token code

export const getAccessTokenData = () => {
  return axios<AccessTokenData>({
    method: "POST",
    url: SPOTIFY_ACCESS_TOKEN_URL,
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
      Authorization:
        "Basic " +
        Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString("base64"),
    },
    data: {
      grant_type: "client_credentials",
    },
  });
};

export const getAuthorizationTokenData = (code: string) => {
  return axios<AuthorizationTokenData>({
    method: 'post',
    url: 'https://accounts.spotify.com/api/token',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      Authorization:
        'Basic ' +
        Buffer.from(
          `${CLIENT_ID}:${CLIENT_SECRET}`
        ).toString('base64'),
    },
    data: {
      code: code,
      redirect_uri: REDIRECT_URL + '/api/callback',
      grant_type: 'authorization_code',
    },
  });
}

Upvotes: 1

Views: 229

Answers (1)

Bench Vue
Bench Vue

Reputation: 9380

405 error means invalid Authentication or wrong format send parameter. Your getAuthorizationTokenData()'s data was Incorrect Request Format.

To send data in application/x-www-form-urlencoded format of axios POST call, you should use the URLSearchParams class to construct the request body.

You need to understand Spotify "Authorization Code Flow" More detail in here

enter image description here

Demo code implemented Spotify login and get access token by Authorization Code Flow and get specific playlist and shows songs. Using next.js.

File structure

I added three files from create next.js project (index.tsx, callbacl.tsx and constant.ts)

npx create-next-app@latest my-spotify-app --typescript

You need to remove ./src/app/pages.tsx file.

enter image description here

Save index.tsx

// pages/index.tsx
import { GetServerSideProps } from 'next';
import { clientId, scope, redirectUri } from './constants';

const Home = ({ spotifyAuthUrl }: { spotifyAuthUrl: string }) => {
    return (
        <div>
            <h1>Spotify Login</h1>
            <a href={spotifyAuthUrl}>Login with Spotify</a>
        </div>
    );
};

export const getServerSideProps: GetServerSideProps = async () => {
    const encodeRedirectUri = encodeURIComponent(redirectUri);
    const spotifyAuthUrl = `https://accounts.spotify.com/authorize?response_type=code&client_id=${clientId}&redirect_uri=${encodeRedirectUri}&scope=${scope}`;
    return { props: { spotifyAuthUrl } };
};

export default Home;

Save callback.tsx

// pages/callback.tsx
import { GetServerSideProps } from 'next';
import axios from 'axios';
import { clientId, clientSecret, redirectUri, myPlaylistId } from './constants';

interface Track {
    track: {
        name: string;
        id: string;
    };
}

const Callback = ({ accessToken, playlistTracks }: { accessToken: string, playlistTracks: Track[] }) => {
    return (
        <div>
            <h1>Spotify Playlist Tracks</h1>
            <p>Access Token: {accessToken}</p>
            <ul>
                {playlistTracks.map((track, index) => (
                    <li key={index}>
                        {track.track.name}
                        <br />
                        <iframe
                            src={`https://open.spotify.com/embed/track/${track.track.id}`}
                            width="300"
                            height="80"
                            allow="encrypted-media"
                        ></iframe>
                    </li>
                ))}
            </ul>
        </div>
    );
};

export const getServerSideProps: GetServerSideProps = async ({ query }) => {
    const code = query.code as string;

    if (!code) {
        return {
            redirect: {
                destination: '/',
                permanent: false,
            },
        };
    }

    try {
        const response = await axios.post(
            'https://accounts.spotify.com/api/token',
            new URLSearchParams({
                grant_type: 'authorization_code',
                code,
                redirect_uri: redirectUri,
                client_id: clientId,
                client_secret: clientSecret,
            }).toString(),
            {
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
            }
        );

        const accessToken = response.data.access_token;

        const playlistResponse = await axios.get(
            `https://api.spotify.com/v1/playlists/${myPlaylistId}/tracks`,
            {
                headers: {
                    Authorization: `Bearer ${accessToken}`,
                },
            }
        );

        const playlistTracks: Track[] = playlistResponse.data.items;

        return { props: { accessToken, playlistTracks } };
    } catch (error) {
        console.error('Error:', error);
        return { props: { accessToken: '', playlistTracks: [] } };
    }
};

export default Callback;

Save constans.ts

// constants.ts
export const clientId = '[your client id]';
export const clientSecret = '[your client secret]';
export const redirectUri = 'http://localhost:3000/callback'; // replace your redirect URI
export const myPlaylistId = '[your playlist id]';
export const scope = 'playlist-read-private user-read-private user-read-email user-read-playback-state user-modify-playback-state streaming'; // Add more scopes if needed

Install dependency

npm insatall axios

Run it

npm run dev

Access it

http:/localhost:3000

Result

enter image description here

Upvotes: 1

Related Questions