Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Authorization Code Flow with PKCE

The Authorization Code flow with PKCE (Proof Key for Code Exchange) is the primary way applications authenticate users through Aero2. PKCE prevents authorization code interception attacks and is mandatory in Aero2.

Flow Diagram

Browser
Your App
Aero2
  1. 1
    Browser → Your App
    User clicks Login
  2. 2
    Your App
    Generate PKCE code_verifier + code_challenge
    crypto.getRandomValues() + SHA-256
  3. 3
    Your App → Aero2
    Redirect to /oauth2/authorize
    ?client_id=...&redirect_uri=...&code_challenge=...&code_challenge_method=S256&response_type=code&scope=openid+profile+email
  4. 4
    Aero2 → Browser
    Show login page
    User authenticates with identity provider
  5. 5
    Aero2 → Your App
    Redirect to redirect_uri with code
    ?code=abc123&state=xyz
  6. 6
    Your App → Aero2
    Exchange code for tokens
    POST /oauth2/token grant_type=authorization_code&code=...&code_verifier=...
  7. 7
    Aero2 → Your App
    Return access_token, id_token, refresh_token
    RS256-signed JWTs
  8. 8
    Your App → Aero2
    Fetch user info (optional)
    GET /oauth2/userinfo Authorization: Bearer <access_token>

Step by Step

Generate PKCE Values

Your application generates a random code_verifier and computes its SHA-256 hash as the code_challenge.

// Generate code_verifier (43-128 characters, URL-safe)
const array = new Uint8Array(32);
crypto.getRandomValues(array);
const codeVerifier = base64UrlEncode(array);
 
// Compute code_challenge = SHA-256(code_verifier)
const hash = await crypto.subtle.digest(
  'SHA-256',
  new TextEncoder().encode(codeVerifier)
);
const codeChallenge = base64UrlEncode(hash);

Try it yourself:

PKCE Generator

Click Generate to create a code_verifier and code_challenge pair

Redirect to Authorize

Redirect the user to Aero2's authorization endpoint with your client details and the code challenge:

GET https://aero2.dev/oauth2/authorize
  ?client_id=your-client-id
  &redirect_uri=https://yourapp.com/callback
  &response_type=code
  &scope=openid profile email
  &state=random-csrf-token
  &code_challenge=<code_challenge>
  &code_challenge_method=S256
  &nonce=random-nonce

User Authenticates

Aero2 presents the login page. The user signs in with their identity provider (e.g., GitHub).

Receive Authorization Code

After authentication, Aero2 redirects back to your redirect_uri with a short-lived authorization code:

https://yourapp.com/callback?code=abc123&state=random-csrf-token

Exchange Code for Tokens

Your server exchanges the code for tokens at the token endpoint:

curl -X POST https://aero2.dev/oauth2/token \
  -d "grant_type=authorization_code" \
  -d "code=abc123" \
  -d "redirect_uri=https://yourapp.com/callback" \
  -d "client_id=your-client-id" \
  -d "client_secret=your-client-secret" \
  -d "code_verifier=<original_code_verifier>"

Response:

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "dGhpcyBpcyBh...",
  "id_token": "eyJhbGciOiJSUzI1NiIs...",
  "scope": "openid profile email"
}

Use the Access Token

Use the access token to call protected APIs:

curl https://aero2.dev/oauth2/userinfo \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."

Why PKCE?

Without PKCE, a malicious app could intercept the authorization code during the redirect and exchange it for tokens. PKCE prevents this by binding the code to the original requester:

  1. Only the app that generated the code_verifier can compute the matching code_challenge
  2. The code can only be exchanged by presenting the original code_verifier
  3. An interceptor without the code_verifier cannot exchange the code

Aero2 only supports S256 (SHA-256) — the plain method is not allowed.

See Also