Skip to content

Instantly share code, notes, and snippets.

@bih
Last active March 28, 2019 10:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bih/1be8b4634ccbcc9fb719e53a57dabc68 to your computer and use it in GitHub Desktop.
Save bih/1be8b4634ccbcc9fb719e53a57dabc68 to your computer and use it in GitHub Desktop.
Spotify Authentication with React Hooks
import React, { ReactNode } from 'react'
import { useSpotifyAccessTokenFromAuthCode, useSpotifyAccessTokenFromRefreshToken } from './hooks'
// Store them as environment variables!
//
const clientId = process.env.SPOTIFY_CLIENT_ID
const clientSecret = process.env.SPOTIFY_CLIENT_SECRET
const redirectUri = process.env.SPOTIFY_REDIRECT_URI
const clientPayload = { clientId, clientSecret, redirectUri }
interface AppProps {
userSession: boolean
refreshToken: string | null,
authorizationCode: string | null
}
export default App(props: AppProps) : ReactNode {
if (props.userSession) {
// refreshToken will be stored in your database somewhere
const accessToken = useSpotifyAccessTokenFromAuthCode({
...clientPayload,
refreshToken: props.refreshToken
})
} else {
// authorizationCode will be passed by Spotify as the "code" parameter
const accessToken = useSpotifyAccessTokenFromRefreshToken({
...clientPayload,
authorizationCode: props.authorizationCode
})
}
if (!accessToken) {
return <h1>Obtaining your accessToken</h1>
}
return <h1>Your Access Token: {accessToken}</h1>
}
/** @format */
import {useState, useEffect} from 'react'
if (typeof window !== 'undefined') {
throw new Error(
[
'This should only be ran in Node.js, and not the browser in any case.',
'Why? It will expose your secret credentials which will result in your Spotify client credentials being revoked.',
'See article for more information: [insert]',
].join('\n'),
)
}
if (typeof fetch === 'undefined') {
throw new Error(
[
'In order to use this, you will need to use the `node-fetch` package.',
'Run the command below to fix this issue.',
' $ yarn add node-fetch',
].join('\n'),
)
}
interface Constructor {
clientId: string
clientSecret: string
redirectUri: string
}
interface Request {
grant_type: 'authorization_code' | 'refresh_token'
client_id: string
client_secret: string
}
interface Response {
access_token: string
token_type: 'Bearer'
scope: string
expires_in: number
}
export interface SpotifyAuthCodeConstructor extends Constructor {
authorizationCode: string | null
}
export interface SpotifyAuthCodeRequest extends Request {
redirect_uri: string
code: string
}
export interface SpotifyAuthCodeResponse extends Response {}
export interface SpotifyRefreshTokenConstructor extends Constructor {
refreshToken: string | null
}
export interface SpotifyRefreshTokenRequest extends Request {
refresh_token: string
}
export interface SpotifyRefreshTokenResponse extends Response {
refresh_token: string
}
export function useSpotifyAccessTokenFromAuthCode({
authorizationCode,
...client
}: SpotifyAuthCodeConstructor): string | null {
if (!client.clientId || !client.clientSecret || !client.redirectUri || !authorizationCode) {
return null;
}
const [accessToken, setAccessToken] = useState(null)
useEffect(() => {
const abortController = new AbortController()
const body: SpotifyAuthCodeRequest = {
client_id: client.clientId,
client_secret: client.clientSecret,
grant_type: 'authorization_code',
redirect_uri: client.redirectUri,
code: authorizationCode,
}
fetch('https://accounts.spotify.com/api/token', {
method: 'POST',
body: JSON.stringify(body),
signal: abortController.signal,
})
.then(response => response.json())
.then((response: SpotifyAuthCodeResponse) => setAccessToken(response.access_token))
return function cancel() {
abortController.abort()
}
}, [{authorizationCode, ...client}])
return accessToken
}
export function useSpotifyAccessTokenFromRefreshToken({
refreshToken,
...client
}: SpotifyRefreshTokenConstructor): string | null {
if (!client.clientId || !client.clientSecret || !client.redirectUri || !refreshToken) {
return null;
}
const [accessToken, setAccessToken] = useState(null)
useEffect(() => {
const abortController = new AbortController()
const body: SpotifyRefreshTokenRequest = {
client_id: client.clientId,
client_secret: client.clientSecret,
grant_type: 'refresh_token',
refresh_token: refreshToken,
}
fetch('https://accounts.spotify.com/api/token', {
method: 'POST',
body: JSON.stringify(body),
signal: abortController.signal,
})
.then(response => response.json())
.then((response: SpotifyRefreshTokenResponse) => setAccessToken(response.access_token))
return function cancel() {
abortController.abort()
}
}, [{authorizationCode, ...client}])
return accessToken
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment