OAuth 2.0: Grant Types & Authorization Code Flow with PKCE
Master delegated authorization: Authorization Code Grant with PKCE (Proof Key for Code Exchange) for SPAs and Mobile, Client Credentials for microservices, Device Flow for CLIs, and deprecation of the Implicit and Password grants.
OAuth 2.0 Authorization Code Flow with PKCE 🛡️
Cryptographic binding between authorization request and token exchange preventing code interception attacks on public clients.
01.1. What is OAuth 2.0? Delegated Authorization Explained
OAuth 2.0 (RFC 6749) is an open industry standard framework for delegated authorization. It allows a third-party application (e.g., Spotify) to access resources hosted on a service provider (e.g., Google Drive or Facebook Profile) on behalf of a user without the user ever revealing their credentials (passwords) to the third party.
The 4 Core Roles in OAuth 2.0:
- Resource Owner: The end user who owns the data and grants access.
- Client: The application requesting access (e.g., Single Page App, Mobile App, Backend Service).
- Confidential Client: A server-side application capable of securely maintaining a secret (e.g., Node.js backend with
CLIENT_SECRET). - Public Client: An application running on user-controlled hardware incapable of keeping secrets (e.g., React SPA, iOS/Android mobile app).
- Confidential Client: A server-side application capable of securely maintaining a secret (e.g., Node.js backend with
- Authorization Server: The system issuing access tokens after authenticating the Resource Owner and obtaining consent (e.g., Auth0, Okta, Keycloak, Google Identity).
- Resource Server: The backend API hosting the protected data (e.g.,
https://api.company.com/v1/orders). It accepts and validates access tokens.
02.2. Deep Dive: Authorization Code Flow with PKCE (RFC 7636)
For Single Page Applications (SPAs) and Mobile Apps (Public Clients), Authorization Code with PKCE (Proof Key for Code Exchange) is the mandatory gold standard.
Why PKCE was Created (The Authorization Code Interception Attack):
In native mobile apps, custom URL schemes (e.g., myapp://callback) register with the mobile operating system. Malicious apps installed on the same device could hijack the custom URI scheme and intercept the returned code. Because public clients have no secret key, the malicious app could immediately exchange the stolen code for an access token.
The Cryptographic Mechanics of PKCE:
- Generate
code_verifier: The client generates a cryptographically high-entropy random string (43 to 128 characters from unreserved characters:[A-Z],[a-z],[0-9],-,.,_,~):code_verifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk" - Calculate
code_challenge: The client hashes the verifier using SHA-256 and Base64URL-encodes the digest:
code\_challenge = Base64URL(SHA256(code\_verifier))
- Authorization Request: The client directs the browser to
/authorizewithcode_challengeandcode_challenge_method=S256. The Authorization Server stores the challenge alongside the generated authorization code. - Token Exchange: The client sends a
POST /oauth/tokenincluding the plaincode_verifierin memory. - Validation: The Authorization Server hashes the provided
code_verifierwith SHA-256. If the result matches the previously storedcode_challenge, it proves that the entity requesting tokens is the exact same entity that initiated the authorization request. Even if an attacker intercepted the authorization code, they cannot exchange it without the secretcode_verifier!
03.3. Overview of Modern vs Deprecated Grant Types
Modern OAuth 2.1 specifications consolidate grant types and deprecate dangerous legacy patterns:
1. Authorization Code Flow with PKCE (Modern Standard):
- Target: SPAs (React, Vue), Mobile Apps (iOS, Android), and Server-Side Web Apps.
- Security: Maximum protection against token leakage in browser history and redirection hijacking.
2. Client Credentials Grant (Machine-to-Machine M2M):
- Target: Internal daemon services, background cron jobs, and microservice-to-microservice communication.
- Workflow: The client authenticates directly using its
client_idandclient_secretviaPOST /oauth/tokento obtain an access token with backend scopes (no user or browser involved).
3. Device Authorization Grant (RFC 8628):
- Target: Smart TVs, CLI tools, Apple TV, game consoles (devices with limited input capabilities).
- Workflow: The device displays a short verification code and URL (
https://activate.example.com). The user visits the URL on their smartphone or laptop to authorize the device.
⚠️ Deprecated Grant Types (Do Not Use!):
- Implicit Grant (Deprecated): Returned access tokens directly in URL fragments (
#access_token=...), exposing tokens to browser history, referrer headers, and malicious extensions. - Resource Owner Password Credentials (ROPC / Deprecated): Required users to type their raw username and password directly into the client application, defeating the purpose of delegated authorization.
04.4. Production PKCE Helper Implementation in TypeScript
Below is a standard Web Crypto API implementation for generating PKCE parameters in browser/Node environments:
typescript/** * Generates a cryptographically secure random code_verifier and SHA-256 code_challenge * compliant with RFC 7636. */ export async function generatePKCE(): Promise<{ codeVerifier: string; codeChallenge: string }> { // 1. Generate 32 cryptographically random bytes (high-entropy) const randomBytes = new Uint8Array(32); crypto.getRandomValues(randomBytes); // 2. Base64URL encode to produce code_verifier (43 characters) const codeVerifier = base64UrlEncode(randomBytes); // 3. Hash code_verifier with SHA-256 const encoder = new TextEncoder(); const data = encoder.encode(codeVerifier); const hashBuffer = await crypto.subtle.digest('SHA-256', data); // 4. Base64URL encode the SHA-256 digest to produce code_challenge const codeChallenge = base64UrlEncode(new Uint8Array(hashBuffer)); return { codeVerifier, codeChallenge }; } function base64UrlEncode(buffer: Uint8Array): string { let binary = ''; const bytes = new Uint8Array(buffer); const len = bytes.byteLength; for (let i = 0; i < len; i++) { binary += String.fromCharCode(bytes[i]); } return btoa(binary) .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=+$/, ''); }
⚖️Architectural Trade-offs & Production Realities
Architectural Advantages
- Eliminates password sharing with third parties, enabling granular, time-bound scoped access
- PKCE eliminates authorization code interception attacks on public clients (SPAs and mobile)
- Client Credentials grant provides a standardized security model for machine-to-machine microservice communication
Trade-offs & Constraints
- Multi-step redirect workflows add network latency during initial user onboarding and login
- Complex protocol state management (state parameter validation, token rotation, scope management)
When connecting Spotify to Discord or Google Assistant, Spotify initiates an OAuth 2.0 PKCE flow. The user grants specific scopes (`user-read-playback-state`), and Spotify issues a scoped access token to Discord without Discord ever seeing or storing the user's Spotify credentials.
🎯 Staff+ Engineering Takeaways
- OAuth 2.0 is a framework for delegated authorization, not authentication.
- Authorization Code with PKCE is mandatory for all SPAs and Mobile Public Clients.
- Client Credentials grant is the standard for service-to-service backend authorization.
- Implicit Grant and Resource Owner Password Grant are deprecated and insecure.
Topic Knowledge Assessment 🧠
Step through 2 scenario questions to test your staff-level grasp.
Why was PKCE (Proof Key for Code Exchange) created for OAuth 2.0 public clients (mobile apps and SPAs)?
How clear and staff-actionable was this system breakdown?