OAuth 2.0 & OpenID Connect
Checking access...
OAuth 2.0 is the industry-standard protocol for delegated authorization. It enables applications to obtain limited access to user accounts on HTTP services without exposing user credentials. OpenID Connect (OIDC) builds on OAuth 2.0 to add an identity layer, making it the dominant protocol for modern authentication and authorization.
Together, they power the authentication and authorization flows behind nearly every major SaaS platform, mobile app, and API ecosystem.
OAuth 2.0 — The Authorization Framework
OAuth 2.0 is not an authentication protocol — it is a delegated authorization framework. It allows a user (resource owner) to grant a third-party application (client) limited access to their resources (hosted by the resource server) without sharing their password.
OAuth 2.0 Roles
| Role | Description | Example |
|---|---|---|
| Resource Owner | The user who owns the protected data | End user |
| Client | The application requesting access | Mobile app, web app, server-side script |
| Authorization Server | Issues tokens after authenticating the resource owner and obtaining authorization | IdP (Okta, Auth0, Azure AD) |
| Resource Server | Hosts the protected resources, accepts and validates access tokens | API server |
OAuth 2.0 Grant Types
| Grant Type | Use Case | Security Level | Refresh Tokens? |
|---|---|---|---|
| Authorization Code | Server-side web apps, mobile apps (with PKCE) | High | Yes |
| Authorization Code + PKCE | Public clients (SPA, mobile) — recommended | Very High | Yes |
| Client Credentials | Server-to-server, machine-to-machine | High | Yes (if configured) |
| Device Code | CLI tools, smart TVs, IoT devices with no browser | Medium | Yes (polling) |
| Refresh Token | Obtain new access tokens without user interaction | Depends on binding | N/A |
| Implicit (deprecated) | Legacy SPAs — DO NOT USE | Low | No |
Danger
The OAuth 2.0 Implicit grant (response_type=token) was deprecated in OAuth 2.1 due to security concerns — access tokens are exposed in the URL fragment, cannot be securely stored in SPAs, and are vulnerable to interception. Use Authorization Code + PKCE instead.
Authorization Code + PKCE — The Gold Standard
PKCE (Proof Key for Code Exchange, pronounced “pixy”) extends the Authorization Code grant to make it secure for public clients that cannot protect a client secret.
┌─────────┐ ┌──────────────┐ ┌──────────────┐│ Client │ │ Auth │ │ Resource ││ (App) │ │ Server │ │ Server │└────┬─────┘ └──────┬───────┘ └──────┬───────┘ │ 1. Generate code_verifier│ │ │ and code_challenge │ │ │ 2. Auth Request │ │ │ + code_challenge ─────→ │ │ 3. User authenticates │ │ │ ←─────────────────────│ │ │ 4. Authorization code │ │ │ ←─────────────────────│ │ │ 5. Auth code │ │ │ + code_verifier ─────→│ │ │ 6. Verify code_verifier │ │ │ matches code_challenge│ │ │ 7. Access Token + RT ────→ │ │ 8. Access Token ────────────────────────────────→│ │ 9. Protected resource ──────────────────────────→│ │ ←─────────────────────────────────────────────│Why PKCE is critical: The code_verifier is a cryptographically random string generated by the client. Even if an attacker intercepts the authorization code, they cannot exchange it for tokens without the code_verifier. This protects against authorization code interception attacks.
Client Credentials Grant — Machine-to-Machine
Used when the client (typically a server-side service) needs to access its own resources, not on behalf of a user. No user involvement.
Service A ──→ Token Endpoint (client_id + client_secret) ──→ Access Token ──→ Service B APISecurity considerations:
- Client secrets must be stored securely (HSM, secrets manager, environment variables)
- Use client authentication methods:
client_secret_basic,client_secret_post, orprivate_key_jwt private_key_jwtis the most secure — uses asymmetric key pair instead of shared secret- Rotate client secrets regularly and immediately after any suspected compromise
OAuth 2.0 Tokens
Access Tokens
Access tokens are credentials used to access protected resources. They represent the authorization granted to the client.
| Property | Opaque Token | JWT (Structured Token) |
|---|---|---|
| Format | Random string | JWT (JSON Web Token) |
| Validation | Introspection endpoint required | Self-validating (signature + claims) |
| Payload | None visible | JSON claims (sub, scope, exp, iss, aud) |
| Performance | Fast to issue, slowest to validate (needs introspection) | Moderate on both — validation is local but parsing overhead |
| Revocation | Immediate (server-side state) | Delayed (until token expires) unless blacklist is checked |
| Size | Small (16-32 bytes) | Larger (compressed JWTs ~1-2KB) |
Refresh Tokens
Refresh tokens allow the client to obtain new access tokens without requiring the user to re-authenticate.
Best practices:
- Bind refresh tokens to the client (sender-constrained)
- Use refresh token rotation — issue a new refresh token with each use; invalidate the previous one
- Set absolute expiration (e.g., 90 days) even with rotation
- Detecting refresh token reuse strongly indicates token theft — revoke all tokens for the user
OpenID Connect — The Identity Layer
OpenID Connect extends OAuth 2.0 with the openid scope. When a client requests the openid scope, the authorization server returns an ID Token — a JWT containing identity claims about the authenticated user.
OIDC Scope-to-Claim Mapping
| Scope | Access | Claims Returned |
|---|---|---|
openid | Required — indicates OIDC request | sub (subject identifier) |
profile | User profile information | name, given_name, family_name, preferred_username |
email | Email address | email, email_verified |
address | Physical address | address (JSON object) |
phone | Phone number | phone_number, phone_number_verified |
offline_access | Refresh token | Enables refresh token issuance |
OIDC Flows
| Flow | response_type | Client Type | PKCE Required? |
|---|---|---|---|
| Authorization Code Flow | code | Confidential (web server) | Recommended |
| Authorization Code + PKCE | code | Public (SPA, mobile) | Required |
| Hybrid Flow | code id_token, code token, code id_token token | Both | Recommended |
| Implicit Flow (deprecated) | id_token token | Public (legacy SPA) | N/A — DO NOT USE |
ID Token Validation
When a client receives an ID Token, it MUST perform the following validations:
- Signature verification — Validate the JWT signature using the IdP’s public key (fetched from the JWKS endpoint)
- Issuer validation — The
issclaim must match the expected IdP issuer identifier - Audience validation — The
audclaim must contain the client’sclient_id - Expiration check — The token must not be expired (check
expclaim) - Issued-at check — The token must not be in the future (check
iatclaim, with acceptable clock skew) - Nonce validation — If a
noncewas sent in the request, it must match thenonceclaim in the ID Token
Token Binding and DPoP
OAuth 2.0 Token Binding
Token binding cryptographically binds tokens to the TLS connection, preventing token export and replay. When a token is bound to the TLS connection, an attacker who steals the token cannot use it from a different device.
DPoP (Demonstration of Proof-of-Possession)
DPoP is a newer mechanism that binds access and refresh tokens to a public-private key pair held by the client. The client proves possession of the private key when using the token.
POST /token HTTP/1.1Host: auth.example.comDPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7...// DPoP proof JWT signed with client's private key// Contains: jti (unique ID), htm (HTTP method), htu (HTTP URI), iat (issued at)Tip
DPoP provides sender-constrained tokens without the TLS layer complexity of token binding. For greenfield deployments, implement DPoP to prevent token replay attacks. For existing deployments, prioritise DPoP for high-value API endpoints.
OAuth 2.1 — The Consolidated Standard
OAuth 2.1 consolidates and simplifies OAuth 2.0 by removing security‑weakening options:
| Change | OAuth 2.0 | OAuth 2.1 |
|---|---|---|
| Implicit grant | Allowed | Removed |
| Resource Owner Password grant | Allowed (deprecated) | Removed |
| Client Credentials grant | No restrictions | Sender-constrained recommended |
| PKCE | Optional for authorization code | Required for public clients |
| Refresh tokens | No binding requirement | Sender-constrained required |
| Redirect URIs | Pattern matches allowed | Exact match required |
Key Takeaways
- OAuth 2.0 is a delegated authorization framework with four roles (resource owner, client, authorization server, resource server) — it is NOT an authentication protocol
- OIDC adds an identity layer to OAuth 2.0 via the ID Token (JWT), enabling secure user authentication on top of OAuth 2.0’s authorization model
- Authorization Code + PKCE is the recommended grant for all public clients (SPA, mobile) — PKCE prevents authorization code interception attacks
- Access tokens can be opaque (server-side validation) or JWT (self-validating); refresh tokens enable ongoing access without re-authentication
- Token binding and DPoP prevent token replay by cryptographically binding tokens to the client or TLS connection
- OAuth 2.1 removes security-weak options (implicit grant, password grant) and requires PKCE for public clients and sender-constrained refresh tokens