OAUTH 2.0 · LAB
Handle denial, rejection and a lost response, then run the complete exchange
Tell an authorization denial from a token endpoint rejection, find a failed request by its request ID, see why resending a code is not recovery, and finish with the complete, fully checked exchange.
ReadyIncludes a simulationUses your lab tenant
The lesson
Builds on: Exchanging a code for tokens.
New to the labs? Start with the lab toolkit and the shared cast and names every lab uses.
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.
Ava denies the printer, and the denial is correlated
Recorded as
oauth.authorizerejected (access_denied) forlab-printerabout[email protected].The tenant's policy refuses a token Ben approved
Recorded as
oauth.tokenrejected (policy_denied) forlab-printerabout[email protected].A wrong client secret is refused
Recorded as
oauth.tokenrejected (invalid_client) forlab-printer.Resending a used code is refused and revokes its tokens
Recorded as
oauth.tokenrejected (code_replayed) forlab-printer.The complete, fully checked exchange succeeds
Recorded as
oauth.tokensucceeded forlab-printerabout[email protected].
Setup
In a fresh terminal, set the
lab-printervariables andAPI_IDandAPI_SECRETforlab-photo-api. Definestart_attempt,handle_callbackandredeemexactly as in the Correlating requests and responses lab.In Access Token Management, create a temporary manager
lab-tmp-deny-ben(Signed JWT, an active key) with this advanced issuance policy, and save it without assigning it.
return {allow: context.subject.email !== '[email protected]', claims: {}};
Press Start on this page.
Walkthrough
Denied at the authorization server. Run
start_attempt, sign in as Ava and choose Deny. The callback carrieserror=access_denied,error_description=The user denied the request.,stateandiss. Pass them tohandle_callback: it validatesstateandiss, then reports "not connected".
Why it matters: an error response does not bypass the transaction and issuer checks. No code was issued, so no token request follows, and the printer can say "The photo account was not connected. You can continue without it or try again."
Refused by policy, not by a person. Open OAuth > Clients > lab-printer, set Access token manager to
lab-tmp-deny-benand save. Runstart_attempt, add&prompt=loginto the address, sign in as Ben and approve. The callback succeeds andhandle_callbackaccepts it, butredeemanswersinvalid_grant, "The tenant's access token policy refused to issue this token."
Why it matters: a refusal does not always mean someone clicked Cancel. In this tenant a code-flow policy refusal arrives at the token endpoint; in the implicit grant lab the same kind of refusal arrives at the callback as access_denied.
Restore: in OAuth > Clients > lab-printer, set Access token manager back to Default access tokens and save.
No callback at all. Run
start_attempt, then open the address with your redirect URI changed by one character, for example a missing trailing slash. The tenant shows its own error page and the printer never hears back. Its pending attempt simply ages out.
Rejected at the token endpoint. Compare the status codes, then capture a request ID.
A wrong secret:
curl -s -i -u "$CLIENT_ID:not-the-secret-not-the-secret-not-the-secret" -d grant_type=authorization_code -d code=x -d redirect_uri=x "$ISSUER/oauth/token"gives401 invalid_client.A missing
code: the request below gives400 invalid_request.A mismatched verifier gives
400 invalid_grant, as in the PKCE lab.
curl -s -D - -o /dev/null -u "$CLIENT_ID:$CLIENT_SECRET" -d grant_type=authorization_code "$ISSUER/oauth/token" | grep -i x-request-id
Search Audit or Logs for that request ID: it is the oauth.token rejected invalid_request event for lab-printer.
Why it matters: the printer records the stage, a correlation ID and a safe error category, never the code, verifier or Authorization header. error_description is untrusted text: display it as text, never as markup.
A lost response. Get a code with
start_attemptandhandle_callback, then exchange it but keep the answer out of sight, as if it never arrived.
Simulation. only the dropped connection is simulated, by not reading the response. The exchange, the issued token, the retry and the revocation are all real.
LOST=$(curl -s -u "$CLIENT_ID:$CLIENT_SECRET" -d grant_type=authorization_code -d "code=$CODE" \
--data-urlencode "redirect_uri=$REDIRECT_URI" -d "code_verifier=$VERIFIER" "$ISSUER/oauth/token")
redeem # the "retry" with the same code
The retry answers invalid_grant, "The authorization code was already used. Tokens issued from it have been revoked." Now look at the token the printer never saw: btl-lab introspect "$(echo "$LOST" | jq -r .access_token)" returns {"active": false}.
Why it matters: a timeout is not a confirmed failure, and resending a code cannot recover it. A second presentation looks like a stolen code, so the server revokes what it issued. The printer reports that the connection did not finish and offers a fresh attempt.
The complete exchange, with every check in place, in the same terminal.
start_attempt, open the address, sign in as Ava and approve.handle_callback "<state>" "<iss>" "<code>": session-boundstate, expectediss, claimed once.redeem: the printer's secret, the sameredirect_uriand this attempt's verifier. Keep the token in memory only:read -r TOKEN.Read
scopeandexpires_infrom the response.Use the token at the API:
btl-lab introspect "$TOKEN".Move on from the callback page without saving its address anywhere.
Compare the Audit chain with the one you labeled in the Following the complete exchange lab: request_started, sign-in or an existing session, code_issued, oauth.token succeeded, then oauth.introspect by lab-photo-api.
Break it
Steps 2 to 5 are the deliberate failures. The only setting changed, in step 2, is restored in the same step.
Check your work
Press Check my progress. Logs also has oauth.authorize rejected invalid_redirect_uri for step 3, which never reached the printer.
Cleanup
Confirm
lab-printeruses Default access tokens, then delete thelab-tmp-deny-benmanager.