OAuth & JWT
So far identity has lived inside one system. The moment a third party is involved — “let this app read my calendar,” “log in with Google” — you face a new problem: how do you grant someone else limited access to your account without handing them your password? OAuth answers the delegation question; JWT answers the “how do we carry proof around” question. They’re often used together but they solve different problems, and conflating them causes endless confusion.
OAuth2: delegation, not login
Section titled “OAuth2: delegation, not login”OAuth2 is a framework for delegated authorization. Its founding insight is that giving an app your password is catastrophic — the app could do anything you can, forever, and you couldn’t revoke it without changing your password everywhere. OAuth replaces that with a scoped, revocable access token issued by the service that holds your account.
The four roles:
RESOURCE OWNER → you (the human) CLIENT → the app wanting access (e.g. a photo printer) AUTHORIZATION SERVER → the one that authenticates you & issues tokens (e.g. Google) RESOURCE SERVER → the API holding your data (e.g. Google Photos)The canonical Authorization Code flow:
1. Client sends you to the Auth Server: "this app wants read access to your photos"2. You authenticate THERE (the client never sees your password) and consent3. Auth Server redirects back to the client with a short-lived AUTHORIZATION CODE4. Client exchanges that code (plus its own secret) for an ACCESS TOKEN — server-to-server5. Client calls the Resource Server with the access token; it returns only the scoped dataThe genius is the scope (“read photos” only) and the separation: your password stays with the party you already trust, and the client gets a narrow, expirable key instead. That’s least privilege applied to delegation.
JWT: a token you can verify without asking
Section titled “JWT: a token you can verify without asking”A JSON Web Token is a compact, signed, self-contained claim. “Self-contained” is the key word: the receiver can verify it using only a key, without calling back to whoever issued it. It has three base64url parts joined by dots:
header . payload . signature ─────── ─────── ───────── {alg,typ} {claims} sign(header.payload, key)
header : which signing algorithm payload: the claims — sub (subject), exp (expiry), iss (issuer), scopes, roles… signature: proves the first two parts weren't altered AND came from the issuerThe signature is everything. Anyone can read a JWT (it is not encrypted — only signed), but only the holder of the key can produce a valid one. Change one byte of the payload and the signature no longer matches, so a tampered token is rejected. This is what lets any server verify a token with just the public key — no shared session store, no database lookup.
Access tokens vs. refresh tokens
Section titled “Access tokens vs. refresh tokens”Tokens force a brutal trade-off. A short lifetime limits the damage of a stolen token; a long lifetime spares users from re-logging-in constantly. You can’t have both with one token — so you use two:
ACCESS TOKEN → short-lived (minutes), sent on every API call. If stolen, it expires fast. Self-verifiable (often a JWT). REFRESH TOKEN → long-lived (days/weeks), sent ONLY to the auth server to mint fresh access tokens. Kept secret; never hits APIs.The access token is the disposable day-pass; the refresh token is the harder-to-steal master key you present only at one well-guarded door. This split is how systems get both convenience and a small blast radius.
The statelessness benefit and the revocation pitfall
Section titled “The statelessness benefit and the revocation pitfall”Here is the whole trade, stated plainly — and it directly continues the sessions-vs-tokens story from Authentication and the scaling story in Statelessness & Sessions.
What statelessness buys us: any server can verify a JWT with just a key — no shared session store, no per-request database lookup. This is a huge win for horizontal scaling and for multi-service architectures, where a token minted by the auth service is trusted by every downstream service independently.
What it costs us: you can’t easily un-issue a self-contained token. A session can be revoked by deleting one row; a valid JWT is valid until it expires, even if you fire the employee or detect the theft thirty seconds after issuing it. The mitigations all claw back some statelessness:
short expiry → limits the window, but never to zero token blocklist → check a denylist of revoked IDs (a lookup — partly stateful again) token versioning → bump a per-user counter; tokens with old versions fail rotate signing key → nuclear option: invalidates EVERY token at onceThere is no perfectly clean answer. You are trading instant revocation for stateless scale, and the right balance — short access tokens plus a refresh mechanism, occasionally backed by a denylist for high-stakes actions — is exactly the kind of deliberate trade this whole part is about.
The thread
Section titled “The thread”OAuth lets a user delegate scoped access without surrendering their password; OIDC adds a proper login on top; JWTs are the signed, self-verifying envelope that carries identity and permissions across trust boundaries without a central lookup. Together they let independent systems trust each other’s claims — buying enormous scale at the price of harder revocation. Whatever crosses these boundaries must also be protected on the wire and at rest, which is exactly Encryption in Transit & at Rest →.
Check your understanding
Section titled “Check your understanding”- What problem does OAuth2 solve that “just give the app your password” creates?
- Distinguish OAuth2 from OIDC — which one is for logging a user in?
- A JWT is signed but not encrypted. What can an attacker do with it, and what can’t they?
- Why split authority into a short access token and a long refresh token instead of one token?
- State the statelessness benefit of JWTs and the revocation pitfall, plus one way to mitigate it.