Beta

Create a tenant

A new tenant starts with its own users, OAuth settings, audit history and logs. You are its first Tenant Admin.

BTL Admin

OAUTH 2.0 · LAB

Read your tenant's metadata member by member and prove supported is not allowed

Read every member of Lab Photos metadata, see a listed grant and an existing scope refused for lab-collage, move an endpoint and follow it, and publish documentation for developers.

ReadyUses your lab tenant

The lesson

Builds on: Discovering server capabilities.

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.

  1. Ask for a scope that exists but is not lab-collage's

    Recorded as oauth.authorize rejected (invalid_scope) for lab-collage.

  2. Use a grant the server lists but lab-collage may not

    Recorded as oauth.token rejected (unauthorized_client) for lab-collage.

  3. Move the revocation endpoint in OAuth > Metadata

    Recorded as tenant.oauth.metadata.update succeeded.

  4. Revoke a token at the address metadata now gives

    Recorded as oauth.revoke succeeded (access_token_found) for lab-collage.

  5. Try to advertise a capability the tenant lacks

    Recorded as tenant.oauth.metadata.update rejected.

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 collage.sh, servers.json and the shell variables from the issuer labs. Run . ./collage.sh in ~/btl-issuer.

  1. In Lab Photos, open OAuth > Scopes and confirm photos.read (common) and photos.delete (exclusive) exist. The Lab Photos preset creates both.

  2. Open OAuth > Flow policy and confirm client_credentials is allowed. The OAuth core track allows it for lab-print-orders; allow it now if it is not.

  3. Confirm lab-collage allows only the authorization_code grant and is not assigned photos.delete.

  4. Press Start on this page with Lab Photos selected.

Walkthrough

  1. List what the document contains and what it leaves out.

GET$ISSUER/.well-known/oauth-authorization-server Open in console
GET $ISSUER/.well-known/oauth-authorization-server
curl -s "$ISSUER/.well-known/oauth-authorization-server" > meta.json; jq -r 'keys[]' meta.json
jq '{par: .pushed_authorization_request_endpoint, device: .device_authorization_endpoint, registration: .registration_endpoint, status: .btl_endpoint_status}' meta.json

The first three are null: those members are absent. btl_endpoint_status marks the matching paths not_implemented.

Why it matters: a member that would have no value is left out, so a client never builds an address the server did not publish. RFC 8414 requires only a few members; most come from optional extensions.

  1. Read the capability members:

jq '{scopes_supported, response_types_supported, grant_types_supported, code_challenge_methods_supported,
  token_endpoint_auth_methods_supported, authorization_response_iss_parameter_supported}' meta.json

scopes_supported lists photos.read and openid but not photos.delete, although that scope exists. Your tenant advertises its common scopes only.

Why it matters: the list shows what the server chooses to advertise. A client uses it to confirm the scopes it needs exist, not as a menu of everything it could ask for.

  1. Supported is not allowed. photos.delete exists, but it is not lab-collage's. Start the listener with btl-lab callback, then:

start_attempt photos "openid photos.delete"

Open the URL. Before any sign-in, the listener receives error=invalid_scope with your state and Lab Photos' iss. Now use a grant that grant_types_supported lists:

curl -s -u "$CLIENT_ID:$CLIENT_SECRET" "$(srv photos token_endpoint)" -d grant_type=client_credentials | jq .

The result is {"error":"unauthorized_client",...}.

Why it matters: metadata describes the server, not any one client. The client's registration decides which of the listed options it may use, and each request is still decided on its own.

  1. Two meanings of none. jq '.token_endpoint_auth_methods_supported, .token_endpoint_auth_signing_alg_values_supported' meta.json prints a list containing none, then null.

Why it matters: here none means a public client that identifies itself without authenticating. The signing algorithm list is required only when a JWT-based client authentication method is offered, and there none would mean an unsigned JWT, which RFC 8414 forbids.

  1. Endpoint addresses are used exactly as given. Get a token for Ava first: start the listener, run start_attempt photos openid, approve, read_response, then:

check_response && TOKEN=$(redeem "$RESP_CODE" | jq -r .access_token)

In Lab Photos, open OAuth > Metadata and change the revocation endpoint path to /oauth/revoke-v2. Save. Then follow the document:

REVOKE=$(curl -s "$ISSUER/.well-known/oauth-authorization-server" | jq -r .revocation_endpoint); echo "$REVOKE"
curl -s -o /dev/null -w 'old path: %{http_code}\n' -X POST "$ISSUER/oauth/revoke"
curl -s -o /dev/null -w 'published path: %{http_code}\n' -u "$CLIENT_ID:$CLIENT_SECRET" "$REVOKE" --data-urlencode "token=$TOKEN"

The document now gives .../oauth/revoke-v2, the old path returns 404, and the revocation at the published address returns 200.

Restore: set the revocation endpoint path back to /oauth/revoke and save.

Why it matters: nothing requires endpoints to follow a naming pattern. A client that built /oauth/revoke onto the issuer breaks after this change; one that reads metadata follows it, after at most one cache lifetime.

  1. Documentation for people. In OAuth > Metadata, add custom metadata and save:

{"service_documentation": "https://photos.lab.example/developers", "x_lab_owner": "issuer-track"}

Both members now appear in discovery: curl -s "$ISSUER/.well-known/oauth-authorization-server" | jq '{service_documentation, x_lab_owner}'.

Why it matters: service_documentation is where developers read what the document cannot express, such as how to request a scope that needs review. Your tenant accepts informational members and x_ members of its own.

Break it

  1. Try to advertise a capability the tenant lacks. In OAuth > Metadata, add {"dpop_signing_alg_values_supported": ["ES256"]} to the custom metadata and save. The change is refused and Audit records tenant.oauth.metadata.update rejected.

Why it matters: a client would turn on DPoP because the document said so. Runtime capabilities come from what the server really does, never from a typed value.

  1. Try to move the revocation endpoint outside the protocol paths, to /revoke. That is refused too.

Check your work

Press Check my progress. The checks look in Lab Photos for:

  • oauth.authorize rejected with invalid_scope, then oauth.token rejected with unauthorized_client, both for lab-collage.

  • tenant.oauth.metadata.update succeeded for the move, then oauth.revoke succeeded with access_token_found for lab-collage.

  • tenant.oauth.metadata.update rejected from Break it.

Audit also shows the metadata updates for the restore and the documentation members.

Cleanup

In OAuth > Metadata, confirm the revocation path is /oauth/revoke, and remove the custom members if you do not want them published. Delete meta.json and run unset TOKEN RESP_CODE.

Back to all labs

We value your privacy

We use cookies and similar technologies to enhance your browsing experience, and analytics to understand our traffic. By clicking "Allow All", you consent to optional analytics. Cookie Policy

The Lab