OAUTH 2.0 · LAB
Prove each client can use only the grant that fits
Classify every lab client by whose access it needs and where it runs, then confirm with real refusals that registration and Flow policy hold each one to its grant.
ReadyUses your lab tenant
The lesson
Builds on: Client credentials, The refresh token grant.
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.
A user-facing client cannot act as itself
Recorded as
oauth.tokenrejected (unauthorized_client) forlab-printer.A public client cannot use client credentials
Recorded as
oauth.tokenrejected (client_authentication_required) forlab-printer-app.A refresh token cannot start access
Recorded as
oauth.tokenrejected (invalid_grant) forlab-printer.A token response is refused while the implicit grant is off
Recorded as
oauth.authorizerejected (unauthorized_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. This lab uses several clients, so each has its own name.
export ISSUER="https://tenant-<id>.beyondthelogin.dev"
export PRINTER_ID="<lab-printer client ID>"; read -rs PRINTER_SECRET
export ORDERS_ID="<lab-print-orders client ID>"
export APP_ID="<lab-printer-app client ID>"
enc() { jq -rn --arg v "$1" '$v|@uri'; }
Press Start on this page.
Walkthrough
Compare what the tenant advertises with its Flow policy.
GET$ISSUER/.well-known/openid-configuration
Open in console
GET $ISSUER/.well-known/openid-configuration HTTP/1.1grant_types_supported lists exactly the grants Flow policy allows: authorization_code, refresh_token and client_credentials after the earlier labs. response_types_supported lists code.
Classify the clients you have built, from OAuth > Clients, using the lesson's questions.
| Client | Whose access | Where it runs | Grant |
|---|---|---|---|
lab-printer | Ava's | backend, confidential | authorization code with PKCE, plus refresh |
lab-printer-app | Ava's | installed app, public | authorization code with PKCE on a loopback address |
lab-print-orders | its own | backend, confidential | client credentials |
lab-photo-api | none: it checks tokens | backend, confidential | no grant, introspection only |
| Token Decoder | the signed-in user's | browser, public | authorization code with PKCE |
Why it matters: whose access it is separates the grants first. Where the client runs then decides how it uses its grant, not which grant it gets.
A service client cannot start a user flow. Open an authorization request for
lab-print-orders.
echo "$ISSUER/oauth/authorize?response_type=code&client_id=$ORDERS_ID&redirect_uri=$(enc http://127.0.0.1:8765/callback)&scope=prints.create&state=x"
It stops on the tenant's error page: the client has no redirect URIs, so there is nowhere to return a person to.
A user-facing client cannot act as itself.
curl -s -u "$PRINTER_ID:$PRINTER_SECRET" -d grant_type=client_credentials "$ISSUER/oauth/token" | jq
The answer is unauthorized_client, "The tenant flow policy or this client's settings do not allow this grant type." Flow policy allows the grant, but the printer's registration does not.
A public client cannot use client credentials at all:
curl -s -d grant_type=client_credentials -d "client_id=$APP_ID" "$ISSUER/oauth/token" | jqanswersunauthorized_client, "Public clients cannot authenticate, so they cannot use this grant or endpoint."
A refresh token does not start access. Present one that was never issued.
curl -s -u "$PRINTER_ID:$PRINTER_SECRET" -d grant_type=refresh_token -d refresh_token=never-issued "$ISSUER/oauth/token" | jq
The answer is invalid_grant. Refresh extends an authorization the client already holds; it is never the first step.
Flows to avoid.
The password grant:
curl -s -d grant_type=password -d [email protected] -d password=demo-password-not-real -d "client_id=$APP_ID" "$ISSUER/oauth/token" | jqanswersunsupported_grant_type. The placeholder is not Ava's real password; never send a real one.The implicit grant: in
$ISSUER/token-decoder, choose response typetokenand start. The decoder reportsunauthorized_client, because Flow policy does not allow the implicit grant.
Why it matters: each grant follows from the situation, and registration plus Flow policy make the wrong choice fail instead of quietly working.
Break it
Steps 3 to 7 are the deliberate refusals. No setting changes in this lab.
Check your work
Press Check my progress. Logs also has oauth.authorize rejected invalid_redirect_uri for step 3 and oauth.token rejected unsupported_grant_type for the password grant. Both are refused before a client is identified, so they appear only in Logs.
Cleanup
None.