OAuth 2.0 Grant Types

The grant_types attribute specifies which OAuth 2.0 flows (authorization grants) the client is permitted to use.

Standard Grant Types (RFC 6749)

authorization_code

The most common and secure flow

  • User redirected to authorization server for authentication and consent
  • Authorization server returns authorization code to client’s redirect URI
  • Client exchanges code for tokens at token endpoint
  • Supports refresh tokens
  • Tokens never exposed to browser
  • Works with PKCE for public clients
sequenceDiagram
    participant User
    participant Client
    participant AuthServer as Authorization Server
    participant API as Resource Server

    User->>Client: 1. Click "Login"
    Client->>AuthServer: 2. Redirect to /authorize
    Note over AuthServer: response_type=code
    User->>AuthServer: 3. Authenticate & consent
    AuthServer->>Client: 4. Redirect with code
    Note over Client: https://app.com/callback?code=ABC123
    Client->>AuthServer: 5. POST /token (exchange code)
    Note over Client,AuthServer: Code + client credentials
    AuthServer->>Client: 6. Return tokens
    Note over Client: access_token, refresh_token, id_token
    Client->>API: 7. Call API with access_token
    Note over Client,API: Authorization: Bearer {token}
    API->>Client: 8. Protected resource

Flow:

  1. Client redirects user to authorization endpoint
  2. User authenticates and grants consent
  3. Authorization server redirects back with code
  4. Client exchanges code for access token (and optionally refresh token)

Best for:

  • Web applications (confidential clients)
  • Single Page Applications with PKCE
  • Mobile applications with PKCE
  • Native applications with PKCE

Example Registration:

{
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"]
}

implicit

⚠️ DEPRECATED - Do not use

  • Tokens returned directly in redirect URI fragment (no code exchange)
  • Access token exposed in browser history and logs
  • No refresh tokens
  • No client authentication
  • Originally designed for SPAs before PKCE existed
sequenceDiagram
    participant User
    participant Client
    participant AuthServer as Authorization Server
    participant API as Resource Server

    User->>Client: 1. Click "Login"
    Client->>AuthServer: 2. Redirect to /authorize
    Note over AuthServer: response_type=token
    User->>AuthServer: 3. Authenticate & consent
    AuthServer->>Client: 4. Redirect with tokens in URL fragment
    Note over Client: https://app.com/callback#access_token=XYZ789
    Note over Client: ⚠️ Token exposed in browser!
    rect rgb(255, 200, 200)
        Note over Client: No token endpoint exchange
        Note over Client: No client authentication
        Note over Client: No refresh token
    end
    Client->>API: 5. Call API with access_token
    Note over Client,API: Authorization: Bearer {token}
    API->>Client: 6. Protected resource

Status: Removed in OAuth 2.1 specification

Migration: Use authorization_code with PKCE instead

password (Resource Owner Password Credentials)

⚠️ Use with extreme caution

  • User provides username and password directly to client application
  • Client sends credentials to token endpoint
  • Token endpoint returns access token (and optionally refresh token)
  • Bypasses authorization server’s login page
  • No interactive consent
sequenceDiagram
    participant User
    participant Client
    participant AuthServer as Authorization Server
    participant API as Resource Server

    User->>Client: 1. Enter username & password
    Note over User,Client: User enters credentials in client app
    Client->>AuthServer: 2. POST /token
    Note over Client,AuthServer: grant_type=password
username=johndoe
password=***
client_id=xyz rect rgb(255, 240, 200) Note over AuthServer: ⚠️ Client handles user credentials directly Note over AuthServer: Only for trusted first-party apps! end AuthServer->>Client: 3. Return tokens Note over Client: access_token, refresh_token Client->>API: 4. Call API with access_token Note over Client,API: Authorization: Bearer {token} API->>Client: 5. Protected resource

When to use:

  • ✅ Highly trusted first-party applications only
  • ✅ Migration path from legacy authentication
  • ❌ Never for third-party applications
  • ❌ Avoid if authorization_code flow is possible

Example Request:

POST /token HTTP/1.1
Host: authorization-server.com
Content-Type: application/x-www-form-urlencoded

grant_type=password
&username=johndoe
&password=A3ddj3w
&client_id=s6BhdRkqt3

Example Registration:

{
  "grant_types": ["password", "refresh_token"],
  "response_types": []
}

client_credentials

Machine-to-machine authentication

  • No user context - client acts on its own behalf
  • Client exchanges its own credentials for access token
  • Used for service-to-service communication
  • No refresh tokens (just request new access token when needed)
  • No authorization code or redirect
sequenceDiagram
    participant Client as Client Service
    participant AuthServer as Authorization Server
    participant API as Resource Server

    Note over Client: No user involved
    Client->>AuthServer: 1. POST /token
    Note over Client,AuthServer: grant_type=client_credentials
client_id=service123
client_secret=***
scope=api:read api:write AuthServer->>AuthServer: 2. Verify client credentials AuthServer->>Client: 3. Return access_token Note over Client: access_token only
(no refresh_token) Client->>API: 4. Call API with access_token Note over Client,API: Authorization: Bearer {token} API->>Client: 5. Protected resource Note over Client: When token expires,
request new token

Flow:

  1. Client authenticates directly at token endpoint
  2. Token endpoint returns access token
  3. Client uses access token to call APIs

Best for:

  • Backend services
  • API-to-API communication
  • Batch jobs
  • Daemon processes
  • Service accounts

Example Request:

POST /token HTTP/1.1
Host: authorization-server.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&scope=api:read api:write

Example Registration:

{
  "grant_types": ["client_credentials"],
  "response_types": [],
  "token_endpoint_auth_method": "private_key_jwt"
}

refresh_token

Exchange refresh token for new access token

  • Not a standalone flow - used with other grant types
  • Allows obtaining new access tokens without re-authentication
  • Must be explicitly requested in scope or grant_types
  • Enables long-lived sessions
  • Can be revoked by authorization server
sequenceDiagram
    participant Client
    participant AuthServer as Authorization Server
    participant API as Resource Server

    Note over Client: Client previously obtained
refresh_token from authorization_code
or password grant Client->>API: 1. Call API with access_token API->>Client: 2. 401 Unauthorized (token expired) Client->>AuthServer: 3. POST /token (refresh) Note over Client,AuthServer: grant_type=refresh_token
refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
client_id=xyz AuthServer->>AuthServer: 4. Validate refresh_token AuthServer->>Client: 5. Return new tokens Note over Client: New access_token
Optionally: new refresh_token Client->>API: 6. Retry API call with new access_token Note over Client,API: Authorization: Bearer {new_token} API->>Client: 7. Protected resource

Flow:

  1. Client receives refresh token from initial authorization
  2. When access token expires, client uses refresh token at token endpoint
  3. Token endpoint returns new access token (and optionally new refresh token)

Example Request:

POST /token HTTP/1.1
Host: authorization-server.com
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
&client_id=s6BhdRkqt3

Supported with:

  • authorization_code
  • password
  • implicit (implicit never provides refresh tokens)
  • client_credentials (just request new access token)

Extended Grant Types

urn:ietf:params:oauth:grant-type:jwt-bearer (RFC 7523)

JWT Bearer Token Flow

  • Exchange a JWT for an access token
  • JWT can be self-issued or from another authorization server
  • Used for federation and delegation scenarios
  • Common in enterprise identity systems
sequenceDiagram
    participant Client as Client/Service
    participant IDP as Identity Provider
    participant AuthServer as Authorization Server
    participant API as Resource Server

    Note over Client: Scenario: Service wants to act
on behalf of user from another IDP Client->>IDP: 1. Obtain JWT assertion Note over IDP: User authenticated here IDP->>Client: 2. Return signed JWT Note over Client: JWT contains subject (user),
issuer, audience, expiry Client->>AuthServer: 3. POST /token Note over Client,AuthServer: grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
assertion=eyJhbGciOiJFUzI1NiIsImtpZCI6IjE2In0... AuthServer->>AuthServer: 4. Validate JWT signature & claims Note over AuthServer: Verify issuer, audience,
expiry, signature AuthServer->>Client: 5. Return access_token Client->>API: 6. Call API with access_token Note over Client,API: Authorization: Bearer {token} API->>Client: 7. Protected resource

Use cases:

  • Service account impersonation
  • Cross-domain authentication
  • Token exchange between systems

Example Request:

POST /token HTTP/1.1
Host: authorization-server.com
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&assertion=eyJhbGciOiJFUzI1NiIsImtpZCI6IjE2In0...

urn:ietf:params:oauth:grant-type:saml2-bearer (RFC 7522)

SAML 2.0 Bearer Assertion Flow

  • Exchange a SAML 2.0 assertion for an access token
  • Used for SAML-to-OAuth bridging
  • Common in enterprise identity federation
  • Allows SAML-based systems to access OAuth-protected APIs
sequenceDiagram
    participant User
    participant Client
    participant SAML_IDP as SAML Identity Provider
    participant AuthServer as Authorization Server
    participant API as Resource Server

    User->>Client: 1. Access application
    Client->>SAML_IDP: 2. SAML authentication request
    User->>SAML_IDP: 3. Authenticate
    SAML_IDP->>Client: 4. SAML assertion (XML)
    Note over Client: SAML assertion contains
user identity & attributes Client->>AuthServer: 5. POST /token Note over Client,AuthServer: grant_type=urn:ietf:params:oauth:grant-type:saml2-bearer
assertion=[base64-encoded-SAML] AuthServer->>AuthServer: 6. Validate SAML assertion Note over AuthServer: Verify signature, issuer,
audience, conditions AuthServer->>Client: 7. Return access_token Client->>API: 8. Call API with access_token Note over Client,API: Authorization: Bearer {token} API->>Client: 9. Protected resource

Use cases:

  • Enterprise SSO integration
  • Legacy SAML systems accessing modern APIs
  • Federation across identity providers

urn:ietf:params:oauth:grant-type:device_code (RFC 8628)

Device Authorization Flow

  • For input-constrained devices (smart TVs, IoT devices, CLI tools)
  • Device displays a user code
  • User enters code on another device (phone, computer)
  • Device polls token endpoint until user completes authorization
sequenceDiagram
    participant Device as Smart TV/IoT Device
    participant AuthServer as Authorization Server
    participant User
    participant Browser as User's Browser/Phone
    participant API as Resource Server

    Device->>AuthServer: 1. POST /device_authorization
    Note over Device,AuthServer: client_id=device123
    AuthServer->>Device: 2. Return device_code & user_code
    Note over Device: device_code=GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS
user_code=WDJB-MJHT
verification_uri=https://example.com/device Device->>User: 3. Display user_code & URL Note over Device,User: "Visit example.com/device
Enter code: WDJB-MJHT" User->>Browser: 4. Open verification_uri Browser->>AuthServer: 5. GET /device User->>Browser: 6. Enter user_code Browser->>AuthServer: 7. Submit code User->>Browser: 8. Authenticate & consent loop Poll every 5 seconds Device->>AuthServer: 9. POST /token (poll) Note over Device,AuthServer: grant_type=urn:ietf:params:oauth:grant-type:device_code
device_code=GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS AuthServer->>Device: 10a. authorization_pending (keep polling) end AuthServer->>Device: 10b. Return tokens Note over Device: access_token, refresh_token Device->>API: 11. Call API with access_token API->>Device: 12. Protected resource

Flow:

  1. Device requests device code from authorization server
  2. Device displays user code and verification URL
  3. User visits URL on another device and enters code
  4. Device polls token endpoint
  5. Once user approves, device receives access token

Best for:

  • Smart TVs
  • IoT devices
  • Command-line tools
  • Devices without browsers

Example Registration:

{
  "grant_types": [
    "urn:ietf:params:oauth:grant-type:device_code",
    "refresh_token"
  ],
  "response_types": []
}

urn:ietf:params:oauth:grant-type:token-exchange (RFC 8693)

Token Exchange Flow

  • Exchange one token for another
  • Supports impersonation and delegation
  • Used for microservices calling other services on behalf of user
  • Supports actor tokens for complex delegation chains
sequenceDiagram
    participant User
    participant ServiceA as Service A (Frontend)
    participant AuthServer as Authorization Server
    participant ServiceB as Service B (Backend)
    participant ServiceC as Service C (Data Layer)

    User->>ServiceA: 1. Request with access_token
    Note over ServiceA: Token has broad permissions
    ServiceA->>AuthServer: 2. POST /token (exchange)
    Note over ServiceA,AuthServer: grant_type=urn:ietf:params:oauth:grant-type:token-exchange
subject_token=original_access_token
subject_token_type=urn:ietf:params:oauth:token-type:access_token
scope=serviceB:read (downscoped) AuthServer->>AuthServer: 3. Validate & create new token Note over AuthServer: New token has reduced scope
for ServiceB only AuthServer->>ServiceA: 4. Return new access_token Note over ServiceA: Token scoped for ServiceB ServiceA->>ServiceB: 5. Call ServiceB with new token Note over ServiceA,ServiceB: Authorization: Bearer {downscoped_token} ServiceB->>AuthServer: 6. POST /token (exchange again) Note over ServiceB,AuthServer: actor_token=serviceB_token
subject_token=downscoped_token
scope=serviceC:write AuthServer->>ServiceB: 7. Return token with actor chain ServiceB->>ServiceC: 8. Call ServiceC Note over ServiceB,ServiceC: Token shows: User -> ServiceA -> ServiceB ServiceC->>ServiceB: 9. Response ServiceB->>ServiceA: 10. Response ServiceA->>User: 11. Final response

Use cases:

  • Microservice architecture (service A calls service B on behalf of user)
  • Token downscoping (reduce scope/permissions)
  • Token type conversion (JWT to opaque, etc.)

Example Request:

POST /token HTTP/1.1
Host: authorization-server.com
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&scope=api:read

Grant Type Comparison

Grant TypeUser ContextRedirectUse CaseSecurity
authorization_code✅ Yes✅ YesWeb/mobile apps🔒🔒🔒🔒🔒
implicit✅ Yes✅ YesDEPRECATED
password✅ Yes❌ NoTrusted first-party only🔒🔒
client_credentials❌ No❌ NoService accounts🔒🔒🔒🔒
refresh_token✅ Yes❌ NoToken refresh🔒🔒🔒🔒
device_code✅ Yes❌ NoInput-limited devices🔒🔒🔒🔒

Typical Combinations

Web Application (Confidential Client)

{
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "private_key_jwt"
}

Single Page Application (Public Client)

{
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none"
}

Note: Must use PKCE

Mobile/Native Application (Public Client)

{
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none"
}

Note: Must use PKCE and platform-specific best practices

Backend Service/API (Confidential Client)

{
  "grant_types": ["client_credentials"],
  "response_types": [],
  "token_endpoint_auth_method": "private_key_jwt"
}

IoT/Smart TV Device

{
  "grant_types": [
    "urn:ietf:params:oauth:grant-type:device_code",
    "refresh_token"
  ],
  "response_types": [],
  "token_endpoint_auth_method": "none"
}

Legacy First-Party App (Migration Scenario)

{
  "grant_types": ["password", "refresh_token"],
  "response_types": [],
  "token_endpoint_auth_method": "client_secret_basic"
}

Note: Migrate to authorization_code as soon as possible

Security Recommendations

  • Use authorization_code for all user-facing applications
  • Use PKCE with authorization_code for public clients
  • Use client_credentials for service accounts
  • Use refresh_token when long-lived sessions are needed
  • Use device_code for input-constrained devices

⚠️ Use with Caution

  • password - Only for highly trusted first-party applications
  • Migration scenarios only
  • Consider authorization_code instead

❌ Do Not Use

  • implicit - Deprecated in OAuth 2.1
  • Replaced by authorization_code with PKCE
  • No security benefits, multiple vulnerabilities

References