PE
Protocol Explorer
OAuth 2.0

OAuth 2.0 — Authorization Code + PKCE

The foundational OAuth 2.0 grant type for delegated user authorization. A client redirects the user to an Authorization Server, which authenticates the user and issues a short-lived authorization code. The client exchanges the code for tokens. PKCE (Proof Key for Code Exchange, RFC 7636) is layered on top to prevent authorization code interception attacks — mandatory for public clients and best practice for all.

RFC 6749 + RFC 7636
Browser / UserWeb AppAuthorization ServerResource Server1GET / (Sign In clicked)3022GET /authorize (redirect)3POST /login (credentials + …4GET /callback?code=...&stat…5POST /token (code exchange)6GET /api/profile
GET https://myapp.example.com/login302

The user clicks 'Sign In'. Before redirecting, the client generates a PKCE code_verifier (random secret) and derives a code_challenge via SHA-256. It redirects the browser to the AS /authorize endpoint embedding the challenge and a CSRF-guard state value.

RFC 7636 §4.1: code_verifier must be 43–128 URL-safe chars from a cryptographically secure source.

RFC 7636 §4.2: S256 is the required method (plain is only allowed if S256 is not feasible). code_challenge = BASE64URL(SHA-256(ASCII(code_verifier))).

RFC 6749 §10.12: The state parameter prevents CSRF — the client must generate it randomly and verify the AS echoes it back unchanged.

response_type=code requests an authorization code; the client will exchange the code for tokens in a back-channel request (step 5), keeping tokens out of the browser.

1 / 6
speed

Step 1: GET / (Sign In clicked)

Request / response
GEThttps://myapp.example.com/login
Accept

text/html

Cryptographic Signature

Construction Steps

1. 1. Generate code_verifier (RFC 7636 §4.1)
code_verifier = high-entropy random string
= "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"

Must be 43–128 characters from the unreserved alphabet:
  [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~"

Generate using a cryptographically secure RNG (e.g., SecureRandom,
crypto.getRandomValues). The value is stored server-side
or in a secure session — NEVER sent until the token request.
2. 2. Derive code_challenge via S256 (RFC 7636 §4.2)
code_challenge = BASE64URL(SHA-256(ASCII(code_verifier)))

  input  : "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
  SHA-256: 0xC481...  (32 bytes)
  BASE64URL(…): "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"

The challenge is safe to embed in the authorization URL because
SHA-256 is not reversible — an observer cannot recover the
code_verifier from the challenge.
3. 3. Store state + code_verifier; send challenge in redirect
Client stores server-side (encrypted cookie or session):
  state          : "xyzABC123"  → returned verbatim by AS (CSRF guard)
  code_verifier  : "dBjftJeZ4CVP..." → revealed only at token endpoint

The authorization URL carries:
  code_challenge        = E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
  code_challenge_method = S256

The AS records the challenge and will verify it when the
authorization code is exchanged.

Signature Base String

code_verifier = dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

Signing Key

Client-generated cryptographically random value (43–128 URL-safe chars)

Signature Output

code_challenge = E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM