Published on

Spotify Web API SDK authorization for personal use only (Next.js)

Authors
  • Name
    Twitter

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

  1. Introduction
  2. Create an app
  3. Installation and Setup
  4. Authentication with API routes
  5. Using SDK

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

Spotify 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.

/api/auth/spotify.ts
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() }
}
Create an ecard at CelebrateThisMortal.comBe more productive with Planda