mirror of
https://github.com/mintlify/docs.git
synced 2026-09-14 13:35:46 +08:00
65fd3621dc
- 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
259 lines
6.8 KiB
Plaintext
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>
|
|
|