Antonio Braccolino, Identity & Access, home

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.

You completed every lab

All 16 labs, from the map of IAM to the reference architectures. Identity has no secrets left for you.

See your achievements

Share your badge

Instagram: download the image and post it as a post or story. LinkedIn: the text is copied when you open it, paste it and attach the image.

OIDC · OAuth 2.0 · PKCE · Basics · 7 steps

What you will learn

  • Why the app never sees the password
  • How PKCE makes a stolen code useless
  • What is inside an ID token
How the user signs in

Step 3: password, then a one-time code. In the token: amr pwd + otp, acr aal2.Step 3: the device signs a challenge, nothing to type. In the token: amr hwk + user, acr aal3.

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
  1. 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).

  2. 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
  3. 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.

  4. 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
  5. 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   ✓
  6. 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.c2lnbmF0dXJlLW9mLWV4YW1wbGU

    ID 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.

    • exp1760003600

      Expiry as a Unix timestamp. After this, the token is worthless.

    • iat1760000000

      When it was issued.

    • auth_time1759999995

      When 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.

  7. 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.…

Example domains and values. Nothing leaves your browser.