Beta

Create a tenant

A new tenant starts with its own users, OAuth settings, audit history and logs. You are its first Tenant Admin.

BTL Admin

OPENID CONNECT · LAB

Produce the four kinds of validation failure and read them from your logs

Feed lab-collage real tokens that are wrong for it and real configuration mistakes, diagnose each from a structured rejection log, and see why each tempting quick fix lets the wrong token in.

Partly readyIncludes a simulationUses both lab tenants

The lesson

Builds on: Signing keys and rotation.

New to the labs? Start with the lab toolkit and the shared cast and names every lab uses.

Partly ready. Most of this lab runs today. Steps that wait on platform features are marked, and Missing infrastructure says what they need.

Needs a second tenant. This lab also uses Lab Mail, a second tenant. Additional tenants currently need a paid subscription or a BTL grant, so you may not be able to do the Lab Mail steps yet (gap G66).

Your progress

Press Start before you begin. Only events your tenant records after that count, in the order below. Checking reads your tenant's Audit, so you need Audit read access in it.

  1. Obtain a genuine ID token issued to the printer

    Recorded as oauth.token succeeded for lab-printer about [email protected].

  2. Move lab-collage's ID tokens to an ES256 key

    Recorded as tenant.oauth.id_token_managers.update succeeded.

  3. Move them back to the RS256 key

    Recorded as tenant.oauth.id_token_managers.update succeeded.

  4. Shorten the ID token lifetime to 60 seconds

    Recorded as tenant.oauth.id_token_managers.update succeeded.

  5. Receive a token you will let expire

    Recorded as oauth.token succeeded for lab-collage about [email protected].

  6. Restore the 300-second lifetime

    Recorded as tenant.oauth.id_token_managers.update succeeded.

Setup

This lab adds the second lab tenant, which the Discovery and Claims labs reuse.

  1. Create a second tenant from the tenant switcher and name it Lab Mail. Additional tenants currently need a paid subscription or a BTL grant (G66). It plays a different provider, the lesson's "another provider".

  2. In Lab Mail, create the user [email protected] (Ava Archer) with a password, and the client lab-mail-collage from the Web preset with the redirect URI https://beyondthelogin.dev/lab/callback/. Store its values:

export ISSUER2='https://<your Lab Mail issuer from Overview>'
export MAIL_CLIENT_ID='<lab-mail-collage client ID>'; read -rs MAIL_CLIENT_SECRET; export MAIL_CLIENT_SECRET
  1. You also need lab-collage (ID token manager on key B), lab-printer, Ava in Lab Photos, the helpers from the authentication request lab, EXPECTED_ISSUER, EXPECTED_AUD, ID_ALGS and JWKS_CACHE. Keep btl-lab callback running.

  2. Send every rejection to a structured log line, in the format the lesson describes:

export REJECTIONS="$HOME/oidc-rejections.jsonl"
vcheck() { btl-lab verify "$1" --issuer "$EXPECTED_ISSUER" --audience "${AUD-$EXPECTED_AUD}" --algs "${ALGS:-$ID_ALGS}" --type id --nonce "$2" --jwks-cache "$JWKS_CACHE" --log "$REJECTIONS" "${@:3}"; }
  1. In Lab Photos, press Start.

Walkthrough

  1. Attack-shaped: a token from a provider you never configured. Sign in to Lab Mail through lab-mail-collage:

eval "$(btl-lab pkce)"; eval "$(btl-lab state)"; MAIL_NONCE=$NONCE
echo "$ISSUER2/oauth/authorize?response_type=code&client_id=$MAIL_CLIENT_ID&redirect_uri=https%3A%2F%2Fbeyondthelogin.dev%2Flab%2Fcallback%2F&scope=openid&state=$STATE&nonce=$NONCE&code_challenge=$CHALLENGE&code_challenge_method=S256"

Sign in as the Lab Mail Ava. The hosted callback page shows code, state and iss. Redeem the code at Lab Mail and keep the ID token:

MAIL_IDT=$(curl -s -u "$MAIL_CLIENT_ID:$MAIL_CLIENT_SECRET" "$ISSUER2/oauth/token" -d grant_type=authorization_code --data-urlencode "code=<code>" --data-urlencode "redirect_uri=https://beyondthelogin.dev/lab/callback/" --data-urlencode "code_verifier=$VERIFIER" | jq -r .id_token)
vcheck "$MAIL_IDT" "$MAIL_NONCE"

The verifier fetches your Lab Photos key set once, finds no key with that kid, and stops at the key check.

Why it matters: a genuine token from a provider you never configured fails before any claim is read.

  1. The same token under a configuration mistake: a key set address from the wrong provider.

btl-lab verify "$MAIL_IDT" --issuer "$EXPECTED_ISSUER" --audience "$EXPECTED_AUD" --algs "$ID_ALGS" --type id --nonce "$MAIL_NONCE" --jwks-uri "$ISSUER2/oauth/jwks" --log "$REJECTIONS"

Now the signature passes, and the issuer check refuses it.

Why it matters: a verified signature identifies the issuer only when the key came from the expected issuer's own set. The exact iss comparison is the backstop.

  1. A genuine token issued to another client, and a token from another attempt. Run a lab-printer sign-in with scope=openid in Lab Photos, keep PRINTER_IDT and PRINTER_NONCE, then run vcheck "$PRINTER_IDT" "$PRINTER_NONCE": it stops at the audience check. Run a fresh lab-collage sign-in and check its token against an older attempt's nonce: vcheck "$ID_TOKEN" "$PRINTER_NONCE" stops at the nonce check.

Why it matters: these are the scattered failures at the audience and nonce checks that the lesson asks you to treat as possible attacks, even when rare.

  1. Configuration mismatch, the lesson's Tuesday release. The upgraded library lost its expected audience. Reproduce it on three fresh, valid lab-collage tokens:

AUD= vcheck "$ID_TOKEN" "$NONCE"

Every run stops at the audience check, and each log line shows "expected_audience": null.

Why it matters: every sign-in failing at the same check, starting right after a change, points at your own configuration, and the expected values in the log are the whole answer.

  1. Configuration mismatch at the provider. Edit the lab-collage ID token manager to sign with ES256 key D. Sign in and run vcheck "$ID_TOKEN" "$NONCE": it stops at the algorithm check. The discovery document now lists ES256 in id_token_signing_alg_values_supported. Decide the fix on purpose: either both sides move to ES256 together, or the provider goes back.

Restore: edit the lab-collage ID token manager to sign with key B again.

  1. Expiry. Set the lab-collage ID token manager's lifetime to 60 seconds, sign in, wait two minutes, and run vcheck "$ID_TOKEN" "$NONCE". It stops at the expiry check, and the log shows a token_age_seconds of about 120.

Restore: set the lifetime back to 300 seconds.

  1. Clock drift.

Simulation. you cannot change the tenant's clock, so you shift your verifier's clock instead. The token is exactly as the tenant issued it.

Run vcheck "$ID_TOKEN" "$NONCE" --clock-offset -600 on a fresh token. It stops at the issued-at check with a negative token_age_seconds. Without the option the same token passes. In production that pattern follows one server.

  1. Key set unavailable. Start with no cached set and a key set address that does not exist on the tenant:

rm -f "$JWKS_CACHE"
btl-lab verify "$ID_TOKEN" --issuer "$EXPECTED_ISSUER" --audience "$EXPECTED_AUD" --algs "$ID_ALGS" --type id --nonce "$NONCE" --jwks-cache "$JWKS_CACHE" --jwks-uri "$ISSUER/oauth/jwks-missing" --log "$REJECTIONS"

The run stops because the key set is unavailable. Now run vcheck "$ID_TOKEN" "$NONCE" to fill the cache, and repeat the command above. It accepts, because the kid is already in a set you fetched recently.

Why it matters: this is the decision the lesson asks you to make in advance. Keep validating with a recent copy when the kid is known, and fail when you cannot verify.

  1. Read the pattern from your logs:

jq -s 'group_by(.check) | map({check: .[0].check, count: length, expected_audience: (map(.expected_audience) | unique)})' "$REJECTIONS"

Confirm that no line holds a token, a code, a nonce, a name or an email address.

  1. What the person sees. Write the page for every one of these failures: one message, a "try again" link that starts a fresh attempt, and the reference (your correlation ID). It never says which check failed and never shows a name from the rejected token.

  1. Compare with the provider's view. Lab Photos Audit shows oauth.token succeeded for every sign-in you rejected. Relying-party validation failures exist only in the relying party's own logs.

Break it

Each tempting fix in the lesson's table makes your relying party accept a real token it should refuse. Try each once, then remove it:

  1. Trust any audience: vcheck "$PRINTER_IDT" "$PRINTER_NONCE" --trusted-audience "$PRINTER_ID". The printer's token now passes. Fix it instead by setting the expected audience correctly.

Restore: run vcheck without --trusted-audience from now on.

  1. Widen the clock tolerance: run the expired token from step 6 with --leeway 3600 --max-iat-age 3600. It passes the time checks. Fix the clock instead.

Restore: use the default leeway and issued-at window again.

  1. Accept whatever the provider lists: ALGS=RS256,ES256 would have hidden the algorithm change in step 5. Change the registration and the allowlist together, on purpose.

Restore: run unset ALGS, so ID_ALGS (RS256) applies again.

Check your work

Press Check my progress in Lab Photos. It looks for, in order: oauth.token succeeded for lab-printer, the move of the lab-collage ID token manager to key D and back, the lifetime change to 60 seconds, a lab-collage token request, and the lifetime change back to 300 seconds.

$REJECTIONS should hold at least one line each for the key, issuer, audience, nonce, algorithm, expiry, issued-at and key set unavailable checks. In Lab Mail, Audit shows oauth.token succeeded for lab-mail-collage.

Cleanup

  1. Confirm that the lab-collage ID token manager signs with key B and has a 300-second lifetime.

  2. Keep Lab Mail, ISSUER2, MAIL_CLIENT_ID, MAIL_CLIENT_SECRET and $REJECTIONS for the Discovery labs. Run unset PRINTER_IDT PRINTER_NONCE AUD ALGS.

Missing infrastructure

  • G66 Second lab tenant: this lab uses Lab Mail, a second tenant. Additional tenants currently need a paid subscription or a BTL grant, so an ordinary learner can do only the Lab Photos steps until every learner can have a second lab tenant.

Back to all labs

We value your privacy

We use cookies and similar technologies to enhance your browsing experience, and analytics to understand our traffic. By clicking "Allow All", you consent to optional analytics. Cookie Policy

The Lab