OAUTH 2.0 · LAB
Configure a client from nothing but an issuer
Answer every question a client needs from Lab Photos metadata, build well-known addresses with the insertion rule, and watch a capability change when you change the tenant's flow policy.
ReadyUses your lab tenant
The lesson
Builds on: Checking the response issuer.
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.
Change which grants Lab Photos allows
Recorded as
tenant.oauth.policy.updatesucceeded.Put the flow policy back as it was
Recorded as
tenant.oauth.policy.updatesucceeded.
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
You need ISSUER and the collage app's collage.sh in ~/btl-issuer from Build the collage app's callback issuer check. Run . ./collage.sh there, then press Start on this page with Lab Photos selected.
Walkthrough
Start from one trusted value.
ISSUERwas copied from your tenant's Overview, a source you control. Fetch the authorization server metadata from the well-known address built from it:
GET$ISSUER/.well-known/oauth-authorization-server
Open in console
GET $ISSUER/.well-known/oauth-authorization-serverThe first member repeats the issuer and the rest describe the server.
Why it matters: metadata does not remove the need for trusted configuration, it shrinks it to a single value. Everything else follows from the issuer.
Answer the lesson's questions from the document instead of from documentation:
curl -s "$ISSUER/.well-known/oauth-authorization-server" | jq '{authorization_endpoint, token_endpoint,
token_endpoint_auth_methods_supported, code_challenge_methods_supported, scopes_supported, jwks_uri,
authorization_response_iss_parameter_supported}'
btl-lab discover "$ISSUER"
You get the two endpoint addresses, client_secret_basic and none, ["S256"], the scopes your tenant advertises plus openid, the key set address and true. btl-lab discover also fetches the key set and lists its key IDs.
Why it matters: some answers are addresses and some are capabilities. A client that guesses a capability wrong either fails or quietly skips a protection the server was ready to provide, such as PKCE or the iss check.
Build addresses with the insertion rule. Save this as
wk.mjsand runnode wk.mjs:
const inserted = (issuer, name) => { const u = new URL(issuer); return `${u.origin}/.well-known/${name}${u.pathname.replace(/\/$/, '')}`; };
const appended = (issuer, name) => `${issuer.replace(/\/$/, '')}/.well-known/${name}`;
for (const issuer of [process.env.ISSUER, 'https://auth.photos.example/business'])
console.log(issuer, '\n RFC 8414:', inserted(issuer, 'oauth-authorization-server'), '\n OIDC: ', appended(issuer, 'openid-configuration'));
For your tenant, whose issuer has no path, both rules give addresses at the root of the host. For the lesson's path issuer they differ: RFC 8414 inserts the name before /business, OpenID Connect Discovery appends it after.
Why it matters: a client that appends the RFC 8414 name after an issuer's path looks in the wrong place. Each issuer on a shared host still has its own document.
See that an address is fixed by its issuer. Your tenant host has no issuer with a
/businesspath, so its inserted address does not exist:
curl -s -o /dev/null -w '%{http_code}\n' "$ISSUER/.well-known/oauth-authorization-server/business"
The result is 404.
Compare the OpenID Connect document. For an issuer without a path the two documents sit side by side:
GET$ISSUER/.well-known/openid-configuration
Open in console
GET $ISSUER/.well-known/openid-configurationdiff <(curl -s "$ISSUER/.well-known/oauth-authorization-server" | jq -S .) <(curl -s "$ISSUER/.well-known/openid-configuration" | jq -S .) && echo identical
The result is identical: your tenant serves one description at both names, including OpenID Connect members such as userinfo_endpoint and id_token_signing_alg_values_supported.
Why it matters: the documents are close relatives with mostly the same field names. A server with a path in its issuer must publish at both addresses to serve both kinds of client, because they no longer sit side by side.
Capabilities follow configuration. In Lab Photos, open OAuth > Flow policy and note whether
client_credentialsis allowed. Toggle it (allow it if it is cleared, clear it if it is allowed) and save. Read the grants again, along with the caching header:
curl -s "$ISSUER/.well-known/oauth-authorization-server" | jq .grant_types_supported
curl -sI "$ISSUER/.well-known/oauth-authorization-server" | grep -i cache-control
client_credentials appeared or disappeared, and the document is served with Cache-Control: public, max-age=300.
Restore: set client_credentials back to how you found it in Flow policy and save. Other labs rely on it when it was allowed.
Why it matters: clients learn about a new capability without anyone changing their settings, after at most one cache lifetime. The same is true when a capability is withdrawn.
Break it
Configure the issuer with a trailing slash, as a hurried operator might:
add_server photos-slash "$ISSUER/" "$CLIENT_ID"
add_server strips the slash to build the well-known address, so the fetch succeeds, but the record is refused with issuer_mismatch: the document names the issuer without the slash.
Why it matters: an address can be built from a sloppy value, but the identity check cannot pass with one. Validate metadata before using an endpoint covers that check in full.
Check your work
Press Check my progress. The checks look for the two tenant.oauth.policy.update events in Lab Photos: the change and the restore. Your terminal shows identical documents, both address rules for the path issuer, a 404 for the missing path document and the grant list changing.
Cleanup
Confirm that Lab Photos' Flow policy is as you found it. Delete wk.mjs if you do not want it. Keep collage.sh and servers.json for the next two labs.