Lab · 05 / 16
Anatomy of a login.
What happens between "Sign in" and your name on screen. Step through a real OpenID Connect flow with PKCE and open the token claim by claim.
- Browser
- where you are
- App
- the site you want to enter
- IdP
- checks who you are (Okta, Entra ID…)
- API
- the service holding your data
- Click "Sign in"
Step 1 of 7
The app has no session
In plain words Like a front desk: before you get in, you are sent to the counter that checks your ID.
The user wants in, but the app cannot check passwords itself. It delegates authentication to the identity provider (IdP).
- 302 → /authorize
Step 2 of 7
Redirect with a PKCE challenge
In plain words The app walks you to the counter (the IdP) with a sealed note: only the app knows what is inside.
The app creates a random code_verifier, keeps it, and sends only its SHA-256 hash (code_challenge). state protects against forged callbacks, nonce ties the token to this request.
GET https://idp.example.com/authorize ?response_type=code &client_id=web-app &redirect_uri=https://app.example.com/callback &scope=openid profile email &state=af0ifjsldkj &nonce=n-0S6_WzA2Mj &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM &code_challenge_method=S256 code_verifier (kept by the app) = dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk - password + codepasskey signature
Step 3 of 7
Password and one-time codePasskey
In plain words You show your ID (the password) and one extra proof (the code on your phone).Your phone vouches for you with a fingerprint or face, and it only works on the real site.
The IdP checks the password, then asks for a one-time code from an authenticator app. Two factors, but both can still be phished on a fake page.The device signs a challenge with a private key bound to this domain, unlocked with a fingerprint or face. Nothing to type, nothing to phish on a look-alike site.
- 302 → /callback?code
Step 4 of 7
Back to the app with a code
In plain words The counter hands you a claim ticket: on its own it does not get you in.
The IdP returns a short-lived, single-use code. The app checks that state matches the one it sent. The code alone is useless without the verifier.
GET https://app.example.com/callback ?code=SplxlOBeZQQYbYS6WxSbIA &state=af0ifjsldkj ✓ state matches - POST /token
Step 5 of 7
Code + verifier for tokens
In plain words The app hands over the ticket and the original note: if they match, it gets the keys.
Server to server, the app sends the code and the original code_verifier. The IdP hashes the verifier and compares it with the challenge from step 2. A stolen code does not pass this check.
POST https://idp.example.com/token grant_type=authorization_code &code=SplxlOBeZQQYbYS6WxSbIA &redirect_uri=https://app.example.com/callback &client_id=web-app &code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk SHA-256(code_verifier) == code_challenge ✓ - id_token + access_token
Step 6 of 7
Tokens issued
In plain words There are two keys: an ID card for the app (ID token) and a pass for the API (access token).
The ID token tells the app who the user is and how they signed in. The access token is for the API. The app validates the signature, iss, aud, exp and nonce before trusting anything.
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjIwMjYtMTAifQ.eyJpc3MiOiJodHRwczovL2lkcC5leGFtcGxlLmNvbSIsInN1YiI6IjI0ODI4OTc2MTAwMSIsImF1ZCI6IndlYi1hcHAiLCJleHAiOjE3NjAwMDM2MDAsImlhdCI6MTc2MDAwMDAwMCwiYXV0aF90aW1lIjoxNzU5OTk5OTk1LCJub25jZSI6Im4tMFM2X1d6QTJNaiIsImFtciI6WyJwd2QiLCJvdHAiLCJtZmEiXSwiYWNyIjoidXJuOmV4YW1wbGU6YWFsMiIsImVtYWlsIjoiYWRhQGV4YW1wbGUuY29tIn0.c2lnbmF0dXJlLW9mLWV4YW1wbGUID token claims
iss"https://idp.example.com"Who issued the token. Must be the IdP the app trusts.
sub"248289761001"Stable, unique user id at this IdP. Use it as the key, not the email.
aud"web-app"Who the token is for. The app rejects tokens issued to someone else.
exp1760003600Expiry as a Unix timestamp. After this, the token is worthless.
iat1760000000When it was issued.
auth_time1759999995When the user actually authenticated.
nonce"n-0S6_WzA2Mj"Must match the value sent in step 2: blocks replayed tokens.
amr["pwd","otp","mfa"]Authentication methods used. Changes with the sign-in method above.
acr"urn:example:aal2"Assurance level reached. Apps can demand a higher one for risky actions.
email"ada@example.com"A profile claim, released because the scope included email.
- GET /me · Bearer
Step 7 of 7
Calling the API
In plain words With the pass, the app asks the API for your data, and the API checks the pass every time.
The API only receives the access token. It checks signature, audience, expiry and scopes on every call, and never sees the password.
GET https://api.example.com/me Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.eyJzY29wZSI6Im9wZW5pZCBwcm9maWxlIGVtYWlsIn0.…
Flow complete.
Example domains and values. Nothing leaves your browser.