← Back

Demo Script

A walkthrough for each integration pattern, in the order they build on each other.

Before you start

Create a brand-new test userrather than reusing an existing account — sessions and device lists are much easier to read when they start empty. Sign up through any of the demos below using an @atko.email address, e.g. yourname.demo@atko.email. All three demos share the same Auth0 tenant and connection, so one signup works for all of them.

Sign in from at least two distinct browsers or devices with that same test user — e.g. Chrome and Firefox, or a laptop and a phone. Incognito/private tabs in the samebrowser usually still share the underlying OS keychain or extensions in ways that can muddy results, so a genuinely different browser or device is worth the extra minute. Most of what's interesting here — multiple sessions, multiple devices in a Device List, revoking one and watching the other die — only shows up once there's more than one signed-in client. Below, steps are tagged A or B for which browser/device to use.

1. Traditional Web App
/demo/traditional
Server-side cookie session, Session Management API, Back-Channel Logout.
  1. 1 · AOpen /demo/traditional and sign up as the new test user.
  2. 2 · AExpand the ID Token / Access Token / Refresh Token panels on the User Profile card — note these are only ever rendered server-side; the browser holds nothing but an encrypted session cookie.
  3. 3 · ACheck Active Sessions — it should show exactly one entry, this browser.
  4. 4 · BOpen /demo/traditional and sign in with the same test user.
  5. 5 · AClick Refresh on Active Sessions — it should now list two sessions. Expand each to compare device/IP/user-agent.
  6. 6 · ARevoke browser B's session from the list.
  7. 7 · BNavigate anywhere in the app — you should be forced back to login. Nothing was clicked in this browser to trigger it; the Management API deletion sent a Back-Channel Logout token that killed this session server-side.

What to look for

  • Session Management API returns both sessions with distinguishing device metadata.
  • Revoking from browser A actually terminates browser B — Back-Channel Logout, not just a client-side redirect.
2. SPA with DPoP
/demo/spa
No server-side session, DPoP-bound access tokens, Refresh Token Metadata, Device List.
  1. 1 · AOpen /demo/spa and sign in with the same test user.
  2. 2 · ACheck the token_type badge — it should read DPoP, not Bearer.
  3. 3 · AExpand the ID Token / Access Token panels. The access token is often opaque — DPoP-bound tokens don’t need to be a readable JWT to be verifiable.
  4. 4 · ACheck Refresh Token Metadata — browser/os/ip stamped by the Post-Login Action at login, mirrored onto the ID token purely so this demo can show it.
  5. 5 · AScroll to Other Devices — one entry, tagged “This device.” Click the pencil icon and rename it, e.g. “Chrome – Laptop.” Refresh the page and confirm the name stuck.
  6. 6 · BOpen /demo/spa and sign in with the same test user.
  7. 7 · BRename this device too, e.g. “Safari – iPhone.”
  8. 8 · AReload Other Devices — it should now list both devices, each with the name you gave it and its own IP/browser/OS metadata intact.
  9. 9 · ARevoke browser B's device.
  10. 10 · BTrigger any token refresh (reload the page, or wait for the access token to expire) — you should be signed out. The refresh token itself was deleted, not just a session record.

What to look for

  • Renaming a device does not wipe its other metadata fields (browser/os/ip/captured_at) — the PATCH merges rather than replaces.
  • Revoking deletes the refresh token, so the other browser can’t silently mint a new access token afterward.
  • This device list is scoped to the SPA application only — it won’t show devices from the Traditional or BFF demos even though they share a user.
3. Backend-for-Frontend
/demo/bff
Server-side token vault, Multi-Resource Refresh Tokens vs Standard RT, Device List.
  1. 1 · AOpen /demo/bff and sign in with the same test user.
  2. 2 · ABefore calling any API, read the MRRT Policy Comparison card at the top. It shows the BFF client has two policy entries (billing + inventory), while the SPA client has none — that structural difference is the entire MRRT feature.
  3. 3 · AClick Call Billing API. The Exchange Ledger appears: RT before, billing audience, RT after (rotated). The RT ID changed because rotation_type is rotating — the policy carries through to the new token.
  4. 4 · AClick Call Inventory API. A second row appears. The RT before on this row matches the RT after from the previous row — the chain badge confirms it is the same rotation lineage, now exchanged for a different audience.
  5. 5 · AClick 'Try unauthorized audience'. Auth0 will either reject the exchange or silently redirect it to the original audience — note the outcome banner and actual aud in the result. This proves the policy boundary is enforced.
  6. 6 · ACompare to the SPA demo — scroll to 'Standard RT — no MRRT policy' at the bottom. The SPA client has no policies field and can only exchange for one audience.
  7. 7 · ACheck Other Devices — one entry. Rename it, then confirm the name persists on refresh.
  8. 8 · BOpen /demo/bff and sign in with the same test user.
  9. 9 · AReload — Other Devices should now show two entries.
  10. 10 · ARevoke browser B's device.
  11. 11 · BReload the page — you should be redirected to log in again.

What to look for

  • MRRT policy comparison card shows the structural difference: BFF has refresh_token.policies[], SPA has nothing.
  • Exchange ledger: each row shows RT before, audience, RT after. The chain badge on row 2 confirms the rotation lineage is continuous.
  • Unauthorized audience test: Auth0 enforces the policy — the actual aud is NOT the requested audience.
  • Tokens never appear in browser storage or network responses — only the session cookie reaches the browser.
4. SPA with Online Refresh Tokens
Parked
Not yet testable — waiting on Auth0 support to enable the online_refresh_tokenstenant flag, which isn't self-service via the Management API on this tenant.
5. Unified Profile Page
/profile
Tenant-wide session view, My Account API profile edit, Fingerprint.com device enrichment.
  1. 1 · ASign into /demo/traditional. In the same browser, open /demo/bff — you should land directly on the BFF demo page without an Auth0 login prompt. Both apps share the same Authorization Server session.
  2. 2 · AVisit /profile. One session card appears showing both a "Traditional" and "BFF" badge with an "SSO shared" label. This is the same Auth0 AS session viewed through two application lenses.
  3. 3 · AA Fingerprint badge resolves next to the Sessions heading within a second or two. It shows a visitorId, visit count, and geolocation for this browser. In the background it fires a POST to /api/profile/fingerprint-link, writing the visitorId into the refresh token's metadata field.
  4. 4 · AExpand the session card. Under "Refresh tokens", find fingerprint_visitor_id — it now holds the Fingerprint visitorId. Under "Fingerprint visits", the last few visits for this browser are listed with timestamps and IPs, fetched from the Fingerprint Server API.
  5. 5 · AScroll past the session list to the "Fingerprint.com — This Device" section. It shows the full visit history table (timestamp, IP, browser, OS), geolocation (city/country/timezone), and — if Smart Signals are active on your Fingerprint plan — confidence score, bot detection, VPN and proxy flags. All of this is fetched server-side; the secret API key is never sent to the browser.
  6. 6 · AReload the profile page. The Fingerprint section visit count increments and a new row appears in the history. The visitorId in the header badge is identical — stable device identity across reloads and sessions.
  7. 7 · AClick the pencil icon next to your display name. Type a new name and press Enter. The change calls PATCH /api/profile/my-account with your own access token scoped to the Management API — not an M2M client_credentials grant. Reload the page; the new name persists because it is stored in user_metadata.display_name.
  8. 8 · BOpen /demo/traditional in a different browser and sign in with the same test user.
  9. 9 · AClick Refresh on the session list. A second card appears for browser B with its own device label, IP, and app badges.
  10. 10 · ARevoke browser B's session from its card. The single Revoke action removes the Auth0 session and all associated refresh tokens for that device.
  11. 11 · BNavigate anywhere in the demo — you are redirected to login. The revoke was driven entirely from browser A via the Management API; nothing was clicked in browser B.
  12. 12 · AFinally, click "Revoke all" next to the Sessions heading. Confirm the dialog. This calls POST /api/profile/sessions/revoke-all, which bulk-deletes every remaining session and refresh token for the user across the whole tenant, then redirects to logout — global sign-out from one button.

What to look for

  • One session card groups Traditional + BFF because they share an Auth0 AS session — the clients[] array on the session record is the source of truth.
  • Fingerprint visitorId is stored in refresh_token_metadata — visible in the expanded session card. The dedicated Fingerprint section fetches the full server-side signal set separately.
  • Smart Signals (bot/VPN/proxy/confidence) only appear if your Fingerprint plan includes them — the section degrades gracefully to visit history if they are absent.
  • My Account API name edit uses the user's own scoped access token. Inspect PATCH /api/profile/my-account in the Network tab — no M2M client_credentials, no admin token.
  • Revoke all is a true global logout: Management API bulk delete of every session and refresh token, not just a cookie clear.
6. SSO Isolation — High Trust App
/demo/high-trust
sso: false on the Auth0 client — logins never reuse an existing AS session.
  1. 1 · AConfirm you are signed into /demo/traditional in browser A. The Auth0 Authorization Server has an active session for this browser.
  2. 2 · AVisit /demo/high-trust. Auth0 presents a full login prompt despite the active session — it does not silently reuse it. This is sso:false in action: the client is permanently configured to opt out of tenant-wide SSO at the Authorization Server level.
  3. 3 · ASign in with the same test user. The High Trust page notes the other active sessions (Traditional / BFF) it detected in this browser, confirming both coexist.
  4. 4 · AVisit /profile. Two distinct session cards appear: one for "Traditional · BFF" labelled "SSO shared", and a separate one for "High Trust" labelled "isolated login". Same user, same browser, same IP — two independent Auth0 AS sessions.
  5. 5 · ARevoke only the High Trust session card. Reload /demo/high-trust — it requires login again. Reload /demo/traditional — it is unaffected. The two sessions are fully independent.
  6. 6 · ASign back into /demo/high-trust. Return to /profile and note the High Trust session card reappears as new — a fresh authenticated_at timestamp, new session ID, separate from the still-active Traditional session.

What to look for

  • sso:false is set on the Auth0 client record via Management API — not a runtime prompt parameter. The isolation is permanent and applies to every login, not just this session.
  • The profile page shows two session cards for the same user because they have different Auth0 session IDs — the unified view groups by session, not by user.
  • Revoking the High Trust session leaves the Traditional session completely untouched — there is no shared state to cascade through.
  • Use case: admin panels, payment flows, medical records — any surface where re-authenticating on every access is a compliance or security requirement regardless of existing SSO state.
7. On-Behalf-Of Token Exchange
/demo/obo
RFC 8693 delegation chain, identity preservation, AI agent pattern.
  1. 1 · AOpen /demo/obo and sign in with the test user.
  2. 2 · ARead the Delegation Chain diagram at the top. Note the two access tokens: AT1 is scoped to the middle-tier audience and carries the user identity; AT2 is the OBO result scoped to the orders audience with the same sub and a new act claim.
  3. 3 · AClick Run OBO Exchange. The exchange result card appears with two toggle buttons for AT1 and AT2.
  4. 4 · AOpen AT1 (incoming). Note sub = the user and aud = middle-tier audience.
  5. 5 · AOpen AT2 (OBO result). The highlighted box at the top shows: sub is unchanged (still the user), azp = the middle-tier service client, and act.sub records who performed the exchange. Open the full claims to confirm aud has changed to the orders audience.
  6. 6 · AClick Call Orders API (full flow). The act delegation chain section builds from top to bottom: the top node is the current token holder, the bottom is the original caller.
  7. 7 · ARead the Orders API response at the bottom — it was called with the OBO token. In a real system, the Orders API would use sub for row-level access control without trusting any out-of-band identity claim.

What to look for

  • sub is identical in AT1 and AT2 — the user identity is preserved through the exchange.
  • azp in AT2 is the middle-tier service client ID, not the user-facing app.
  • act claim records the delegation chain: who exchanged the token and on whose behalf.
  • The downstream Orders API never sees the original AT1 — it only sees the scoped, delegated AT2.
8. Custom Token Exchange
/demo/cte
Exchange any external token format for Auth0 AT + ID Token + RT via an Action.
  1. 1 · AOpen /demo/cte. No login — Custom Token Exchange IS the authentication. There is no browser redirect to Universal Login.
  2. 2 · ARead the flow diagram: External Partner JWT goes directly to Auth0 /oauth/token. A CTE Action validates it and calls setUser(). Auth0 returns a full AT + ID Token + RT.
  3. 3 · AEdit the external token fields — change the email to match your test user on the tenant (the Action maps by email). Click Exchange for Auth0 Tokens.
  4. 4 · AIn the External Partner JWT card, read the claims that were sent to Auth0. Note the iss is https://partner-idp.demo — an entirely external issuer Auth0 does not natively trust.
  5. 5 · AOpen the ID Token. Confirm sub is an Auth0 user ID (auth0|...) — the Action mapped the external identity to an existing Auth0 user. The email and name from the external JWT appear as standard OIDC claims.
  6. 6 · AOpen the Access Token. aud is the Auth0 middle-tier API — a fully functional Auth0 AT that can be used to call downstream APIs, despite the user never seeing a login screen.
  7. 7 · ATry changing the email to one that does not exist on the tenant. The exchange will fail with an error from the Action — demonstrating that CTE does not create new users; it only maps to existing ones unless the Action explicitly provisions them.

What to look for

  • No browser redirect — CTE is a pure server-side token endpoint call.
  • The incoming token format is completely custom (HS256 JWT with a demo issuer) — Auth0 never validates it; the Action does.
  • The resulting ID Token contains an Auth0 sub, not the external sub — the Action performed identity mapping.
  • If the CTE Profile is not yet created in the dashboard, the error card shows the exact manual step needed.
Cleanup

When you're done, delete the test user from the Auth0 dashboard (User Management → Users) rather than leaving it around — it keeps the tenant's user list legible for the next person running through this script.