Files
mintlify__docs/tutorials/authorization-code-flow.mdx
Mintlify Agent 65fd3621dc Add Spotify Web API documentation
- Created 111 MDX documentation files
- Added 89 API endpoint pages across 14 categories
- Included 11 concept guides and 7 tutorials
- Configured Mintlify with Spotify branding (#1DB954)
- All pages validated successfully
2026-02-07 11:47:26 +00:00

259 lines
6.8 KiB
Plaintext

---
title: "Authorization Code Flow"
description: "Implement the Authorization Code Flow for server-side applications"
---
# Authorization Code Flow tutorial
The Authorization Code Flow is the most secure method for authenticating users in server-side applications. This tutorial shows you how to implement it step by step.
## When to use this flow
Use Authorization Code Flow when:
- Building a server-side web application
- You can securely store a client secret
- You need long-term access to user data
- You want to refresh tokens automatically
## Flow diagram
<Steps>
<Step title="Request authorization">
Redirect user to Spotify's authorization page
</Step>
<Step title="User approves">
User grants permission to your app
</Step>
<Step title="Receive code">
Spotify redirects back with authorization code
</Step>
<Step title="Exchange for token">
Your server exchanges code for access token
</Step>
<Step title="Access API">
Use access token to make API requests
</Step>
</Steps>
## Implementation
### Step 1: Build authorization URL
```javascript
const client_id = 'your_client_id';
const redirect_uri = 'https://yourapp.com/callback';
const scope = 'user-read-private user-read-email';
const state = generateRandomString(16);
const authUrl = new URL('https://accounts.spotify.com/authorize');
authUrl.searchParams.append('response_type', 'code');
authUrl.searchParams.append('client_id', client_id);
authUrl.searchParams.append('scope', scope);
authUrl.searchParams.append('redirect_uri', redirect_uri);
authUrl.searchParams.append('state', state);
// Store state in session for validation
req.session.state = state;
// Redirect user
res.redirect(authUrl.toString());
```
### Step 2: Handle callback
```javascript
app.get('/callback', async (req, res) => {
const code = req.query.code;
const state = req.query.state;
const storedState = req.session.state;
// Validate state
if (state !== storedState) {
return res.status(403).send('State mismatch');
}
// Exchange code for token
try {
const tokenData = await exchangeCodeForToken(code);
req.session.access_token = tokenData.access_token;
req.session.refresh_token = tokenData.refresh_token;
res.redirect('/dashboard');
} catch (error) {
res.status(500).send('Authentication failed');
}
});
```
### Step 3: Exchange code for token
```javascript
async function exchangeCodeForToken(code) {
const client_id = process.env.CLIENT_ID;
const client_secret = process.env.CLIENT_SECRET;
const redirect_uri = process.env.REDIRECT_URI;
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({
grant_type: 'authorization_code',
code: code,
redirect_uri: redirect_uri
})
});
if (!response.ok) {
throw new Error('Token exchange failed');
}
return await response.json();
}
```
### Step 4: Make API requests
```javascript
async function getUserProfile(accessToken) {
const response = await fetch('https://api.spotify.com/v1/me', {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
return await response.json();
}
```
## Complete example
Here's a complete Express.js example:
```javascript
require('dotenv').config();
const express = require('express');
const session = require('express-session');
const crypto = require('crypto');
const app = express();
app.use(session({
secret: 'your-session-secret',
resave: false,
saveUninitialized: true
}));
const CLIENT_ID = process.env.CLIENT_ID;
const CLIENT_SECRET = process.env.CLIENT_SECRET;
const REDIRECT_URI = process.env.REDIRECT_URI;
function generateRandomString(length) {
return crypto.randomBytes(length).toString('hex');
}
app.get('/login', (req, res) => {
const state = generateRandomString(16);
req.session.state = state;
const scope = 'user-read-private user-read-email playlist-read-private';
const authUrl = new URL('https://accounts.spotify.com/authorize');
authUrl.searchParams.append('response_type', 'code');
authUrl.searchParams.append('client_id', CLIENT_ID);
authUrl.searchParams.append('scope', scope);
authUrl.searchParams.append('redirect_uri', REDIRECT_URI);
authUrl.searchParams.append('state', state);
res.redirect(authUrl.toString());
});
app.get('/callback', async (req, res) => {
const code = req.query.code;
const state = req.query.state;
if (state !== req.session.state) {
return res.status(403).send('State mismatch');
}
try {
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({
grant_type: 'authorization_code',
code: code,
redirect_uri: REDIRECT_URI
})
});
const data = await response.json();
req.session.access_token = data.access_token;
req.session.refresh_token = data.refresh_token;
res.redirect('/profile');
} catch (error) {
res.status(500).send('Error during authentication');
}
});
app.get('/profile', async (req, res) => {
if (!req.session.access_token) {
return res.redirect('/login');
}
try {
const response = await fetch('https://api.spotify.com/v1/me', {
headers: {
'Authorization': `Bearer ${req.session.access_token}`
}
});
const profile = await response.json();
res.json(profile);
} catch (error) {
res.status(500).send('Error fetching profile');
}
});
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
```
## Security best practices
<AccordionGroup>
<Accordion title="Always validate state parameter">
Store state in session and verify it matches in the callback to prevent CSRF attacks.
</Accordion>
<Accordion title="Use HTTPS in production">
Never use HTTP for redirect URIs in production environments.
</Accordion>
<Accordion title="Store tokens securely">
Use secure session storage or encrypted databases for tokens.
</Accordion>
<Accordion title="Implement token refresh">
Automatically refresh expired tokens before making API calls.
</Accordion>
</AccordionGroup>
## Next steps
<CardGroup cols={2}>
<Card title="Refreshing tokens" href="/tutorials/refreshing-tokens">
Handle expired tokens
</Card>
<Card title="Authorization concepts" href="/concepts/authorization">
Learn more about authorization
</Card>
<Card title="Scopes" href="/concepts/scopes">
Understand permissions
</Card>
</CardGroup>