Learn / APIs, auth and backend architecture
OAuth 2.1, JWT and PKCE
Lesson 16 of 37 · 11 min read ·
The problem OAuth solves
A user wants to let your app read their Google Calendar. The wrong answer is asking for their Google password. OAuth exists so a user can grant a scoped, revocable delegation without ever showing you their credentials.
Four roles, worth naming precisely because the specs use them constantly:
- Resource owner — the user.
- Client — your application.
- Authorization server — issues tokens (Google's login).
- Resource server — holds the data (the Calendar API).
The authorization code flow with PKCE, step by step
This is the only flow you need in 2026. OAuth 2.1 removed the implicit and password grants, and PKCE is now required for every client, not just mobile.
- Your app generates a random
code_verifier(43–128 characters) and hashes it:code_challenge = BASE64URL(SHA256(code_verifier)). - Redirect the user to the authorization server with
response_type=code,client_id,redirect_uri,scope, a randomstate, and thecode_challenge(pluscode_challenge_method=S256). - The user authenticates on the authorization server and consents. Your app never sees the password.
- The server redirects back to your
redirect_uriwith a short-livedcodeand thestateyou sent. - Verify
statematches what you generated. This is your CSRF defence; skipping it is a real vulnerability. - Your backend exchanges the code at the token endpoint, sending the original
code_verifier. - The server hashes the verifier, compares it to the challenge from step 2, and if they match returns an
access_tokenand usually arefresh_token.
What PKCE prevents: an attacker who intercepts the authorization code — through a malicious app registering the same URL scheme, a leaky redirect, browser history — cannot exchange it, because they do not have the verifier. The code alone is useless. That is the entire point, and it is why PKCE is mandatory now.
What a JWT actually is
Three base64url segments separated by dots: header, payload, signature.
It is signed, not encrypted. Anyone can read the payload. Paste one into a decoder and you will see the claims in plain text. Never put anything secret in a JWT.
The signature proves two things: it was issued by whoever holds the signing key, and it has not been modified since. Nothing else.
Validating one properly means checking all of:
- The signature, against the expected key.
alg— against an allowlist you control. Never trust the token's own header to choose the algorithm;alg: noneand algorithm-confusion attacks exist precisely because implementations did.exp— expired tokens are rejected.iss— issued by the authorization server you expect.aud— intended for your API, not a different service that shares an issuer.
Using a maintained library is the correct move here. Hand-rolled JWT validation is a reliable source of vulnerabilities.
The stateless trade-off
A JWT's appeal is that any service can verify it without a database lookup. The price: you cannot revoke it. A stolen token is valid until it expires.
The standard mitigation is short-lived access tokens (5–15 minutes) plus a long-lived refresh token stored server-side, which can be revoked. Logout revokes the refresh token; the access token dies on its own shortly after. If you need instant revocation, you need a check against shared state, and at that point a plain opaque session token is often the simpler, better choice. Sessions are not outdated — they are frequently correct.
Where tokens live
- Browser:
httpOnly,Secure,SameSitecookies.localStorageis readable by any XSS, and one XSS then means full account takeover. - Mobile: the platform keychain / keystore.
- Server-to-server: environment or a secrets manager. See secrets management.
Gotchas
- Register exact redirect URIs. Wildcards enable token theft.
- Scopes should be the minimum the feature needs — least privilege applies to tokens too.
- Rotate refresh tokens on use and detect reuse; a reused refresh token means it leaked.
- Clock skew breaks
expvalidation across machines. Allow a small leeway, and keep NTP running.
Prove you know it
Explain the seven steps above out loud, from memory, and answer two questions without notes: what exactly does PKCE prevent, and why can you not revoke a JWT? If you stall on either, that is the gap.