OPENID CONNECT · LAB
Map OpenID Connect registration settings onto lab-collage
Write lab-collage's registration with the standard names, find where each setting lives in your tenant or confirm it is missing, and break sign-in by making the registration and the relying party disagree.
Partly readyUses your lab tenant
The lesson
Builds on: Reading provider metadata.
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.
- G9 Dynamic client registration and registration management
- G17 OIDC logout: RP-initiated, front-channel, back-channel, session management
- G18 Pairwise subject identifiers
- G20 `acr` / `acr_values` and acr-driven step-up
- G60 Per-client OIDC registration defaults (`default_max_age`, `application_type`, `initiate_login_uri`, `require_auth_time`)
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.
Sign in without being asked anything, and find auth_time
Recorded as
oauth.authorizesucceeded (code_issued) forlab-collageabout[email protected].Restrict lab-collage to assigned exclusive scopes
Recorded as
tenant.oauth.clients.updatesucceeded.The tenant refuses openid for the restricted client
Recorded as
oauth.authorizerejected (invalid_scope) forlab-collage.Allow common and assigned scopes again
Recorded as
tenant.oauth.clients.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 on key B, Ava,ID_ALGS, andrp_discoverwith the redefined helpers from the provider discovery lab. Runrp_discover "$ISSUER"and keepbtl-lab callbackrunning.Press Start.
Walkthrough
Write
registration.jsonforlab-collagewith the lesson's standard names, filled from the client screen and the ID token manager:
jq -n --arg id "$CLIENT_ID" '{client_id: $id,
redirect_uris: ["http://127.0.0.1:8765/callback", "https://beyondthelogin.dev/lab/callback/"],
token_endpoint_auth_method: "client_secret_basic", scope: "openid profile email photos.read", application_type: "web",
id_token_signed_response_alg: "RS256", subject_type: "public", require_auth_time: true, default_max_age: 3600,
post_logout_redirect_uris: ["https://beyondthelogin.dev/lab/callback/"]}' > registration.json
Mark each field as configurable here, fixed, or missing:
| Setting | In your tenant |
|---|---|
redirect_uris | Client screen, matched exactly. Loopback addresses match on any port, the path never. |
token_endpoint_auth_method | Fixed to client_secret_basic for confidential clients. |
scope | Client screen: common plus assigned scopes, or assigned exclusive scopes only. |
id_token_signed_response_alg | Set by the signing key of the assigned ID token manager (key B, RS256). |
subject_type | Public only. |
require_auth_time | Always in effect: auth_time is a protected claim in every ID token. |
application_type, default_max_age, default_acr_values, initiate_login_uri | Missing. Send max_age with each request instead. |
| Logout addresses | Missing. |
Why it matters: the names are the ones a client uses when it registers through an API. Knowing which are real here tells you which decisions your relying party must still enforce itself.
Prove the
require_auth_timebehavior. In a window where Ava is signed in, runsignin_url 'openid'andexchange. The tenant asks nothing, andbtl-lab decode "$ID_TOKEN"still showsauth_time, the time of her earlier sign-in.
Try registering the way the lesson's API would:
curl -si -X POST "$ISSUER/oauth/register" -H 'Content-Type: application/json' -d @registration.json | head -3
The tenant answers 501: dynamic registration is not available, so registrations are made in the tenant portal.
Exact return addresses.
lab-collagealready has two registered return addresses, the loopback listener and the hosted callback page. Runsignin_url 'openid'and replace the encodedredirect_uriwithhttps%3A%2F%2Fbeyondthelogin.dev%2Flab%2Fcallback%2F. The hosted page shows the code, because that address is registered exactly. Now remove the final%2Fand open it again: the tenant shows its own error page and never redirects.
Why it matters: the lesson keeps sign-in on its own return address. Exact matching is what makes that separation enforceable.
Algorithm agreement. Your
ID_ALGSand the manager's key describe the same decision from two sides. In the validation failure lab, moving the manager to ES256 key D changed the effectiveid_token_signed_response_algwithout changing your allowlist, and every sign-in failed at the algorithm check until both sides agreed. Note inregistration.jsonthat the two must change together.
Planned walkthrough
These steps run once dynamic registration (G9), per-client OpenID Connect defaults (G60), pairwise subjects (G18), authentication context (G20) and logout addresses (G17) exist.
Register through the API:
POST$ISSUER/oauth/register
Open in console
POST $ISSUER/oauth/register HTTP/1.1
Content-Type: application/json
{"redirect_uris": ["https://beyondthelogin.dev/lab/callback/"], "client_name": "lab-tmp-registered", "token_endpoint_auth_method": "client_secret_basic", "scope": "openid profile", "id_token_signed_response_alg": "RS256", "subject_type": "public", "require_auth_time": true, "default_max_age": 300}The tenant answers 201 with client_id, client_secret, a registration_access_token and a registration_client_uri. Read the registration back with the access token, change default_max_age, and finally delete lab-tmp-registered.
Set
default_max_ageto 300 onlab-collage. Sign in more than five minutes after Ava's last password sign-in, with nomax_agein the request: the tenant asks for her password andauth_timeis fresh. Sendmax_age=3600in a request and see it override the default.Set
default_acr_valuesand check theacrin the token, as the authentication context lessons describe.Register
post_logout_redirect_urisand the logout notification addresses, and use them in the logout labs.
Break it
A registration without
openid. Onlab-collage, set scopes to Assigned exclusive scopes only.openidis a common scope, so the client can no longer request it: the nextsignin_url 'openid'returnserror=invalid_scope. Changing this setting also ends the client's existing tokens and consents.
Restore: set lab-collage scopes back to Common and assigned scopes, and run a sign-in to confirm openid works again (Ava approves consent once more).
A return address with one extra character. Run
signin_url 'openid'with the encodedredirect_urichanged tohttp%3A%2F%2F127.0.0.1%3A8765%2Fcallback%2F. The tenant shows its error page, because a trailing slash makes it a different address.
Check your work
Press Check my progress. It looks for, in order: a code_issued sign-in for lab-collage, a tenant.oauth.clients.update (scopes restricted), oauth.authorize rejected with invalid_scope, and a second tenant.oauth.clients.update (scopes restored).
In Logs, oauth.register appears with not_implemented, and the unregistered return addresses appear as oauth.authorize rejected with invalid_redirect_uri. registration.json should be annotated for every field.
Cleanup
Confirm that lab-collage allows common and assigned scopes and still has exactly its two lab return addresses. Delete registration.json when you no longer need it.
Missing infrastructure
G9 (dynamic client registration and management):
/oauth/registerreturns501. Planned step 1 will registerlab-tmp-registeredthrough the API, read and update it with the registration access token, and delete it.G60 (per-client OpenID Connect registration defaults): there is no per-client
default_max_age,require_auth_timeswitch (auth_timeis always issued),application_typeorinitiate_login_uri. Planned step 2 will show the default forcing a password after five minutes and a per-requestmax_ageoverriding it.G18 (pairwise subject identifiers):
subject_type: pairwiseandsector_identifier_uriare not offered. The claims labs approximate a per-sectorsubuntil it exists.G20 (
acrandacr_values): there is nodefault_acr_values, andacr_valuesis not supported. Planned step 3 will request and check an authentication context.G17 (OpenID Connect logout): there are no
post_logout_redirect_uris,frontchannel_logout_uriorbackchannel_logout_uri. Planned step 4 will register them for the logout labs.