OAuth 2.0 Response Types
The response_types attribute specifies what the authorization endpoint returns in the authorization response after user authentication and consent.
OAuth 2.0 Response Types
code
Authorization Code Flow (recommended)
- Returns authorization code in redirect URI query parameters
- Code must be exchanged for tokens at token endpoint
- Most secure - tokens never exposed to browser
- Supports refresh tokens
- Works with PKCE for public clients
Authorization Response:
HTTP/1.1 302 Found
Location: https://client.example.com/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz
Pairs with grant type: authorization_code
Best for: All application types (web, SPA, mobile, native)
Security features:
- Tokens never in browser
- Client authentication at token endpoint (confidential clients)
- PKCE support for public clients
- Refresh token support
token
Implicit Flow - DEPRECATED
- Returns access token directly in redirect URI fragment
- No code exchange step
- Tokens exposed in browser history and logs
- No refresh tokens
- No client authentication possible
Authorization Response:
HTTP/1.1 302 Found
Location: https://client.example.com/callback#access_token=2YotnFZFEjr1zCsicMWpAA
&token_type=Bearer
&expires_in=3600
&state=xyz
Pairs with grant type: implicit
Status: Deprecated in OAuth 2.1
Migration: Use code with PKCE instead
OpenID Connect Response Types
id_token
Returns only ID token
- ID token returned in redirect URI fragment
- No access token
- No token endpoint interaction
- Only provides user identity information
Authorization Response:
HTTP/1.1 302 Found
Location: https://client.example.com/callback#id_token=eyJhbGc...
&state=xyz
Pairs with grant type: implicit
Use when: You only need to verify user identity, not access APIs
Status: Implicit flow variant - being phased out
id_token token
Returns ID token AND access token
- Both tokens returned in redirect URI fragment
- Both tokens exposed in browser
- No code exchange
- No refresh tokens
Authorization Response:
HTTP/1.1 302 Found
Location: https://client.example.com/callback#id_token=eyJhbGc...
&access_token=2YotnFZFEjr1zCsicMWpAA
&token_type=Bearer
&expires_in=3600
&state=xyz
Pairs with grant type: implicit
Status: Deprecated - use code instead
OpenID Connect Hybrid Flow Response Types
Hybrid flows combine aspects of Authorization Code Flow and Implicit Flow.
code id_token
Returns authorization code AND ID token
- Code returned in query parameters
- ID token returned in fragment
- Code exchanged for access token (and another ID token) at token endpoint
- Allows client to verify user identity before exchanging code
Authorization Response:
HTTP/1.1 302 Found
Location: https://client.example.com/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz
#id_token=eyJhbGc...
Pairs with grant type: authorization_code
Use when:
- Need immediate user identity verification
- Still want refresh token from token endpoint
- Complex applications with multiple components
code token
Returns authorization code AND access token
- Code returned in query parameters
- Access token returned in fragment
- Code can be exchanged for refresh token
- Access token available immediately
Authorization Response:
HTTP/1.1 302 Found
Location: https://client.example.com/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz
#access_token=2YotnFZFEjr1zCsicMWpAA
&token_type=Bearer
&expires_in=3600
Pairs with grant type: authorization_code implicit
Uncommon: Usually want ID token if using hybrid flow
code id_token token
Returns all three: code, ID token, AND access token
- Code returned in query parameters
- ID token and access token returned in fragment
- Maximum information returned immediately
- Complex validation requirements
Authorization Response:
HTTP/1.1 302 Found
Location: https://client.example.com/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz
#id_token=eyJhbGc...
&access_token=2YotnFZFEjr1zCsicMWpAA
&token_type=Bearer
&expires_in=3600
Pairs with grant type: authorization_code implicit
Use when: Need immediate tokens AND refresh capability (rare)
Response Type Breakdown
| Response Type | What’s Returned | Where | Code Exchange? | Token Endpoint? | Refresh Tokens? |
|---|---|---|---|---|---|
code | Authorization code | Query | Yes | Yes | Yes |
token | Access token | Fragment | No | No | No |
id_token | ID token | Fragment | No | No | No |
id_token token | ID + Access token | Fragment | No | No | No |
code id_token | Code + ID token | Query + Fragment | Yes | Yes | Yes |
code token | Code + Access token | Query + Fragment | Yes | Yes | Yes |
code id_token token | All three | Query + Fragment | Yes | Yes | Yes |
Understanding Response Type Syntax
Space-Separated Values
Multiple values are space-separated and indicate multiple items returned:
id_token tokenmeans “id_token AND token”code id_tokenmeans “code AND id_token”
Order Independence
Order doesn’t matter in the specification:
id_token token=token id_tokencode id_token=id_token code
However, some implementations may be strict about order.
Location Matters
Query parameters (after ?):
- Authorization code
- State parameter
- Error codes
Fragment (after #):
- Access tokens
- ID tokens
- Token metadata (expires_in, token_type)
Relationship to Grant Types
// Authorization Code Flow
{
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"]
}
// Implicit Flow (deprecated)
{
"grant_types": ["implicit"],
"response_types": ["token", "id_token", "id_token token"]
}
// Hybrid Flow
{
"grant_types": ["authorization_code", "implicit"],
"response_types": ["code id_token", "code token", "code id_token token"]
}
// Client Credentials (no authorization endpoint)
{
"grant_types": ["client_credentials"],
"response_types": []
}
// Device Flow (no authorization endpoint redirect)
{
"grant_types": ["urn:ietf:params:oauth:grant-type:device_code"],
"response_types": []
}
Security Considerations
Why code is Secure
- Tokens never in browser: Access tokens only at token endpoint
- Client authentication: Confidential clients authenticate when exchanging code
- Short-lived codes: Authorization codes expire quickly (typically 60 seconds)
- One-time use: Codes can only be exchanged once
- PKCE protection: Public clients use PKCE to prevent code interception
Why Implicit/Hybrid are Less Secure
- Tokens in URL fragment: Can leak via browser history, referer headers, logs
- No client authentication: Can’t verify client identity
- No refresh tokens: Can’t provide long-lived sessions securely
- Broader attack surface: More places tokens can leak
Validation Requirements
For code
- Verify state parameter matches
- Exchange code at token endpoint
- Validate tokens received from token endpoint
For id_token (any response with ID token)
- Verify state parameter matches
- Validate ID token signature
- Verify
iss(issuer) claim - Verify
aud(audience) claim matches client_id - Verify
exp(expiration) hasn’t passed - Verify
nonceclaim matches (if nonce was used) - If code is also present, verify
c_hashin ID token - If access token is also present, verify
at_hashin ID token
For Hybrid Flows
- Validate ID token from authorization response
- Exchange code at token endpoint
- Validate tokens from token endpoint
- Verify ID token from authorization response matches token endpoint (same
subclaim)
Modern Best Practices (2025+)
Recommended: Authorization Code Flow
For all application types:
{
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"]
}
With PKCE for public clients:
{
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"token_endpoint_auth_method": "none"
}
Use with Caution: Hybrid Flows
Only use hybrid flows if you have specific requirements:
- Multiple backend components needing different token types
- Performance optimization requiring immediate tokens
- Complex architectures with legacy constraints
Avoid: Implicit Flow
Do not use any implicit flow response types:
tokenid_tokenid_token token
Migration path: All implicit flows should migrate to code with PKCE.
Example Registrations
Web Application
{
"client_name": "My Web App",
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"token_endpoint_auth_method": "private_key_jwt",
"redirect_uris": ["https://example.com/callback"]
}
Single Page Application
{
"client_name": "My SPA",
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"token_endpoint_auth_method": "none",
"redirect_uris": ["https://example.com/callback"]
}
Mobile Application
{
"client_name": "My Mobile App",
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"token_endpoint_auth_method": "none",
"redirect_uris": ["com.example.app:/callback"]
}
Backend Service (no authorization endpoint)
{
"client_name": "My Service",
"grant_types": ["client_credentials"],
"response_types": [],
"token_endpoint_auth_method": "private_key_jwt"
}
Authorization Request Examples
Authorization Code Flow
GET /authorize?
response_type=code
&client_id=s6BhdRkqt3
&state=xyz
&redirect_uri=https://client.example.com/callback
&scope=openid profile email
HTTP/1.1
Host: authorization-server.com
Authorization Code with PKCE
GET /authorize?
response_type=code
&client_id=s6BhdRkqt3
&state=xyz
&redirect_uri=https://client.example.com/callback
&scope=openid profile email
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
HTTP/1.1
Host: authorization-server.com
Hybrid Flow (code + id_token)
GET /authorize?
response_type=code id_token
&client_id=s6BhdRkqt3
&state=xyz
&nonce=n-0S6_WzA2Mj
&redirect_uri=https://client.example.com/callback
&scope=openid profile email
HTTP/1.1
Host: authorization-server.com