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

Watch the tenant check DPoP proofs at the token endpoint and UserInfo

See the tenant apply the lesson's proof checklist, require DPoP for one client, bind a code with dpop_jkt, and keep a confidential client's refresh token usable across key changes.

PlannedUses your lab tenant

The lesson

Builds on: Creating a DPoP proof.

New to the labs? Start with the lab toolkit and the shared cast and names every lab uses.

Planned. The core of this lab waits on platform features that are not built yet. The planned walkthrough shows exactly how it will run; Do today is a real exercise you can do now.

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

  1. Keep app-key.pem, app-key.jwk, the b64url helper and the shell variables for lab-printer and lab-printer-app from the earlier labs.

  2. Planned (G14): on lab-printer, turn on DPoP-bound access tokens required (dpop_bound_access_tokens: true).

  3. Planned (G14): open OAuth > Clients > DPoP proof test, a tenant tool that runs a pasted proof through the checklist for a chosen method and address and shows which check passed or failed, so you can see each check without sending malformed requests.

Note: the lab toolkit has no command yet that signs a DPoP proof with your own key. The planned steps say exactly which proof each request needs; they need that command before they can run.

Planned walkthrough

  1. Paste a correct UserInfo proof into the proof test with method GET and address $ISSUER/oidc/userinfo. Every check passes: one header, required claims, typ, an asymmetric alg, a signature by the embedded key with no private members, matching htm and htu, and freshness.

Why it matters: "Checking a proof". These are the lesson's eight checks, in the tenant's own words.

  1. In the same tool, change the method to POST: the htm check fails. Change the address to another path, or to http://: the htu check fails.

Why it matters: a proof is valid for one method and one public address only.

  1. Request a token for lab-printer without a DPoP header. Expect 400 invalid_request.

Why it matters: "At the token endpoint". dpop_bound_access_tokens closes the unbound route for that client.

  1. Start an authorization for lab-printer-app with &dpop_jkt=<thumbprint of app-key.jwk>, then redeem the code with a proof from app-key.pem. It succeeds.

Why it matters: the authorization code itself is bound to the key, so a code stolen on its way back is useless without it.

  1. Refresh a lab-printer token (with its Basic credentials) using a proof from a new key, printer-dpop-2.pem. It succeeds, and the new access token's cnf.jkt is the new key's thumbprint.

Why it matters: confidential clients authenticate on every refresh, so their refresh tokens are not bound to a DPoP key and a back end can change keys without losing its grants.

  1. Switch lab-printer-app's access token manager to opaque and call UserInfo with DPoP again: still served, because the tenant reads the binding from storage, as introspection does. Switch it back to jwt.

Why it matters: "At the photo API". The binding check works for both token formats; for an opaque token, the resource gets cnf.jkt from introspection and compares it itself.

Do today

  1. Read the exact public addresses a proof's htu must match.

GET$ISSUER/.well-known/oauth-authorization-server Open in console
GET $ISSUER/.well-known/oauth-authorization-server

Note token_endpoint and the userinfo_endpoint from the OpenID configuration. A server behind a load balancer must compare against these public addresses, not the internal one it sees.

  1. Introspect a current lab-printer-app token with btl-lab introspect "$TOKEN" and list the fields a DPoP-aware resource would need that are missing: cnf with jkt, and a token type of DPoP.

  2. Open lab-printer in OAuth > Clients. There is no DPoP setting, so no client can require binding today.

  3. Take the two claim sets you assembled in Build DPoP proof contents and hashes by hand, for POST at the token endpoint and GET at UserInfo, and run each through the lesson's checklist by hand. For the UserInfo one, also write the two comparisons only a resource makes: ath against the token in the Authorization header, and the key's thumbprint against cnf.jkt.

  4. Run the bearer-only behavior the lesson warns about, using the tenant as the resource: call UserInfo with Authorization: Bearer $TOKEN and no proof. It succeeds, as an older API that ignores cnf would. That is why every resource that receives bound tokens needs the checks before the protection is real.

Break it

These run once G14 exists. Each is a client mistake the tenant must catch.

  1. Redeem the step 4 code with a proof from a different key of yours. Expect 400 invalid_grant.

  2. Reuse a UserInfo proof made for one access token with a newer token from the same app. Expect 401 with WWW-Authenticate: DPoP error="invalid_token", because ath names the old token.

  3. Send a bound token as Authorization: Bearer with no proof. Expect a refusal: a resource that knows about DPoP never accepts a bound token as a bearer token.

Check your work

Today, Audit shows oidc.userinfo succeeded userinfo_served for the bearer call. Once G14 exists, Audit shows oauth.token refusals for the missing proof and the dpop_jkt mismatch, an oidc.userinfo refusal for the ath mismatch, and tenant.oauth.clients.update for the client setting.

Cleanup

  1. Once G14 exists, turn DPoP-bound access tokens required off on lab-printer, and confirm lab-printer-app's token manager format is jwt.

  2. Revoke the tokens you obtained and delete printer-dpop-2.pem if you made it.

Missing infrastructure

  • G14: proof checks at the token endpoint and UserInfo, dpop_bound_access_tokens, dpop_jkt, binding for opaque tokens through storage and introspection, and a proof test tool in the tenant portal.

  • G3: a sample protected resource, so the lab can compare a DPoP-aware resource with a deliberately bearer-only lab endpoint, clearly labelled and limited to the lab.

  • Once these exist and the toolkit can sign proofs, the Planned walkthrough runs as written.

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