OAUTH 2.0 · LAB
Register a review-site client through the registration API
Act as the printer's deployment pipeline: register a client with an initial access token, compare open registration, and today create the same client in the portal to see what an API would replace.
PlannedUses your lab tenant
The lesson
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.
- G9 Dynamic client registration and registration management
- G56 Client JWKS (`jwks` / `jwks_uri` per client)
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
Set the shell variables for your lab tenant (
export ISSUER=...) and startbtl-lab callbackin a second terminal when a step signs in.Planned (G9): open OAuth > Registration and set the mode to Protected (proposed options Off, Protected, Open; default Off). Set the defaults for registered clients: consent mode always, a "Registered through the API" label on the consent screen, allowed scopes
photos.read, and no refresh grant.Planned (G9): on the same screen, create an initial access token named
review-pipeline, expiring in 7 days, with the constraint "redirect URIs must start withhttp://127.0.0.1:8765/". It is shown once, so read it without echoing:read -rs IAT; export IAT.
Planned walkthrough
Find the endpoint in metadata.
GET$ISSUER/.well-known/oauth-authorization-server
Open in console
GET $ISSUER/.well-known/oauth-authorization-serverregistration_endpoint is $ISSUER/oauth/register.
Why it matters: "When a form is not enough". Software finds the endpoint in metadata, with no console visit.
Register review site 482 as the pipeline would.
REG=$(curl -s "$ISSUER/oauth/register" -H "Authorization: Bearer $IAT" -H 'Content-Type: application/json' -d '{
"client_name": "Photo Printer review 482",
"redirect_uris": ["http://127.0.0.1:8765/review-482/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"token_endpoint_auth_method": "client_secret_basic",
"scope": "photos.read"}')
jq 'del(.client_secret, .registration_access_token)' <<<"$REG"
The response stays in the shell variable REG, never in a file. Expect 201 with a tenant-chosen client_id, a client_secret and the registered metadata. The jq filter leaves the two credentials out of your screen.
Why it matters: "Sending a registration request". The body is JSON client metadata, and protected registration tells the tenant which token, and so which administrator, stands behind this client.
Open OAuth > Clients. The new client is listed with its origin, "Registered with initial access token review-pipeline". Audit shows
oauth.registersucceeded with the token's creator as the accountable person.
Why it matters: registrations stay traceable to whoever issued the token, and revoking that token stops new registrations without touching existing clients.
Run a code flow with the new client. The consent screen shows "Photo Printer review 482" with the "Registered through the API" label.
Why it matters: the tenant treats a self-described name with caution rather than as proof.
Switch the mode to Open for this step only, and register an installation with no credential and
token_endpoint_auth_methodnone. Each call returns a newclient_id. With G56, an installation could instead send its public key by value injwksand useprivate_key_jwt.
Restore: set the registration mode back to Protected.
Why it matters: "One client per installation". Each registration can be disabled alone, but nothing in it proves which software sent it.
Do today
Call the registration endpoint.
curl -si "$ISSUER/oauth/register" -H 'Content-Type: application/json' -d '{"client_name":"x"}'
The answer is 501 temporarily_unavailable, and Logs show oauth.register rejected not_implemented.
Read the metadata request from Planned walkthrough step 1 against your tenant. There is no
registration_endpoint, andbtl_endpoint_status["/oauth/register"]is"not_implemented".Do the pipeline's job by hand. In OAuth > Clients, create
lab-tmp-review-482with the Web application preset, using the step 2 values: redirect URIhttp://127.0.0.1:8765/review-482/callback, grantsauthorization_codeandrefresh_token, scopephotos.read. Note which form fields map directly to RFC 7591 names (client_name,redirect_uris,grant_types,response_types,scope) and that the secret is shown once.Edit the client and try the redirect URI
http://review-482.test.printer.example/callback. The portal refuses it, because a remote address must use HTTPS. A registration endpoint applies the same rule.Open Audit:
tenant.oauth.clients.createnames you as the actor. A console registration always has an authenticated, accountable person behind it. Imagine doing steps 3 and 5 several times a day for every review site, which is the case the lesson describes.
Break it
These run once G9 exists.
Register with a redirect URI outside the token's constraint,
http://127.0.0.1:9999/x. Expect400 {"error":"invalid_redirect_uri"}.Revoke
review-pipelinein the portal and register again. Expect401withWWW-Authenticate: Bearer error="invalid_token". The client from step 2 keeps working: it is a separate record.
Check your work
Today, Logs show oauth.register rejected not_implemented, and Audit shows tenant.oauth.clients.create and, after Cleanup, tenant.oauth.clients.delete for lab-tmp-review-482. Once G9 exists, Audit also shows oauth.register succeeded twice, the two Break it refusals, and the initial access token's creation and revocation.
Cleanup
Delete
lab-tmp-review-482in OAuth > Clients.Once G9 exists, keep the registered review client and the
REGvariable (same shell) for the registration management labs, or delete the client; set the registration mode back to Off when you finish them.
Missing infrastructure
G9: the registration endpoint, a tenant registration policy (Off, Protected, Open, with Off as the default), initial access tokens with constraints and expiry, labelling of registered clients on the consent screen, attribution of each registration to its token's creator, and a registration rate limit registered as a visible tenant usage policy. Open mode must stay bounded per tenant.
G56: per-client public keys, so an installation can register a key by value and use
private_key_jwt.Once these exist, the Planned walkthrough runs as written.