Authorization Code Flow
The Authorization Code flow is the recommended OAuth2 flow for all applications that involve user authentication. It provides the best security by keeping tokens away from the browser’s address bar and supporting refresh tokens for long-lived sessions.
How it works
Section titled “How it works”The authorization code flow involves two steps:
- Authorization: The user authenticates and grants permission, receiving an authorization code
- Token Exchange: The application exchanges the code for tokens at the token endpoint
Step 1: Authorization request
Section titled “Step 1: Authorization request”Redirect the user to the authorization endpoint with the following parameters:
Required parameters
Section titled “Required parameters”| Parameter | Description |
|---|---|
client_id | Your application’s client identifier |
redirect_uri | Where to send the user after authorization (must be pre-registered) |
response_type | Must be code |
scope | Space-separated list of scopes (include openid for OIDC) |
PKCE parameters
Section titled “PKCE parameters”See the PKCE documentation for details on generating these values.
| Parameter | Description |
|---|---|
code_challenge | Base64url-encoded SHA256 hash of the code verifier |
code_challenge_method | Must be S256 |
Recommended parameters
Section titled “Recommended parameters”| Parameter | Description |
|---|---|
state | Random string to prevent CSRF attacks |
nonce | Random string included in the ID token to prevent replay attacks |
Example authorization URL
Section titled “Example authorization URL”GET /auth/authorize? client_id=my-app& redirect_uri=https://my-app.com/callback& response_type=code& scope=openid profile email& code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM& code_challenge_method=S256& state=abc123& nonce=xyz789Authorization response
Section titled “Authorization response”After successful authentication and consent, the user is redirected back to your application:
https://my-app.com/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=abc123Step 2: Token exchange
Section titled “Step 2: Token exchange”Exchange the authorization code for tokens by making a POST request to the token endpoint.
Request parameters
Section titled “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 |
client_id | Yes | Your application’s client identifier |
client_secret | Conditional | Required for confidential clients |
code_verifier | Conditional | Required if PKCE was used |
Example token request
Section titled “Example token request”curl -X POST https://auth.example.com/auth/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=authorization_code" \ -d "code=SplxlOBeZQQYbYS6WxSbIA" \ -d "redirect_uri=https://my-app.com/callback" \ -d "client_id=my-app" \ -d "code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"curl -X POST https://auth.example.com/auth/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=authorization_code" \ -d "code=SplxlOBeZQQYbYS6WxSbIA" \ -d "redirect_uri=https://my-app.com/callback" \ -d "client_id=my-app" \ -d "client_secret=my-secret" \ -d "code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"curl -X POST https://auth.example.com/auth/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -u "my-app:my-secret" \ -d "grant_type=authorization_code" \ -d "code=SplxlOBeZQQYbYS6WxSbIA" \ -d "redirect_uri=https://my-app.com/callback" \ -d "code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"Token response
Section titled “Token response”{ "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "8xLOxBtZp8", "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...", "scope": "openid profile email"}Using refresh tokens
Section titled “Using refresh tokens”When the access token expires, use the refresh token to obtain a new one without requiring user interaction.
curl -X POST https://auth.example.com/auth/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=refresh_token" \ -d "refresh_token=8xLOxBtZp8" \ -d "client_id=my-app" \ -d "client_secret=my-secret"Configuration
Section titled “Configuration”Client setup
Section titled “Client setup”- Create a client in the admin console (Clients → Add Client)
- Set the client type:
- Public for SPAs, mobile apps, desktop apps
- Confidential for server-side web apps
- Enable Authorization Code flow in OAuth2 flows settings
- Register your Redirect URIs
- Configure PKCE requirement (recommended: required)
PKCE configuration
Section titled “PKCE configuration”PKCE can be configured at two levels:
- Global: Settings → General → PKCE Required
- Per-client: Clients → [Client] → OAuth2 Flows → PKCE
See PKCE documentation for detailed configuration options.
Security best practices
Section titled “Security best practices”- Always use PKCE - Even for confidential clients, PKCE adds defense in depth
- Validate state parameter - Prevent CSRF attacks by verifying the state matches
- Validate nonce in ID token - Prevent replay attacks
- Use short-lived authorization codes - Codes should expire within minutes
- Store tokens securely - Never expose tokens in URLs or localStorage for sensitive apps
- Implement token refresh - Use refresh tokens instead of storing long-lived access tokens
- Use HTTPS everywhere - Never transmit tokens over unencrypted connections
Error handling
Section titled “Error handling”Authorization errors
Section titled “Authorization errors”Errors during authorization are returned as query parameters:
https://my-app.com/callback?error=access_denied&error_description=User+denied+consent&state=abc123Common error codes:
invalid_request- Missing or invalid parameterunauthorized_client- Client not authorized for this flowaccess_denied- User denied the requestunsupported_response_type- Invalid response_typeinvalid_scope- Invalid or unknown scope
Token errors
Section titled “Token errors”Errors during token exchange return a JSON response with cache prevention headers per RFC 6749 Section 5.2:
HTTP/1.1 400 Bad RequestContent-Type: application/jsonCache-Control: no-storePragma: no-cache
{ "error": "invalid_grant", "error_description": "Authorization code has expired"}For client authentication failures (HTTP 401), a WWW-Authenticate header is included when HTTP Basic authentication was used:
HTTP/1.1 401 UnauthorizedWWW-Authenticate: BasicCommon error codes:
invalid_grant- Code expired, already used, or invalid (HTTP 400)invalid_client- Client authentication failed (HTTP 401)invalid_request- Missing required parameter (HTTP 400)