Authorization Code Flow
The Authorization Code flow is the most common OAuth2 flow for web applications. It involves redirecting the user to the authorization server, where they log in and grant consent, then redirecting back with an authorization code that can be exchanged for tokens.
When to Use
- Server-side web applications that can securely store client secrets
- Applications where the token exchange happens on the backend
For public clients (SPAs, mobile apps), use the Authorization Code + PKCE flow instead.
Flow Diagram
┌──────────┐ ┌──────────────┐ ┌──────────┐
│ User │ │ Your App │ │ S-Auth │
└────┬─────┘ └──────┬───────┘ └────┬─────┘
│ │ │
│ 1. Click "Login" │ │
│ ─────────────────────────────────────────>│ │
│ │ │
│ │ 2. Redirect to /authorize │
│ <─────────────────────────────────────────────────────────────────────────────────────│
│ │ │
│ 3. Login & Grant Consent │ │
│ ─────────────────────────────────────────────────────────────────────────────────────>│
│ │ │
│ 4. Redirect with code │ │
│ <─────────────────────────────────────────────────────────────────────────────────────│
│ │ │
│ │ 5. Exchange code for tokens │
│ │ ─────────────────────────────────────────>│
│ │ │
│ │ 6. Return tokens │
│ │ <─────────────────────────────────────────│
│ │ │
│ 7. Logged in! │ │
│ <─────────────────────────────────────────│ │
│ │ │
Step 1: Authorization Request
Redirect the user to the authorization endpoint:
GET https://auth.sebbyk.net/authorize?
response_type=code&
client_id=YOUR_CLIENT_ID&
redirect_uri=https://yourapp.com/callback&
scope=openid%20profile%20email&
state=RANDOM_STATE_VALUE
Parameters
| Parameter | Required | Description |
|---|---|---|
response_type | Yes | Must be code |
client_id | Yes | Your application's client ID |
redirect_uri | Yes | Must match a registered redirect URI |
scope | No | Space-separated list of scopes |
state | Recommended | Random value to prevent CSRF |
What Happens
- S-Auth checks if the user is logged in
- If not, redirects to login page
- After login, shows consent screen (if first authorization)
- Generates authorization code
- Redirects to your
redirect_uriwith the code
Step 2: Authorization Response
After the user grants consent, S-Auth redirects back to your application:
https://yourapp.com/callback?
code=AUTH_CODE&
state=RANDOM_STATE_VALUE
Always verify the state parameter matches what you sent!
Error Response
If something goes wrong:
https://yourapp.com/callback?
error=access_denied&
error_description=User%20denied%20consent&
state=RANDOM_STATE_VALUE
Step 3: Token Exchange
Exchange the authorization code for tokens:
curl -X POST https://auth.sebbyk.net/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Authorization: Basic BASE64(client_id:client_secret)" \
-d "grant_type=authorization_code" \
-d "code=AUTH_CODE" \
-d "redirect_uri=https://yourapp.com/callback"
Request Parameters
| Parameter | Required | Description |
|---|---|---|
grant_type | Yes | Must be authorization_code |
code | Yes | The authorization code received |
redirect_uri | Yes | Must match the original request |
Authentication
Use one of:
Client Secret Basic (Recommended):
Authorization: Basic BASE64(client_id:client_secret)
Client Secret Post:
client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET
Step 4: Token Response
{
"access_token": "sat_abc123...",
"token_type": "Bearer",
"expires_in": 900,
"refresh_token": "srt_xyz789...",
"scope": "openid profile email"
}
Using the Access Token
Include the access token in API requests:
curl https://auth.sebbyk.net/userinfo \
-H "Authorization: Bearer sat_abc123..."
Example: Node.js Implementation
import express from 'express';
const app = express();
// Step 1: Redirect to authorization
app.get('/login', (req, res) => {
const state = crypto.randomUUID();
req.session.oauthState = state;
const params = new URLSearchParams({
response_type: 'code',
client_id: process.env.CLIENT_ID,
redirect_uri: 'https://yourapp.com/callback',
scope: 'openid profile email',
state,
});
res.redirect(`https://auth.sebbyk.net/authorize?${params}`);
});
// Step 2 & 3: Handle callback and exchange code
app.get('/callback', async (req, res) => {
const { code, state, error } = req.query;
// Verify state
if (state !== req.session.oauthState) {
return res.status(400).send('Invalid state');
}
if (error) {
return res.status(400).send(`OAuth error: ${error}`);
}
// Exchange code for tokens
const response = await fetch('https://auth.sebbyk.net/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': `Basic ${Buffer.from(
`${process.env.CLIENT_ID}:${process.env.CLIENT_SECRET}`
).toString('base64')}`,
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code,
redirect_uri: 'https://yourapp.com/callback',
}),
});
const tokens = await response.json();
// Store tokens and create session
req.session.accessToken = tokens.access_token;
req.session.refreshToken = tokens.refresh_token;
res.redirect('/dashboard');
});
Security Considerations
- Always use HTTPS in production
- Validate the state parameter to prevent CSRF attacks
- Keep client secrets secure - never expose in frontend code
- Use short-lived authorization codes - they expire in 10 minutes
- Store tokens securely - use HttpOnly cookies or secure server-side storage