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

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. Sign in without being asked anything, and find auth_time

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

  2. Restrict lab-collage to assigned exclusive scopes

    Recorded as tenant.oauth.clients.update succeeded.

  3. The tenant refuses openid for the restricted client

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

  4. Allow common and assigned scopes again

    Recorded as tenant.oauth.clients.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 on key B, Ava, ID_ALGS, and rp_discover with the redefined helpers from the provider discovery lab. Run rp_discover "$ISSUER" and keep btl-lab callback running.

  2. Press Start.

Walkthrough

  1. Write registration.json for lab-collage with 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
  1. Mark each field as configurable here, fixed, or missing:

SettingIn your tenant
redirect_urisClient screen, matched exactly. Loopback addresses match on any port, the path never.
token_endpoint_auth_methodFixed to client_secret_basic for confidential clients.
scopeClient screen: common plus assigned scopes, or assigned exclusive scopes only.
id_token_signed_response_algSet by the signing key of the assigned ID token manager (key B, RS256).
subject_typePublic only.
require_auth_timeAlways in effect: auth_time is a protected claim in every ID token.
application_type, default_max_age, default_acr_values, initiate_login_uriMissing. Send max_age with each request instead.
Logout addressesMissing.

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.

  1. Prove the require_auth_time behavior. In a window where Ava is signed in, run signin_url 'openid' and exchange. The tenant asks nothing, and btl-lab decode "$ID_TOKEN" still shows auth_time, the time of her earlier sign-in.

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

  1. Exact return addresses. lab-collage already has two registered return addresses, the loopback listener and the hosted callback page. Run signin_url 'openid' and replace the encoded redirect_uri with https%3A%2F%2Fbeyondthelogin.dev%2Flab%2Fcallback%2F. The hosted page shows the code, because that address is registered exactly. Now remove the final %2F and 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.

  1. Algorithm agreement. Your ID_ALGS and 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 effective id_token_signed_response_alg without changing your allowlist, and every sign-in failed at the algorithm check until both sides agreed. Note in registration.json that 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.

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

  1. Set default_max_age to 300 on lab-collage. Sign in more than five minutes after Ava's last password sign-in, with no max_age in the request: the tenant asks for her password and auth_time is fresh. Send max_age=3600 in a request and see it override the default.

  2. Set default_acr_values and check the acr in the token, as the authentication context lessons describe.

  3. Register post_logout_redirect_uris and the logout notification addresses, and use them in the logout labs.

Break it

  1. A registration without openid. On lab-collage, set scopes to Assigned exclusive scopes only. openid is a common scope, so the client can no longer request it: the next signin_url 'openid' returns error=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).

  1. A return address with one extra character. Run signin_url 'openid' with the encoded redirect_uri changed to http%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/register returns 501. Planned step 1 will register lab-tmp-registered through 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_time switch (auth_time is always issued), application_type or initiate_login_uri. Planned step 2 will show the default forcing a password after five minutes and a per-request max_age overriding it.

  • G18 (pairwise subject identifiers): subject_type: pairwise and sector_identifier_uri are not offered. The claims labs approximate a per-sector sub until it exists.

  • G20 (acr and acr_values): there is no default_acr_values, and acr_values is 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_uri or backchannel_logout_uri. Planned step 4 will register them for the logout labs.

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