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

Label your tenant's tokens, then send a subject token and an actor token together

Label each token Lab Photos issues with its RFC 8693 type identifier and ask the tenant which it recognizes. Then plan a support console exchange where Ben acts for Ava.

PlannedUses your lab tenant

The lesson

Builds on: Delegation and impersonation.

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.

Setup

These steps are real today.

  1. In Lab Photos, confirm OAuth > Flow policy allows refresh_token. On lab-printer, allow the refresh_token grant and assign openid, photos.read and offline_access.

  2. Keep CLIENT_ID and CLIENT_SECRET for lab-printer in the shell.

Planned walkthrough

This walkthrough runs once your tenant supports token exchange with actor tokens and policy conditions on the actor (G6). Ben, the help desk member of the lab cast, plays the lesson's support agent, and Ava is the customer.

Planned setup

  1. In Groups, create lab-tmp-support-agents with Ben as its only member, and set a password for [email protected].

  2. In OAuth > Clients, create lab-tmp-support-console (confidential web, redirect URI http://127.0.0.1:8765/callback, PKCE required, scope openid). Store its credentials as CONSOLE_ID and CONSOLE_SECRET with read -rs.

  3. In OAuth > Resources (planned), add the print API at https://prints.lab.example owning a read scope prints.read.

  4. On lab-tmp-support-console, set the exchange policy (planned): subject tokens are access tokens issued to lab-printer; actor tokens are ID tokens issued to lab-tmp-support-console whose subject is in lab-tmp-support-agents; target https://prints.lab.example with prints.read; delegation; lifetime 10 minutes.

Steps

  1. Ben signs in to the console with an ordinary code flow for lab-tmp-support-console and scope openid, which gives the console BEN_ID_TOKEN. Ava's printer access token is AT.

  2. The console exchanges both tokens:

curl -s -u "$CONSOLE_ID:$CONSOLE_SECRET" "$ISSUER/oauth/token" \
  --data-urlencode grant_type=urn:ietf:params:oauth:grant-type:token-exchange \
  --data-urlencode "subject_token=$AT" --data-urlencode subject_token_type=urn:ietf:params:oauth:token-type:access_token \
  --data-urlencode "actor_token=$BEN_ID_TOKEN" --data-urlencode actor_token_type=urn:ietf:params:oauth:token-type:id_token \
  --data-urlencode resource=https://prints.lab.example -d scope=prints.read | jq -r .access_token

Decoded, the new token has sub Ava, client_id the console and act.sub Ben.

Why it matters: one request identifies three parties. Client authentication speaks for the console, the actor token for Ben, and the subject token for Ava. Several agents share the console, so only an actor token can say which of them is acting.

  1. Remove Ben from lab-tmp-support-agents and repeat step 2. The result is invalid_request, and Audit records oauth.token rejected invalid_actor_token (planned reason). Add Ben back.

Why it matters: the server applies its own current policy to the actor, whatever any token says, much as it would compare a may_act claim with the party asking to act.

  1. Send actor_token_type without actor_token. The result is invalid_request.

Why it matters: the type is required with an actor token and must not be sent without one.

Do today

  1. Collect every token type your tenant issues. Start btl-lab callback and run a lab-printer code flow for openid photos.read offline_access:

eval "$(btl-lab pkce)"; eval "$(btl-lab state)"
echo "$ISSUER/oauth/authorize?response_type=code&client_id=$CLIENT_ID&redirect_uri=http%3A%2F%2F127.0.0.1%3A8765%2Fcallback&scope=openid%20photos.read%20offline_access&state=$STATE&code_challenge=$CHALLENGE&code_challenge_method=S256"

Approve as Ava, check state and iss, then exchange the code:

read -rs CODE
RESP=$(curl -s -u "$CLIENT_ID:$CLIENT_SECRET" "$ISSUER/oauth/token" -d grant_type=authorization_code --data-urlencode "code=$CODE" \
  --data-urlencode redirect_uri=http://127.0.0.1:8765/callback --data-urlencode "code_verifier=$VERIFIER"); unset CODE

Label each token with the lesson's identifiers: .access_token is urn:ietf:params:oauth:token-type:access_token, .refresh_token is urn:ietf:params:oauth:token-type:refresh_token, and .id_token is urn:ietf:params:oauth:token-type:id_token. The access token is also a JWT, but from its own issuer's point of view it is labeled by purpose, not format.

  1. Ask the tenant which of them it can read as tokens it issued:

for f in access_token refresh_token id_token; do printf '%s: ' "$f"
  curl -s -u "$CLIENT_ID:$CLIENT_SECRET" "$ISSUER/oauth/introspect" --data-urlencode "token=$(jq -r .$f <<<"$RESP")" | jq -c '{active, token_type}'; done
unset RESP

The access token is active with token_type Bearer, the refresh token is active with no token_type, and the ID token is active: false: it is a statement for the client, not a credential the server tracks. The label exists because a server accepting several kinds of token must know which one it is reading.

  1. No configuration can grant permission to act. In OAuth > Access token managers, try to add a claim mapping named may_act to any manager. The name is reserved, so the change is refused.

Check your work

There are no automated checks while this lab is planned. For Do today, look in Lab Photos' Audit for oauth.token succeeded for lab-printer and three oauth.introspect events for lab-printer, two with access_token_found and refresh_token_found and one with token_not_found.

Cleanup

Delete lab-tmp-support-console and lab-tmp-support-agents if you created them for the planned steps. Keep the lab-printer settings.

Missing infrastructure

  • G6 Token exchange, including actor tokens, the six token type identifiers, requested_token_type, and exchange policy conditions on the actor such as group membership. Issuing may_act grant tokens, as in the lesson's "Let the agent view my order", would be a further addition on top of G6; the planned walkthrough uses the server's own group policy instead.

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