Selected work

Production engineering

Identity as its own domain

OAuth 2.0 and OpenID Connect in production — Authorization Code with PKCE, JWT validation against JWKS, and SSO between applications.

  • OAuth 2.0
  • OpenID Connect
  • PKCE
  • JWT
  • RS256
  • JWKS
  • SSO
  • Java

Context

Authentication is the one part of an application where a plausible-looking implementation and a correct one are hard to tell apart from the outside. Both let the right user in. Only one of them keeps the wrong user out. Working on identity and access management is what turned OAuth from a set of terms I could define into a set of decisions I could defend — and taught me that most of the difficulty is not the cryptography but the trust boundaries.

Written at the level a public case study can go: the shape of the problems and how I reasoned about them. No proprietary code, internal endpoints, schemas, customer details or unreleased work appears here.

Identity as a separate domain

The boundary in the middle is the point. Everything above it issues assertions; everything below it verifies them and decides what they permit.

Identity domain
Authorization endpoint/authorizeFront channel. Where the user authenticates, on the identity provider’s own domain.
Token endpoint/tokenBack channel. Exchanges the code for tokens after verifying the PKCE verifier.
JWKS/.well-known/jwks.jsonPublic keys, so verification needs no shared secret and keys can rotate.
Trust boundary
Token validationsignature · iss · aud · expA valid signature proves who issued the token. The claims decide whether it was issued for you.
Application domain
Sessionserver-sideThe browser holds a session cookie, not a token. XSS then cannot steal a bearer token.
Authorizationmay they do this?A different question from authentication, and the application’s own to answer.
Resource APIsthe actual productEach verifies the token it receives rather than trusting the caller.

The flow, step by step

This is the diagram I wish someone had shown me first. Open any step for the reasoning behind it.

Authorization Code flow with PKCE

Step through it. The colour tells you whether a message can be seen by an attacker who controls the browser.

Front channelBack channelLocal

The engineering

  1. Why the authorization code exists at all

    The first thing worth understanding is that the authorization server cannot talk to the application directly — it can only redirect the browser. Everything in the Authorization Code flow follows from that: the code is a short-lived, single-use reference handed through an untrusted channel (the URL bar, the history, the referer header), which the client then exchanges for tokens over a direct back-channel call that an attacker cannot observe. Tokens never travel through the browser’s address bar.

  2. PKCE, and the attack it actually stops

    PKCE exists because a redirect URI is not a secret. If a malicious application on the same device can register or intercept the redirect, it can steal the authorization code in transit. PKCE binds the code to the client that started the flow: the client sends a hash of a random verifier up front, and must present the original verifier at the token endpoint. Intercepting the code is then useless without the verifier, which never left the legitimate client.

  3. Validating a token means more than checking the signature

    An RS256 JWT is signed, not encrypted — anyone can read the claims; only the holder of the private key could have produced the signature. Verification means fetching the right public key from the JWKS endpoint by key ID, checking the signature, and then checking the claims that actually scope the token: issuer, audience, expiry, and nonce where the flow requires it. A token that verifies cryptographically but was issued for a different audience is not a valid token for you.

  4. Authentication and authorization are different questions

    Authentication answers "who is this", authorization answers "may they do this", and conflating them produces systems where being logged in is accidentally the same as being permitted. Keeping identity as its own domain with its own boundary — the identity provider issues assertions, the application decides what those assertions entitle you to — is what makes permissions reasonable to change later.