OAUTH 2.0 · LAB
Read what was really registered and handle each registration error
Compare a registration response with its request field by field, plan for a secret that expires, and trigger each RFC 7591 error. Today, compare the portal's secret handling and validation.
PlannedUses your lab tenant
The lesson
Builds on: Registering a client dynamically.
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
- G10 Client secret rotation overlap
Setup
Keep the shell variables and, once G9 exists, the
IATtoken and theREGresponse from Register a review-site client through the registration API.Planned (G9): in OAuth > Registration, set the client secret lifetime for registered clients to 30 days and keep the refresh grant off for registered clients.
Planned walkthrough
Compare what you asked for with what was registered.
diff <(jq -S '{grant_types, response_types, scope}' <<<"$REG") \
<(echo '{"grant_types":["authorization_code","refresh_token"],"scope":"photos.read"}' | jq -S .)
refresh_token was dropped and response_types: ["code"] was added.
Why it matters: "Checking what was registered". The response is the record of what was registered, not an echo of the request.
Read the two timestamps.
for t in client_id_issued_at client_secret_expires_at; do date -u -d @"$(jq -r .$t <<<"$REG")"; done # macOS: date -u -r
They are thirty days apart.
Why it matters: "A successful registration". An expiring secret needs a plan before that date: register again, or update the registration where the tenant allows it.
Decide whether the client can still do its job: it can sign in and read photos, but cannot refresh. Write that decision in your pipeline notes, or report it for a person to decide.
Why it matters: a client must read the response and act on differences, rather than fail later in a way that looks unrelated.
Do today
In OAuth > Clients, create
lab-tmp-review-482with the Web application preset: redirect URIhttp://127.0.0.1:8765/review-482/callback, scopephotos.read. Note what you receive: the client ID, and the secret shown once. There is no expiry; tenant secrets last until rotated. Store the secret:read -rs REVIEW_SECRET; export REVIEW_SECRET REVIEW_ID=<its client_id>.Make sure the client works, then rotate its secret in the portal and try the old one.
curl -s -u "$REVIEW_ID:$REVIEW_SECRET" "$ISSUER/oauth/token" -d grant_type=refresh_token -d refresh_token=demo | jq .error
Before rotating, the answer is invalid_grant (or unauthorized_client if the client lacks the refresh grant): either way the client authenticated, and only the request was wrong. After rotating, the same command gives invalid_client, and Audit shows oauth.token rejected invalid_client. The old secret stopped at once, with no warning date. Compare that with an expiry announced in a registration response.
Trigger the portal's own validation and compare each message with the RFC 7591 codes:
The redirect URI
http://review-482.test.printer.example/cb: refused, because a remote address must use HTTPS. A registration endpoint would answerinvalid_redirect_uri.A redirect URI with a fragment, such as
http://127.0.0.1:8765/cb#x: refused. Same code.A redirect URI whose query uses a response parameter name, such as
http://127.0.0.1:8765/cb?code=1: refused. Same code.A grant the Flow policy does not allow: the portal says to allow it in Flow policy first. A registration endpoint would answer
invalid_client_metadataor substitute a value.
Write down which of the four RFC 7591 codes the portal has no equivalent for: the two software statement errors, because the portal has no statements.
Break it
These run once G9 exists. Each refusal is a 400 with an error code, except the last.
redirect_uris: ["http://review-482.test.printer.example/cb"]:invalid_redirect_uri.grant_types: ["authorization_code"]withresponse_types: ["token"]:invalid_client_metadata.software_statement: "not-a-jwt":invalid_software_statement.No
Authorizationheader in Protected mode:401with aWWW-Authenticate: Bearerchallenge.
None of these is cured by sending the same request again. Stop and report the code.
Check your work
Today, Audit shows tenant.oauth.clients.create, tenant.oauth.credentials.rotate and oauth.token rejected invalid_client for lab-tmp-review-482. Once G9 exists, Audit also shows oauth.register refusals with each error code as the reason.
Cleanup
Delete
lab-tmp-review-482in OAuth > Clients andunset REVIEW_SECRET.Once G9 exists, delete the registered review client or keep it for the registration management labs.
Missing infrastructure
G9: the registration endpoint and its error responses, and secret expiry for registered clients.
G10: a rotation overlap window, so a pipeline can renew a secret before
client_secret_expires_atwithout downtime.Once these exist, the Planned walkthrough runs as written.