OAuth 2.0 Overview
OAuth 2.0 is an authorization framework that enables third-party applications to obtain limited access to a user's resources without exposing credentials. It has become the industry standard for API authorization, used by Google, GitHub, Facebook, and virtually every major platform.
Core Roles
| Role | Description | Example |
|------|-------------|---------|
| Resource Owner | The user who grants access | You, the end user |
| Client | The application requesting access | Your mobile app or SPA |
| Authorization Server | Issues tokens after authentication | Google's OAuth 2.0 endpoint |
| Resource Server | Hosts the protected resources | Google Calendar API |
Grant Types
OAuth 2.0 defines several grant types for different scenarios:
The Problem PKCE Solves
Before PKCE (Proof Key for Code Exchange), public clients like SPAs and mobile apps used the Implicit Grant, which returned the access token directly in the URL fragment. This approach had serious security issues:
2. Tokens were accessible to JavaScript in the same origin
3. No way to verify the authorization code exchange came from the legitimate client
PKCE eliminates these issues by introducing a cryptographic challenge-verifier pair.
PKCE Flow Step by Step
Step 1: Generate Code Verifier and Challenge
The client generates a cryptographically random string called the code verifier, then creates a SHA-256 hash (the code challenge).
function generateCodeVerifier() {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return base64URLEncode(array);
}
async function generateCodeChallenge(verifier) {
const encoder = new TextEncoder();
const data = encoder.encode(verifier);
const digest = await crypto.subtle.digest('SHA-256', data);
return base64URLEncode(new Uint8Array(digest));
}
function base64URLEncode(buffer) {
return btoa(String.fromCharCode(...new Uint8Array(buffer)))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}
Step 2: Redirect to Authorization Server
The client redirects the user to the authorization server, including the code challenge and the method used (`S256`).
https://auth.example.com/authorize?
response_type=code&
client_id=YOUR_CLIENT_ID&
redirect_uri=https://yourapp.com/callback&
code_challenge=YOUR_SHA256_CHALLENGE&
code_challenge_method=S256&
state=CSRF_TOKEN
The `state` parameter is a CSRF token that must be verified when the user returns.
Step 3: User Authenticates and Consents
The authorization server authenticates the user, presents a consent screen, and redirects back to the client with a temporary authorization code.
https://yourapp.com/callback?code=AUTH_CODE&state=CSRF_TOKEN
The client must verify that the `state` parameter matches what it sent.
Step 4: Exchange Code for Token
The client sends the authorization code along with the original plain-text code verifier to the token endpoint.
curl -X POST https://auth.example.com/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "code=AUTH_CODE" \
-d "redirect_uri=https://yourapp.com/callback" \
-d "client_id=YOUR_CLIENT_ID" \
-d "code_verifier=ORIGINAL_VERIFIER"
The authorization server computes the SHA-256 hash of the verifier and compares it to the challenge received in step 2. If they match, it issues tokens.
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "dGhpcyBpcyBhIHJlZnJl...",
"scope": "openid profile email"
}
Why PKCE Is Secure
Without PKCE, an intercepted authorization code could be exchanged by an attacker. With PKCE, the attacker would also need the original code verifier, which was never transmitted during the authorization step. Since only the legitimate client possesses the verifier, the exchange is secure even over an insecure channel.
Refresh Tokens with PKCE
Refresh tokens extend access without requiring the user to re-authenticate. When the access token expires, the client uses the refresh token to obtain a new one:
curl -X POST https://auth.example.com/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "refresh_token=REFRESH_TOKEN" \
-d "client_id=YOUR_CLIENT_ID"
Best Practices
Summary
PKCE transforms the authorization code flow into a secure protocol for public clients by adding a cryptographic binding between the authorization request and the token exchange. Its proof-of-possession model ensures that even if the authorization code is intercepted, it cannot be exchanged without the original verifier. For any new application, the PKCE-authorized code flow is the recommended OAuth 2.0 grant type.