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.
- G19 `claims` request parameter
- G21 Group and custom-attribute token claims; directory roles fixed to `member`
- G61 Per-scope or per-claim consent choices
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.
Sign in to start this lab and check your progress. Log in or create an account.
A scope the tenant does not define is refused
Recorded as
oauth.authorizerejected (invalid_scope) forlab-collage.Send lab-collage's profile claims to UserInfo only
Recorded as
tenant.oauth.id_token_managers.updatesucceeded.Collect the claims from UserInfo
Recorded as
oidc.userinfosucceeded (userinfo_served) forlab-collage.Decline at the consent page and handle it
Recorded as
oauth.authorizerejected (access_denied) forlab-collageabout[email protected].Restore both destinations
Recorded as
tenant.oauth.id_token_managers.updatesucceeded.
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
You need
lab-collagewith itslab-collageID token manager (default mappings), Ava,rp_discoverand the redefined helpers from the provider discovery lab, andaccount.jsonfrom the previous lab. Runrp_discover "$ISSUER"and keepbtl-lab callbackrunning.Press Start.
Walkthrough
Build the scope table. Sign in four times, with
openid,openid%20profile,openid%20emailandopenid%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| Scope | Claims released here |
|---|---|
profile | name, given_name, family_name |
email | email, 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.
Ask for a scope the tenant does not define:
signin_url 'openid%20address'. The callback carrieserror=invalid_scope, because Lab Photos defines noaddressscope and holds no address to release.
Move the claims where the code flow expects them. On the
lab-collageID token manager, set the five standard mappings (name,given_name,family_name,email,email_verified) to the UserInfo destination only, and save. Sign in withopenid%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.
The
claimsparameter. Ask for individual claims, with the scope shrunk toopenid:
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.
Missing claims. Sign in with
openid%20emailonly. There is nogiven_name. Write your two rules:Greeting: "Welcome" with no name when
given_nameis absent, null or empty.Receipts: use
emailonly whenemail_verifiedis exactlytrue; otherwise ask Ava for an address and verify it yourself. Admin-created Ava hasfalse, so your rule asks, and a missingemail_verifiednever counts astrue.
More than you need. Sign in with
openid%20profile%20email:nameandfamily_namearrive although the collage app uses onlygiven_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.
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.1Discovery 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.
At consent, untick the email scope while allowing the rest. The sign-in succeeds, and
emailandemail_verifiedare missing from UserInfo, exactly the lesson's "When claims are missing" response. Your rules from step 5 keep the sign-in working.
Break it
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 handleerror=access_deniedas in the authentication errors lab.
A handler that fails the sign-in whenever
given_nameis 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
On the
lab-collageID token manager, set the five standard mappings back to both destinations, ID token and UserInfo.Run
unset CLAIMS.
Missing infrastructure
G19 (
claimsrequest parameter): the parameter is ignored, so there is noessential,valueorvalues, and noid_tokenmember. Planned step 1 will request claims individually and placeemailin the ID token.G21 (group and custom-attribute claims): there are no claims behind
addressorphone, and nopicture. Step 1 will then showprofilereleasing 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.