- Published on
Spotify Web API SDK authorization for personal use only (Next.js)
- Authors
- Name
TLDR: You can't get a permanent access token to your own Spotify account. If you need to use APIs that require additional scopes, you need to go through the authorization process. You will also need to store your refresh token securely, and update it after every use.
Table of Contents
Introduction
I wanted to create playlists programmatically on my own Spotify account.
I thought it would be cool if people could build birthday playlists for their friends straight from their birthday card, and have it playable on the card as well, so I implemented this feature for celebratethismortal.com
Create an app
Navigate to https://developer.spotify.com/dashboard
Follow the instructions there to create an app.
You might also have to add your account under your app's user management
Installation and Setup
Install the SDK
npm i @spotify/web-api-ts-sdk -save
Create functions for storing and retrieving your access token. I called these storeSpotifyCredentials
and getSpotifyCredentials
Authentication with API routes
Create an API route to start the authentication flow. Put in your redirect_uri and scopes.
const state = 'ANY_STATE_VALUE'
export async function GET(request: NextRequest) {
const redirect_uri = 'http://localhost:3000/api/spotify/callback' // choose a redirect URI
const scopes = ['playlist-modify-public', 'playlist-modify-private', 'ugc-image-upload']
redirect(
'https://accounts.spotify.com/authorize?' +
paramsToQueryString({
response_type: 'code',
client_id: process.env.SPOTIFY_CLIENT_ID!,
scope: scopes.join(' '),
redirect_uri: redirect_uri,
state: state,
})
)
}
You will later call this API route on your browser to be redirected to Spotify's OAuth.
Create an API route at the redirect URI you selected, to accept the authorization code from Spotify.
// http://localhost:3000/api/admin/spotify/callback/route.ts
import { storeSpotifyCredentials } from '@/lib/spotify'
import { NextRequest, NextResponse } from 'next/server'
const client_id = process.env.SPOTIFY_CLIENT_ID!
const client_secret = process.env.SPOTIFY_CLIENT_SECRET!
const state = 'ANY_STATE_VALUE'
export async function GET(request: NextRequest) {
const redirect_uri = 'http://localhost:3000/api/admin/spotify/callback'
const { searchParams } = new URL(request.url)
let code = searchParams.get('code')
const spotifyState = searchParams.get('state')
if (state === null || spotifyState !== state) return NextResponse.json({ error: 'invalid state' }, { status: 400 })
if (code === null) return NextResponse.json({ error: 'invalid code' }, { status: 400 })
const response = await fetch('https://accounts.spotify.com/api/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Basic ${Buffer.from(client_id + ':' + client_secret).toString('base64')}`,
},
body: new URLSearchParams({
code,
redirect_uri,
grant_type: 'authorization_code',
}),
})
const credentials = await response.json()
await storeSpotifyCredentials({ ...credentials, updatedAt: Date.now() })
return NextResponse.json({ success: true })
}
Now go to your browser, and call the first API route you made (/api/auth/spotify
in my example) to start the authentication flow.
After you go through Spotifies OAuth, your access token should have already been stored, due to Spotify calling your redirect URI.
Using SDK
Here are two functions I used to authenticate with the WEB Api, which includes handing refresh tokens.
Spotify expires your access token every hour, so this handles requesting a new one.
Be careful not to entirely replace your old Spotify credentials when you store your new ones, because I think I saw on some Github issue that you may not get a refresh token again since you always use the same one, however I think I got a new refresh token when I did this.
import { AccessToken, SpotifyApi } from '@spotify/web-api-ts-sdk'
const client_id = process.env.SPOTIFY_CLIENT_ID!
const client_secret = process.env.SPOTIFY_CLIENT_SECRET!
export const getSpotifyAuthenticated = async () => {
let credentials = await getSpotifyCredentials()
if (!credentials) throw new Error('NO CREDENTIALS STORED')
if (Date.now() >= credentials.updatedAt + credentials.expires_in * 1000) {
credentials = await getRefreshToken(credentials.refresh_token)
}
const spotify = SpotifyApi.withAccessToken(client_id, credentials)
return spotify
}
const getRefreshToken = async (refreshToken: string) => {
const url = 'https://accounts.spotify.com/api/token'
const payload = {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Basic ${Buffer.from(client_id + ':' + client_secret).toString('base64')}`,
},
body: new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: refreshToken,
}),
}
const body = await fetch(url, payload)
const response = await body.json()
if (!response.access_token) {
console.error(response)
throw new Error(response.error)
}
storeSpotifyCredentials(response)
return { ...(response as AccessToken), updatedAt: Date.now() }
}