OAuth 2.0 Grant Types & Flows
Complete technical specification of Authorization Code with PKCE, M2M Client Credentials, SAML 2.0 Bearer assertions, Device Flow, parameter line-by-line annotations, and token rotation.
Grant Type Decision Matrix: "What Type of App Are You Building?"
Choosing the correct OAuth 2.0 flow depends on your application architecture, client trust tier, and device capabilities:
| Application Type | Client Type | Recommended Flow | Client Secret? | Token Recipients |
|---|---|---|---|---|
| Server-Side Web App (Node.js, Express, Next.js SSR, Spring, ASP.NET) | Confidential | Authorization Code Flow | ✅ Yes (Kept on backend server) | ID Token → Web Server Access Token → Web Server |
| Single Page App (SPA) (React, Vue, Angular, Svelte) | Public | Auth Code + PKCE (RFC 7636) | ❌ No (Never store secret in browser) | ID Token → SPA Frontend Access Token → SPA / API |
| Native Mobile App (iOS Swift, Android Kotlin, React Native) | Public | Auth Code + PKCE (RFC 7636) | ❌ No (Binary can be decompiled) | ID Token → Mobile App Access Token → Mobile App |
| Daemon / Cron / Microservice (Background worker, CLI tool, M2M) | Confidential | Client Credentials Flow | ✅ Yes (Stored in secret manager) | Access Token → Daemon Service (No user / No ID token) |
| IoT / Smart TV / CLI Device (Apple TV, PlayStation, headless terminal) | Public | Device Authorization Flow (RFC 8628) | ❌ No | Tokens → Device after secondary phone/browser approval |
Authorization Code Flow (Golden Standard - 3-Legged)
The 3-legged authorization code grant is the industry standard for interactive user login and delegated API authorization:
sequenceDiagram
autonumber
actor User as 👤 End User
participant Client as 💻 Public Client (SPA / App)
participant AuthServer as 🛡️ Authorization Server
participant API as ⚡ Protected API / Gateway
Note over Client: Generate code_verifier & code_challenge (S256)
Client->>AuthServer: 1. GET /authorize?response_type=code&code_challenge=...
AuthServer->>User: 2. Prompts Login & Consent Screen
User->>AuthServer: 3. Submits Credentials & Consents
AuthServer-->>Client: 4. 302 Redirect with ?code=SplxlOBeZQQYbYS6WxSbIA&state=...
Note over Client,AuthServer: Back Channel Exchange
Client->>AuthServer: 5. POST /token (grant_type=authorization_code, code, code_verifier)
Note over AuthServer: Verify SHA256(code_verifier) == code_challenge
AuthServer-->>Client: 6. 200 OK { access_token, id_token, refresh_token }
Client->>API: 7. GET /api/resource (Authorization: Bearer <access_token>)
API-->>Client: 8. 200 OK (Protected JSON Data)
Full HTTP Request & Response Examples:
// STEP 1: Front-Channel Authorization Request (Browser GET)
GET /authorize?
response_type=code
&client_id=s6BhdRkqt3
&redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb
&scope=openid%20profile%20email
&state=af0ifjsldkj HTTP/1.1
Host: authorization-server.example.com
// STEP 2: Front-Channel Redirect Response (302 Found)
HTTP/1.1 302 Found
Location: https://client.example.com/cb?
code=SplxlOBeZQQYbYS6WxSbIA
&state=af0ifjsldkj
// STEP 3: Back-Channel Token Request (Server-to-Server HTTPS POST)
POST /token HTTP/1.1
Host: authorization-server.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
grant_type=authorization_code
&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb
&client_id=s6BhdRkqt3
&client_secret=gX1fBat3bV
// STEP 4: Token Response JSON Payload
{
"access_token": "SlAV32hkKG",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "8xLOxBtZp8",
"scope": "openid profile email"
} Annotated Parameter Breakdown
1. GET /authorize Request Parameters
| Parameter | Type | Purpose & Security Function |
|---|---|---|
response_type=code | Required | Tells the Authorization Server the client is initiating the Authorization Code Grant. |
client_id | Required | The public identifier assigned to the application during developer registration. |
redirect_uri | Required | Destination URL where the user browser returns with the authorization code. Must match registered whitelist. |
scope | Optional/Recommended | Space-separated string specifying requested access boundaries (e.g. openid profile email). |
state | Required for Security | Cryptographic random value verified on callback to prevent Cross-Site Request Forgery (CSRF). |
2. POST /token Request Parameters
| Parameter | Type | Purpose & Security Function |
|---|---|---|
grant_type=authorization_code | Required | Informs the token endpoint that an authorization code is being redeemed for access tokens. |
code | Required | The single-use, short-lived (30-60s) authorization code received from the front-channel redirect. |
redirect_uri | Required | Must match the exact redirect_uri sent in Step 1 to prevent redirect hijacking. |
client_id / client_secret | Confidential Only | Confidential credentials verifying the client identity on the back-channel. |
PKCE Cryptographic Extension (RFC 7636)
Proof Key for Code Exchange (PKCE) replaces static client secrets for public clients (SPAs and mobile apps) to prevent authorization code interception:
- Client Generates Verifier:
code_verifier= Cryptographically random string (43–128 characters using[A-Z],[a-z],[0-9],-,.,_,~). - Client Computes Challenge:
code_challenge = BASE64URL(SHA256(code_verifier)). - Front Channel: Client sends
code_challengeandcode_challenge_method=S256in theGET /authorizerequest. - Back Channel: Client sends original plain
code_verifierin thePOST /tokenrequest. - Server Validation: Authorization Server hashes the incoming verifier and checks:
BASE64URL(SHA256(code_verifier)) === code_challenge.
Client Credentials Flow (RFC 6749 Section 4.4)
Used for machine-to-machine (M2M) communication without an interactive human user:
sequenceDiagram
autonumber
participant Daemon as 🤖 Microservice / Daemon / CLI
participant IdP as 🔑 Authorization Server
participant API as 🗄️ Protected Backend API
Note over Daemon,IdP: Direct Machine-to-Machine (No User Involved)
Daemon->>IdP: 1. POST /token (grant_type=client_credentials, client_id, client_secret)
Note over IdP: Authenticate Client Secret & Validate Scopes
IdP-->>Daemon: 2. 200 OK { access_token: "eyJ...", token_type: "Bearer", expires_in: 3600 }
Daemon->>API: 3. GET /api/protected/reports (Authorization: Bearer eyJ...)
Note over API: Verify JWT Signature (JWKS) & Check Scopes
API-->>Daemon: 4. 200 OK (Internal Reports JSON Payload)
POST /token HTTP/1.1
Host: authorization-server.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id=daemon_service_app
&client_secret=secure_daemon_secret
&scope=read:reports%20execute:jobs
// Token Response
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "read:reports execute:jobs"
} SAML 2.0 Bearer Assertion Flow (RFC 7522)
Enables an enterprise client that has already authenticated via legacy SAML 2.0 SSO to exchange its signed XML assertion for an OAuth 2.0 Access Token without forcing the user to re-authenticate:
POST /token HTTP/1.1
Host: authorization-server.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Asaml2-bearer
&assertion=PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi... Device Authorization Flow (RFC 8628)
Designed for input-constrained devices (smart TVs, gaming consoles, CLI tools) with no browser or keyboard:
To sign in, visit this URL on your mobile phone or computer:
CODE: WDJB-MJHT
The smart TV continuously polls the Authorization Server in the background until the user approves the prompt on their phone.
// 1. Device initiates code request
POST /device/code HTTP/1.1
client_id=smart_tv_app
// 2. Authorization Server returns user_code and device_code
{
"device_code": "GmRhmhcxhwAzkoEqiMEg...",
"user_code": "WDJB-MJHT",
"verification_uri": "https://authmatrix.local/activate",
"expires_in": 900,
"interval": 5
}
// 3. Device polls /token endpoint every 5 seconds
POST /token HTTP/1.1
grant_type=urn:ietf:params:oauth:grant-type:device_code
&device_code=GmRhmhcxhwAzkoEqiMEg...
&client_id=smart_tv_app Interaction Code Flow (Native Embedded SDKs)
An modern OAuth extension (pioneered by Okta Identity Engine) enabling mobile and desktop client applications to natively orchestrate multi-factor authentication, biometric challenges, and passwordless authentication step-by-step without requiring a full web browser redirect.
Deprecated Flows in OAuth 2.1
- Implicit Flow (
response_type=token): Returns Access Tokens directly in the browser URL fragment. Highly vulnerable to token extraction from browser history, HTTP referer headers, and malicious browser extensions. Fix: Replace with Auth Code + PKCE. - Resource Owner Password Flow (
grant_type=password): The application collects the user's master password directly. Re-introduces the password anti-pattern and prevents MFA/FIDO2 passkey adoption. Fix: Migrate to Auth Code or Interaction Code Flow.
Refresh Tokens & Rotation Lineage (RFC 6749 Section 6)
Refresh tokens allow applications to obtain fresh Access Tokens without repeatedly prompting the user to log in:
POST /token HTTP/1.1
Host: authorization-server.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
&client_id=s6BhdRkqt3
// Token Refresh Response (Issues new token pair)
{
"access_token": "SlAV32hkKG_NEW",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA_ROTATED"
} Every time a refresh token is exchanged, the Authorization Server invalidates it and issues a fresh refresh token pair. If an attacker intercepts and attempts to use a previously invalidated refresh token, the Authorization Server detects token reuse and immediately invalidates the entire session lineage, locking out the attacker.