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

Map scopes to the claims they release, and handle claims that never arrive

Build your tenant's scope-to-claim table, move profile claims out of the ID token into UserInfo, send a claims parameter the provider does not support, and keep signing people in when claims are missing.

Partly readyUses your lab tenant

The lesson

Builds on: Standard and custom claims.

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.

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. A scope the tenant does not define is refused

    Recorded as oauth.authorize rejected (invalid_scope) for lab-collage.

  2. Send lab-collage's profile claims to UserInfo only

    Recorded as tenant.oauth.id_token_managers.update succeeded.

  3. Collect the claims from UserInfo

    Recorded as oidc.userinfo succeeded (userinfo_served) for lab-collage.

  4. Decline at the consent page and handle it

    Recorded as oauth.authorize rejected (access_denied) for lab-collage about [email protected].

  5. Restore both destinations

    Recorded as tenant.oauth.id_token_managers.update succeeded.

Request console

Requests in this lab can be sent from this page to your tenant: open one and choose Send. Fill in the values below first. They stay in this page's memory and are gone when you leave; secrets are never stored or sent anywhere except the request you send.

Setup

  1. You need lab-collage with its lab-collage ID token manager (default mappings), Ava, rp_discover and the redefined helpers from the provider discovery lab, and account.json from the previous lab. Run rp_discover "$ISSUER" and keep btl-lab callback running.

  2. Press Start.

Walkthrough

  1. Build the scope table. Sign in four times, with openid, openid%20profile, openid%20email and openid%20profile%20email. For each, record the claims beyond the protocol ones in the ID token (btl-lab decode "$ID_TOKEN") and in UserInfo:

GET$ISSUER/oidc/userinfo Open in console
GET $ISSUER/oidc/userinfo HTTP/1.1
Authorization: Bearer $TOKEN
ScopeClaims released here
profilename, given_name, family_name
emailemail, email_verified

Why it matters: the lesson's fourteen-claim profile is the specification's maximum, not a promise. This tenant holds three name claims, so profile releases those and nothing more.

  1. Ask for a scope the tenant does not define: signin_url 'openid%20address'. The callback carries error=invalid_scope, because Lab Photos defines no address scope and holds no address to release.

  1. Move the claims where the code flow expects them. On the lab-collage ID token manager, set the five standard mappings (name, given_name, family_name, email, email_verified) to the UserInfo destination only, and save. Sign in with openid%20profile%20email. The ID token now carries only protocol claims, and UserInfo returns the name and email.

Why it matters: an ID token can travel further than a UserInfo response, for example when it is later sent back as a hint. Keeping personal data out of it is a placement choice.

  1. The claims parameter. Ask for individual claims, with the scope shrunk to openid:

CLAIMS=$(jq -rn '{userinfo: {given_name: null, email: {essential: true}, email_verified: {essential: true}}} | tojson | @uri')
signin_url 'openid' "&claims=$CLAIMS"

Sign in and exchange. The request succeeds, and UserInfo returns only sub. The discovery document says "claims_parameter_supported": false: this provider ignored the parameter and used the scopes alone.

Why it matters: whatever you ask for, you work with what actually arrives.

  1. Missing claims. Sign in with openid%20email only. There is no given_name. Write your two rules:

    • Greeting: "Welcome" with no name when given_name is absent, null or empty.

    • Receipts: use email only when email_verified is exactly true; otherwise ask Ava for an address and verify it yourself. Admin-created Ava has false, so your rule asks, and a missing email_verified never counts as true.

  1. More than you need. Sign in with openid%20profile%20email: name and family_name arrive although the collage app uses only given_name. Keep the allowlist from the previous lab:

curl -s "$USERINFO_ENDPOINT" -H "Authorization: Bearer $TOKEN" | jq '{given_name, email, email_verified}' > account.json

Planned walkthrough

These steps run once the claims parameter (G19), richer directory claims (G21) and per-claim consent (G61) exist.

  1. Request individual claims and their placement:

GET$ISSUER/oauth/authorize?response_type=code&client_id=$CLIENT_ID&redirect_uri=http%3A%2F%2F127.0.0.1%3A8765%2Fcallback&scope=openid&state=$STATE&nonce=$NONCE&code_challenge=$CHALLENGE&code_challenge_method=S256&claims=%7B%22userinfo%22%3A%7B%22given_name%22%3Anull%2C%22picture%22%3Anull%7D%2C%22id_token%22%3A%7B%22email%22%3A%7B%22essential%22%3Atrue%7D%7D%7D Open in console
GET $ISSUER/oauth/authorize?response_type=code&client_id=$CLIENT_ID&redirect_uri=http%3A%2F%2F127.0.0.1%3A8765%2Fcallback&scope=openid&state=$STATE&nonce=$NONCE&code_challenge=$CHALLENGE&code_challenge_method=S256&claims=%7B%22userinfo%22%3A%7B%22given_name%22%3Anull%2C%22picture%22%3Anull%7D%2C%22id_token%22%3A%7B%22email%22%3A%7B%22essential%22%3Atrue%7D%7D%7D HTTP/1.1

Discovery then says claims_parameter_supported: true. UserInfo returns given_name and picture, and the ID token carries email, with nothing from the rest of profile.

  1. At consent, untick the email scope while allowing the rest. The sign-in succeeds, and email and email_verified are missing from UserInfo, exactly the lesson's "When claims are missing" response. Your rules from step 5 keep the sign-in working.

Break it

  1. Try to decline only the email part. Run signin_url 'openid%20profile%20email' '&prompt=consent' so the consent page appears. It offers only Allow access and Deny access, so the closest real equivalent today is requesting fewer scopes. Select Deny access and handle error=access_denied as in the authentication errors lab.

  1. A handler that fails the sign-in whenever given_name is missing would punish a choice the provider offered. Repeat step 5 and confirm that your handler still starts a session.

Check your work

Press Check my progress. It looks for, in order: oauth.authorize rejected with invalid_scope, the destination change on the lab-collage ID token manager, oidc.userinfo with userinfo_served, oauth.authorize rejected with access_denied, and the destination restore from Cleanup.

Your scope table should match the observed responses.

Cleanup

  1. On the lab-collage ID token manager, set the five standard mappings back to both destinations, ID token and UserInfo.

  2. Run unset CLAIMS.

Missing infrastructure

  • G19 (claims request parameter): the parameter is ignored, so there is no essential, value or values, and no id_token member. Planned step 1 will request claims individually and place email in the ID token.

  • G21 (group and custom-attribute claims): there are no claims behind address or phone, and no picture. Step 1 will then show profile releasing more than the collage app uses.

  • G61 (per-scope or per-claim consent): the consent page is all or nothing. Planned step 2 will decline the email scope at consent and show the sign-in succeeding without the email claims.

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