AsyncAPI OAuth2 Authorization Code Flow - Secure Server-Side Authentication
What is OAuth2 Authorization Code Flow in API?
OAuth2 Authorization Code Flow is a secure authentication and authorization mechanism designed for applications that can securely store a client secret. It's the most complete and secure OAuth2 flow, providing a way for client applications to obtain access tokens by first obtaining an authorization code.
The flow works as follows:
- The client redirects the user to the authorization server
- The user authenticates and grants permissions to the client
- The authorization server redirects back to the client with an authorization code
- The client exchanges this code for an access token by making a backend server request that includes the client secret
- The client uses the access token to access protected resources
This flow is ideal for server-side applications where the client secret can be securely stored, and it provides a high level of security by keeping the access token exchange on the backend, away from potentially vulnerable client-side code.
When to Use OAuth2 Authorization Code Flow
OAuth2 Authorization Code Flow is suitable for:
- Server-side web applications that can securely store client secrets
- Native desktop applications that can securely store client secrets
- Mobile applications with secure storage capabilities
- Applications requiring a high level of security
- Scenarios where you need to obtain both access tokens and refresh tokens
- Applications that need to access user data on their behalf
- Long-lived applications that need to maintain access to resources
- Applications where the frontend and backend are separate
- Enterprise applications with strict security requirements
- Applications that need to verify the identity of the client
When Not to Use OAuth2 Authorization Code Flow
OAuth2 Authorization Code Flow is not recommended for:
- Single-page applications (SPAs) without a backend (consider Implicit Flow or PKCE extension)
- Public clients that cannot securely store client secrets
- Simple applications where the authentication complexity is not justified
- Scenarios where the redirect flow creates a poor user experience
- Machine-to-machine authentication with no user involvement (use Client Credentials Flow)
- Applications with limited redirect capabilities
- Extremely simple applications where API key authentication would suffice
- Scenarios where the authorization server doesn't support this flow
- Applications where the user is authenticating directly (use Password Flow)
- Environments where redirects are problematic or restricted
Pros and Cons
Pros
- High Security: The most secure OAuth2 flow when implemented correctly
- Complete Separation: Access tokens are never exposed to the browser/frontend
- Refresh Token Support: Can obtain refresh tokens for long-term access
- User Consent: Explicit user consent for permission scopes
- Widely Supported: Implemented by most authorization servers
- Revocable Access: Tokens can be revoked if compromised
- Flexible Scopes: Fine-grained control over permissions
- Standard Compliance: Well-defined in OAuth2 specifications
- Suitable for Confidential Clients: Ideal for applications that can securely store secrets
- PKCE Extension: Can be enhanced with PKCE for public clients
Cons
- Complexity: More complex to implement than other flows
- Requires Backend: Needs a server component to exchange the code
- Redirect Required: User experience includes redirects
- Client Secret Management: Requires secure storage of client secrets
- Multiple Requests: Requires multiple HTTP requests to complete
- Implementation Overhead: More code and configuration required
- Potential for CSRF Attacks: If state parameter is not properly implemented
- Session Management: Requires session handling for the redirect
- Not Suitable for All Clients: Not ideal for public clients without PKCE
- Authorization Server Dependency: Relies on a properly configured authorization server
Examples
Here's how to define an OAuth2 Authorization Code Flow in AsyncAPI:
{
"type": "oauth2",
"flows": {
"authorizationCode": {
"authorizationUrl": "https://auth.example.com/authorize",
"tokenUrl": "https://auth.example.com/token",
"refreshUrl": "https://auth.example.com/refresh",
"availableScopes": {
"read:messages": "Read messages from the broker",
"write:messages": "Write messages to the broker",
"admin": "Full administrative access"
}
}
},
"description": "OAuth2 Authorization Code flow for secure API access"
}
Another example with different scopes:
{
"type": "oauth2",
"flows": {
"authorizationCode": {
"authorizationUrl": "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
"tokenUrl": "https://login.microsoftonline.com/common/oauth2/v2.0/token",
"availableScopes": {
"user.read": "Read user profile",
"mail.read": "Read user mail",
"calendars.readwrite": "Read and write to user calendars"
}
}
},
"description": "Microsoft Identity Platform OAuth2 Authorization Code flow"
}
Implementation Example
When implementing OAuth2 Authorization Code Flow in your application:
- Register your application with the authorization server to obtain client ID and client secret
- Implement the redirect endpoint in your application to receive the authorization code
- Use a secure backend to exchange the authorization code for tokens
- Store tokens securely and refresh them when they expire
- Implement proper state parameter to prevent CSRF attacks
- Use PKCE extension for additional security, especially for mobile apps
- Validate tokens before using them to access resources
- Implement proper error handling for authentication failures
Here's a simple example using Node.js and Express:
const express = require('express');
const axios = require('axios');
const crypto = require('crypto');
const app = express();
// Configuration
const config = {
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
redirectUri: 'http://localhost:3000/callback',
authorizationUrl: 'https://auth.example.com/authorize',
tokenUrl: 'https://auth.example.com/token'
};
// Generate a random state parameter for CSRF protection
app.get('/login', (req, res) => {
const state = crypto.randomBytes(16).toString('hex');
// Store state in session
req.session.oauthState = state;
// Redirect to authorization server
const authUrl = new URL(config.authorizationUrl);
authUrl.searchParams.append('client_id', config.clientId);
authUrl.searchParams.append('redirect_uri', config.redirectUri);
authUrl.searchParams.append('response_type', 'code');
authUrl.searchParams.append('state', state);
authUrl.searchParams.append('scope', 'read:messages write:messages');
res.redirect(authUrl.toString());
});
// Handle the callback with authorization code
app.get('/callback', async (req, res) => {
const { code, state } = req.query;
// Verify state parameter to prevent CSRF
if (state !== req.session.oauthState) {
return res.status(403).send('State validation failed');
}
try {
// Exchange authorization code for tokens
const tokenResponse = await axios.post(config.tokenUrl, {
client_id: config.clientId,
client_secret: config.clientSecret,
code: code,
redirect_uri: config.redirectUri,
grant_type: 'authorization_code'
}, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
// Store tokens securely (in session, database, etc.)
const { access_token, refresh_token, expires_in } = tokenResponse.data;
req.session.tokens = {
accessToken: access_token,
refreshToken: refresh_token,
expiresAt: Date.now() + (expires_in * 1000)
};
res.redirect('/dashboard');
} catch (error) {
console.error('Token exchange error:', error.response?.data || error.message);
res.status(500).send('Authentication failed');
}
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
The AsyncAPI specification for OAuth2 Authorization Code Flow follows this JSON Schema:
{
"type": "object",
"required": [ "authorizationUrl", "tokenUrl", "availableScopes" ],
"properties": {
"authorizationUrl": {
"description": "The authorization URL to be used for this flow. This MUST be in the form of an absolute URL.",
"type": "string",
"format": "uri"
},
"availableScopes": {
"description": "The available scopes for the OAuth2 security scheme. A map between the scope name and a short description for it.",
"$ref": "https://asyncapi.pavelon.dev/schemas/v3/security/oauth2/oauth2Scopes.json"
},
"refreshUrl": {
"description": "The URL to be used for obtaining refresh tokens. This MUST be in the form of an absolute URL.",
"type": "string",
"format": "uri"
},
"tokenUrl": {
"description": "The token URL to be used for this flow. This MUST be in the form of an absolute URL.",
"type": "string",
"format": "uri"
}
},
"patternProperties": {
"^x-[\\w\\d\\.\\x2d_]+$": true
},
"additionalProperties": false
}