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 TypeWhat’s ReturnedWhereCode Exchange?Token Endpoint?Refresh Tokens?
codeAuthorization codeQueryYesYesYes
tokenAccess tokenFragmentNoNoNo
id_tokenID tokenFragmentNoNoNo
id_token tokenID + Access tokenFragmentNoNoNo
code id_tokenCode + ID tokenQuery + FragmentYesYesYes
code tokenCode + Access tokenQuery + FragmentYesYesYes
code id_token tokenAll threeQuery + FragmentYesYesYes

Understanding Response Type Syntax

Space-Separated Values

Multiple values are space-separated and indicate multiple items returned:

  • id_token token means “id_token AND token”
  • code id_token means “code AND id_token”

Order Independence

Order doesn’t matter in the specification:

  • id_token token = token id_token
  • code 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

  1. Tokens never in browser: Access tokens only at token endpoint
  2. Client authentication: Confidential clients authenticate when exchanging code
  3. Short-lived codes: Authorization codes expire quickly (typically 60 seconds)
  4. One-time use: Codes can only be exchanged once
  5. PKCE protection: Public clients use PKCE to prevent code interception

Why Implicit/Hybrid are Less Secure

  1. Tokens in URL fragment: Can leak via browser history, referer headers, logs
  2. No client authentication: Can’t verify client identity
  3. No refresh tokens: Can’t provide long-lived sessions securely
  4. Broader attack surface: More places tokens can leak

Validation Requirements

For code

  1. Verify state parameter matches
  2. Exchange code at token endpoint
  3. Validate tokens received from token endpoint

For id_token (any response with ID token)

  1. Verify state parameter matches
  2. Validate ID token signature
  3. Verify iss (issuer) claim
  4. Verify aud (audience) claim matches client_id
  5. Verify exp (expiration) hasn’t passed
  6. Verify nonce claim matches (if nonce was used)
  7. If code is also present, verify c_hash in ID token
  8. If access token is also present, verify at_hash in ID token

For Hybrid Flows

  1. Validate ID token from authorization response
  2. Exchange code at token endpoint
  3. Validate tokens from token endpoint
  4. Verify ID token from authorization response matches token endpoint (same sub claim)

Modern Best Practices (2025+)

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:

  • token
  • id_token
  • id_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

References