OAUTH 2.0 · LAB
Trace your tenant's metadata to the documents that define it
Map discovery fields and tenant behavior to the specifications and requirement keywords behind them, and see the tenant refuse to advertise a capability it does not have.
ReadyUses your lab tenant
The lesson
Builds on: Migrating older integrations.
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.
Publish custom metadata fields
Recorded as
tenant.oauth.metadata.updatesucceeded.Advertising a capability the server lacks is refused
Recorded as
tenant.oauth.metadata.updaterejected.Remove the custom fields again
Recorded as
tenant.oauth.metadata.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 the Metadata Management permission. Set
ISSUERand press Start on this page.
Walkthrough
Read your tenant's discovery document and note, for each field, the document that defines it.
GET$ISSUER/.well-known/openid-configuration
Open in console
GET $ISSUER/.well-known/openid-configuration HTTP/1.1| Field | Defined by |
|---|---|
issuer, authorization_endpoint, token_endpoint | RFC 8414 and OpenID Connect Discovery 1.0 |
code_challenge_methods_supported | RFC 8414, for PKCE from RFC 7636 |
authorization_response_iss_parameter_supported | RFC 9207 |
revocation_endpoint | RFC 8414, for revocation from RFC 7009 |
introspection_endpoint | RFC 8414, for introspection from RFC 7662 |
userinfo_endpoint | OpenID Connect Discovery 1.0, an OpenID Foundation Final Specification |
Why it matters: no single document describes this server. RFCs from the IETF and final specifications from the OpenID Foundation each contribute fields, and later documents update earlier ones without replacing them.
Read
btl_service_status("partial") andbtl_endpoint_status. List the catalogued endpoints that are not implemented and the specification each comes from: device authorization (RFC 8628), pushed authorization requests (RFC 9126), dynamic client registration (RFC 7591) and logout (OpenID Connect). Thebtl_prefix marks fields this provider defines for itself.
Registered names. Send the device authorization grant's registered URN to the token endpoint.
curl -s -d grant_type=urn:ietf:params:oauth:grant-type:device_code -d device_code=none -d client_id=x "$ISSUER/oauth/token" | jq
The answer is unsupported_grant_type. Look up both the URN and the error code in the IANA OAuth Parameters registry at https://www.iana.org/assignments/oauth-parameters/, and note which RFC registered each.
Private and informational metadata. In Metadata Management, under Custom metadata fields, add
{"op_policy_uri": "https://example.com/policy", "x_lab_note": "oauth core labs"}and save. Fetch discovery again: both appear.
A capability the server lacks cannot be advertised. Try adding
{"dpop_signing_alg_values_supported": ["ES256"]}. Saving is refused withinvalid_metadata. Registered names describe real behavior; private names use thex_prefix.
Why it matters: a client trusts discovery to tell it what the server does. A registered field that claims DPoP support the server lacks would send clients down a path that fails.
Follow PKCE through the documents, with your tenant's evidence beside each step.
| Document | What it said about PKCE | Evidence in your tenant |
|---|---|---|
| RFC 7636 (2015) | defined it, mainly for public clients | code_challenge_methods_supported: ["S256"] |
| RFC 8252, BCP 212 (2017) | required it for public native apps | lab-printer-app refused without a challenge (Public and confidential clients lab) |
| RFC 9700, BCP 240 (2025) | public clients MUST use it; RECOMMENDED for confidential clients | per-client PKCE policy: Required by default, Optional possible |
| OAuth 2.1 (Internet-Draft) | part of the authorization code grant itself | Required is the default for every new client here |
Requirement keywords, with tenant evidence for each.
| Requirement | Keyword | Your evidence |
|---|---|---|
| RFC 9700: the password grant | MUST NOT | unsupported_grant_type (password grant lab) |
| RFC 9700: public clients use PKCE | MUST | pkce_required for lab-printer-app |
| RFC 9700: PKCE for confidential clients | RECOMMENDED | Required by default, Optional only by an explicit choice (migration lab) |
| RFC 9700: implicit and other token responses | SHOULD NOT | off by default, with a warning when enabled (implicit grant lab) |
| RFC 10017: browser-based clients and the implicit grant | MUST NOT | the tenant still offers implicit as an explicit choice; record this as a deliberate difference |
Why it matters: capitalized keywords carry precise force, and a server's defaults show how it reads them. A MUST NOT leaves nothing to decide; a SHOULD NOT leaves a narrow path that needs a reason.
Break it
Step 5 is the refusal. Nothing is saved.
Check your work
Press Check my progress. Logs also has oauth.token rejected unsupported_grant_type for step 3.
Cleanup
In Metadata Management, remove the custom fields from step 4 and save. Confirm discovery no longer lists them.