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

Disconnect the printer and measure what stops working

Revoke tokens as the printer, see one client refused when it tries to revoke another's token, and measure what still works at an API that introspects and at one that does not.

Partly readyUses your lab tenant

The lesson

Builds on: Token introspection, Client credentials.

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

Partly ready. Most of this lab runs today. Steps that wait on platform features are marked, and Missing infrastructure says what they need.

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. Revoke Ava's refresh token as lab-printer

    Recorded as oauth.revoke succeeded (refresh_token_found) for lab-printer.

  2. See the revoked refresh token refused

    Recorded as oauth.token rejected (refresh_revoked) for lab-printer.

  3. Revoke only an access token

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

  4. Be refused revoking lab-printer's token as lab-print-orders

    Recorded as oauth.revoke rejected (other_client_token) for lab-print-orders.

  5. Find a disabled client refused at its next refresh

    Recorded as oauth.token rejected (client_disabled) for lab-printer.

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. Choose Lab Photos as the lab tenant and press Start.

  2. Confirm lab-printer uses Default access tokens (Signed JWT) and has the refresh token grant. Load CLIENT_ID and CLIENT_SECRET for it, ORDERS_ID and ORDERS_SECRET for lab-print-orders, and API_ID and API_SECRET for lab-photo-api, each secret with read -rs.

  3. Use the authorize, exchange and refresh helpers from Rotate a refresh token family, and add a revocation helper that prints only the status line and body:

revoke() {  # $1 = token, $2 = hint; uses lab-printer unless REVOKE_AS is set to "id:secret"
  curl -s -w '\nHTTP %{http_code}\n' -u "${REVOKE_AS:-$CLIENT_ID:$CLIENT_SECRET}" "$ISSUER/oauth/revoke" \
    --data-urlencode "token=$1" ${2:+-d token_type_hint=$2}
}
  1. Run two photo APIs in separate terminals, both expecting the default audience:

    • local validation: btl-lab resource --mode jwt on port 8766

    • introspection: btl-lab resource --mode introspect --port 8767 (with API_ID and API_SECRET exported)

Walkthrough

  1. Connect. authorize "photos.read offline_access", sign in as Ava, exchange, and keep REFRESH. Call both APIs at GET /photos: 200 and 200.

  1. Disconnect by revoking the refresh token, the credential that keeps the connection alive.

POST$ISSUER/oauth/revoke Open in console
POST $ISSUER/oauth/revoke HTTP/1.1
Content-Type: application/x-www-form-urlencoded

token=$REFRESH&token_type_hint=refresh_token

In the console, enter lab-printer's client ID and secret, or run revoke "$REFRESH" refresh_token. Returns 200 with an empty body.

Why it matters: the answer carries nothing beyond its status. The printer wanted the token to stop working, and it has.

  1. Send exactly the same request again: 200. Revoke a made-up string: revoke "no-such-token" also returns 200.

Why it matters: unknown and already revoked tokens get the same answer, so revocation is safe to repeat after a timeout, and the answer reveals nothing about which strings are tokens.

  1. See what the revocation reached.

    • refresh "$REFRESH": invalid_grant, Audit reason refresh_revoked.

    • The introspecting API on 8767, after its cached answer expires (about a minute): 401.

    • The JWT API on 8766: still 200.

Why it matters: revoking a refresh token also revoked the access tokens from its grant, but only an API that asks the authorization server can see that. Local validation accepts the JWT until its exp.

  1. Measure the window. Read exp with btl-lab decode "$TOKEN" and subtract the time of step 2: echo $(( exp - revoked_at )) seconds. With the default one-hour lifetime the window is long; the lesson's ten-minute token gave six minutes.

Why it matters: short lifetimes keep this window small, and an API can introspect even a JWT before a destructive operation such as a deletion.

  1. Revoke only an access token. Start a fresh connection (step 1), then revoke "$TOKEN" access_token. refresh "$REFRESH" still succeeds.

Why it matters: revoking an access token may leave the refresh token working. A client that wants to end everything sends the refresh token.

  1. Try to end another client's access. With a current lab-printer refresh token in REFRESH, revoke it while authenticating as the back-office job:

REVOKE_AS="$ORDERS_ID:$ORDERS_SECRET" revoke "$REFRESH" refresh_token

Returns 400 with {"error": "unauthorized_client"}, and Audit records oauth.revoke rejected with other_client_token. Introspect the token as lab-printer: it is still active.

Why it matters: one client may not end another's access. The tenant refuses with an error, as RFC 7009 describes, so the caller learns it cannot do this, while unknown strings still get 200.

  1. Disconnect in the right order. The printer marks the connection disconnected first, keeps the refresh token only until the server answers 200, and only then deletes it:

if revoke "$REFRESH" refresh_token | grep -q 'HTTP 200'; then unset REFRESH; echo "revoked and deleted"; else echo "kept in the pending queue; retry later"; fi

Why it matters: deleting first would leave a working token at the photo service and no way left to revoke it. A 503 means the token may still work.

  1. End access from the authorization server's side. Start a fresh connection, then in Clients set lab-printer to disabled and save. Run refresh "$REFRESH": 401 invalid_client, Audit reason client_disabled. Enable it again: the tokens issued before were revoked by the change, so the printer has to connect again.

Why it matters: the provider can end access even when the client is offline or no longer trusted, and the client finds out only at its next request.

Restore: make sure lab-printer is enabled.

Break it

Revoke with a wrong secret: REVOKE_AS="$CLIENT_ID:wrong-secret-wrong-secret-wrong-secret" revoke "$TOKEN". Returns 401 invalid_client, the same refusal as at the token endpoint. Audit records it against lab-printer.

Check your work

Press Check my progress. The checks look for, in order: oauth.revoke with refresh_token_found, oauth.token rejected with refresh_revoked, oauth.revoke with access_token_found, the refused oauth.revoke with other_client_token from lab-print-orders, and oauth.token rejected with client_disabled.

Audit also shows oauth.revoke succeeded with token_not_found for the made-up string, and tenant.oauth.clients.update twice for disabling and enabling lab-printer.

Cleanup

  1. Confirm lab-printer is enabled.

  2. Stop both btl-lab resource terminals. Run unset TOKEN REFRESH RESP REVOKE_AS ORDERS_SECRET API_SECRET.

Missing infrastructure

  • G40 Connected applications. There is no page where Ava sees each client she approved, what it can access and when it was last used, and no administrator view of grants per user or client. Removing a grant there would end it and every family under it. Once it exists, step 9 becomes: sign in to $ISSUER/account as Ava, remove lab-printer, and watch its next refresh fail with invalid_grant while the client itself stays enabled for other people.

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