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

Fetch and validate a claim at another endpoint

Plan fetching and validating a signed claim JWT from a reference, and today build a fetcher with an endpoint allowlist, timeout and size limit, call a real protected endpoint, and meet the lesson's failure rows for real.

PlannedUses both lab tenants

The lesson

Builds on: Locating claims at another source.

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

Planned. The core of this lab waits on platform features that are not built yet. The planned walkthrough shows exactly how it will run; Do today is a real exercise you can do now.

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

Setup

The fetch discipline can be practised today against Lab Mail's real UserInfo endpoint, standing in for a claims endpoint. Signed claim JWTs from an endpoint need G63 and a claims endpoint (G3).

  1. Add the endpoints you have agreed to call to Lab Mail's entry in claims-providers.json. Exact addresses, HTTPS only:

jq --arg mail "$ISSUER2" '.[$mail].endpoints = [$mail + "/oidc/userinfo"]' claims-providers.json > c.tmp && mv c.tmp claims-providers.json
  1. In Lab Mail, open the access token manager assigned to lab-mail-collage, note its lifetime, and set it to 60 seconds.

  2. Save the fetcher. It checks the address before sending anything, then calls with a 3-second timeout and a 16 KB limit, requires application/jwt, and hands a JWT to the validator from Validate an embedded claims-provider JWT. Its log line has the stage, the host, the status, the result and the check, never the token.

fetch_claim() {
  local endpoint=$1 token=$2 host resp meta status type
  host=$(node -e 'try { console.log(new URL(process.argv[1]).host) } catch { console.log("invalid") }' "$endpoint")
  say() { echo "{\"stage\":\"distributed_claim\",\"host\":\"$host\",\"status\":\"${status:-none}\",\"result\":\"$1\",\"check\":\"$2\"}"; }
  if [[ "$endpoint" != https://* ]] || ! jq -e --arg e "$endpoint" 'any(.[]; (.endpoints // []) | index($e))' claims-providers.json > /dev/null; then
    say refused endpoint_not_allowed; return 1; fi
  resp=$(curl -s --max-time 3 --max-filesize 16384 -H "Authorization: Bearer $token" -w '\n%{http_code} %{content_type}' "$endpoint")
  meta=$(tail -n 1 <<<"$resp"); status=${meta%% *}; type=${meta#* }
  case "$status" in
    200) [[ "$type" == application/jwt* ]] || { say rejected not_a_jwt; return 1; }
         node aggregated-check.mjs "$(sed '$d' <<<"$resp")" lab_enrolled ;;
    401) say missing ask_again_through_provider; return 1 ;;
    *) say missing unavailable_try_later; return 1 ;;
  esac
}

Planned walkthrough

  1. Take the reference from Lab Photos' UserInfo. Check its endpoint against the allowlist, exactly and over HTTPS, before sending anything.

  2. Fetch it with fetch_claim "<endpoint>" "<access token from the reference>". Lab Mail answers 200 with Content-Type: application/jwt.

  3. The validator checks issuer, key, signature, times and audience exactly as for an aggregated statement, then reads lab_enrolled. Apply the discount and discard the access token.

Why it matters: the HTTPS connection shows which server answered while it lasted. The signature shows what Lab Mail stated, after the connection has closed.

Do today

  1. A reference to an address you never agreed to call:

fetch_claim "http://127.0.0.1:8765/claims" "unused"

refused endpoint_not_allowed, before any request: btl-lab callback, if running, prints nothing.

Why it matters: checking the address first protects your internal network and keeps the credential away from servers you did not choose.

  1. Get a fresh MAIL_TOKEN (sign in to lab-mail-collage as Ava Lin, as in the previous lab), then fetch from the allowed endpoint at once:

fetch_claim "$ISSUER2/oidc/userinfo" "$MAIL_TOKEN"

Lab Mail answers 200 with application/json, and the fetcher reports rejected not_a_jwt.

Why it matters: a plain JSON body proves only who answered over this connection, not what the claims provider stated, so a claims endpoint must answer with a JWT.

  1. Wait 70 seconds and fetch again: status 401 (Lab Mail sends WWW-Authenticate: Bearer error="invalid_token"), and the fetcher reports missing ask_again_through_provider without retrying. Lab Mail's Audit shows oidc.userinfo rejected.

Why it matters: with no refresh token for the other source, an expired reference means asking the person again through their provider, not hammering the endpoint.

  1. Read the three log lines: stage, host, status, result and check. No token, no body.

  2. Note in your relying party's design when it would fetch: once, when Ava chooses the discount, never at every sign-in or page.

Why it matters: every fetch tells the claims provider that you asked about this person, and when.

Break it

Planned, once G63 exists: take Lab Mail's claims endpoint down (or point the allowlist at a missing path). The fetch times out or fails, the fetcher reports unavailable_try_later, and the order continues without the discount. Nothing signs the person out.

Check your work

Today: one endpoint_not_allowed, one not_a_jwt and one ask_again_through_provider line, and Lab Mail's Audit showing oidc.userinfo served once and then rejected for lab-mail-collage.

Once G63 exists, Lab Mail's Audit shows one claims read per checkout, not one per page.

Cleanup

  1. Restore the access token lifetime on Lab Mail's manager.

  2. unset MAIL_TOKEN. Keep claims-providers.json and the scripts if you want them, or delete them.

Missing infrastructure

  • G63 (aggregated and distributed claims). References in Lab Photos' responses and claim-scoped Lab Mail tokens.

  • G3 (sample protected resource API). A claims endpoint that returns the claim as a signed application/jwt, so the fetcher's accept path can complete.

  • 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