OAUTH 2.0 · LAB
Hold a client to one authentication method
Read the tenant's supported methods, authenticate correctly, and see every other way of presenting a credential refused with the same uninformative answer.
Partly readyUses your lab tenant
The lesson
Builds on: Client credentials.
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.
- G8 Client authentication beyond `client_secret_basic` and `none`
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.
Authenticate with the registered method
Recorded as
oauth.tokensucceeded forlab-print-orders.The right secret sent the wrong way is refused
Recorded as
oauth.tokenrejected (unsupported_auth_method) forlab-print-orders.A wrong secret is refused
Recorded as
oauth.tokenrejected (invalid_client) forlab-print-orders.A public client revokes with its client ID only
Recorded as
oauth.revokesucceeded (token_not_found) forlab-printer-app.A public client cannot use introspection
Recorded as
oauth.introspectrejected (client_authentication_required) forlab-printer-app.
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. In this lab
CLIENT_IDandCLIENT_SECRETholdlab-print-orders, andAPP_IDholdslab-printer-app. Defineencas in the earlier labs.Press Start on this page.
Walkthrough
Read the methods the tenant supports at each back-channel endpoint.
curl -s "$ISSUER/.well-known/openid-configuration" | jq '{token_endpoint_auth_methods_supported, introspection_endpoint_auth_methods_supported, revocation_endpoint_auth_methods_supported}'
The answers are ["client_secret_basic","none"], ["client_secret_basic"] and ["client_secret_basic","none"].
Why it matters: client authentication happens only where a client talks directly to the authorization server. Each endpoint publishes which methods it accepts.
Authenticate with the registered method, building the header the careful way: form-encode the client ID and secret, then join them with a colon and Base64-encode the result.
BASIC=$(printf '%s:%s' "$(enc "$CLIENT_ID")" "$(enc "$CLIENT_SECRET")" | openssl base64 -A)
curl -s -H "Authorization: Basic $BASIC" -d grant_type=client_credentials -d scope=prints.create "$ISSUER/oauth/token" | jq '{token_type, scope}'
The answer has a token. This tenant generates secrets from characters that need no escaping, so the form-encoding step changes nothing here, but a client that skips it fails as soon as a secret contains + or :.
The right secret, sent the wrong way. Put it in the body, as a developer copying another provider's example might (
client_secret_post).
curl -s -i -d grant_type=client_credentials -d scope=prints.create -d "client_id=$CLIENT_ID" -d "client_secret=$CLIENT_SECRET" "$ISSUER/oauth/token"
The answer is 401 invalid_client with "Confidential clients authenticate with HTTP Basic (client_secret_basic). Public clients send only client_id, with no secret." Audit records reason unsupported_auth_method.
Why it matters: the credential was correct, but the method was not the one this client uses. If a client could fall back to another method, any weaker credential the server still held would become a second way in.
Both at once: add
-u "$CLIENT_ID:$CLIENT_SECRET"to step 3. The answer is the same401. One request uses one method; two are not doubly authenticated.
A wrong secret, then an unknown client ID.
curl -s -i -u "$CLIENT_ID:wrong-secret-wrong-secret-wrong-secret" -d grant_type=client_credentials "$ISSUER/oauth/token"
curl -s -i -u "${CLIENT_ID%?}0:$CLIENT_SECRET" -d grant_type=client_credentials "$ISSUER/oauth/token"
Both answer 401 with the same body and WWW-Authenticate: Basic realm="OAuth token". If the last character of your client ID is already 0, use another digit in the second command.
Why it matters: the response does not say which part was wrong, so it does not help someone guessing at secrets. Audit records the wrong secret against lab-print-orders; the unknown client ID appears only as a count in Logs.
A public client authenticates with
none. Revoke a value that is not a token aslab-printer-app, identified by its client ID alone.
POST$ISSUER/oauth/revoke
Open in console
POST $ISSUER/oauth/revoke HTTP/1.1
Content-Type: application/x-www-form-urlencoded
client_id=$APP_ID&token=not-a-tokenThe answer is 200 with {}. Send the same body to $ISSUER/oauth/introspect: 400 unauthorized_client, because introspection requires a client that can authenticate.
Planned walkthrough
These steps need a per-client authentication method setting with more methods (G8).
Open OAuth > Clients > lab-print-orders and set Token endpoint authentication method to
client_secret_post. Repeat step 3:200. Repeat step 2:401 invalid_client, now because Basic is not this client's method.Register a public key for
lab-print-ordersand switch it toprivate_key_jwt. A client assertion built by the lab toolkit from your own key is accepted, and both secret forms are refused. Audit records which method each request used.
Break it
Steps 3 to 6 are the deliberate failures. No setting changes.
Check your work
Press Check my progress. Logs also has an oauth.token rejected invalid_client summary with status 401 that includes the unknown client ID from step 5.
Cleanup
None.
Missing infrastructure
**G8, client authentication beyond
client_secret_basicandnone.** A per-clienttoken_endpoint_auth_methodsetting withclient_secret_post,client_secret_jwt,private_key_jwt(a registered JWK orjwks_uri) andtls_client_authorself_signed_tls_client_auth, with Audit recording the method used. With it, the planned steps movelab-print-ordersbetween methods and see each other method refused.