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

IDENTITY FUNDAMENTALS · LAB

Watch a session start, change identifier and end

Read your tenant's real session cookie, prove its identifier is replaced when sign-in completes, show that clearing a cookie is not signing out, and end a session on the server.

Partly readyUses your lab tenant

The lesson

Builds on: How applications communicate.

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. Shorten the browser session lifetime

    Recorded as tenant.oauth.policy.update succeeded.

  2. Finish Ava's sign-in with her second step

    Recorded as account.second_step succeeded (second_step_completed) about [email protected].

  3. Get a code for the printer using only the session cookie

    Recorded as oauth.authorize succeeded (code_issued) for lab-printer about [email protected].

  4. End the session on the server

    Recorded as account.sign_out succeeded (signed_out).

  5. Change Ava's password as an administrator

    Recorded as tenant.users.credentials.set succeeded about [email protected].

Setup

You need Ava with her authenticator app and no management roles, lab-printer with Ava's consent, and the toolkit.

  1. On this lab page choose Lab Photos and press Start.

  2. Flow policy: write down the current browser session lifetime, then set it to 900 seconds. Save.

  3. In the shell, set the variables and helpers from the How applications communicate lab:

ISSUER=https://tenant-<id>.beyondthelogin.dev
CLIENT_ID=<lab-printer client ID>
REDIRECT=http://127.0.0.1:8765/callback
authz() { echo "$ISSUER/oauth/authorize?response_type=code&client_id=$CLIENT_ID&redirect_uri=$(node -p 'encodeURIComponent(process.argv[1])' "$REDIRECT")&scope=$1&state=$STATE&nonce=$NONCE&code_challenge=$CHALLENGE&code_challenge_method=S256"; }
fresh() { eval "$(btl-lab pkce)"; eval "$(btl-lab state)"; }
check() { for s in "$@"; do curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' -H "Cookie: __Host-btl-oauth-session=$s" "$ISSUER/account"; done; }

Note: you will copy Ava's session identifiers into the shell. They belong to a test user, never leave your machine, and stop working by the end of the lab.

Walkthrough

  1. Open a private window and DevTools > Application > Cookies for your tenant host. Open $ISSUER/login. There is a __Host-btl-signin cookie, which binds email codes and links to this browser, but no session cookie yet.

  2. Enter Ava's email and password. On the authenticator page a __Host-btl-oauth-session cookie appears. Copy its value:

read -rs STAGED
  1. Enter the authenticator code. The __Host-btl-oauth-session value is now different. Copy it too, and read its attributes in DevTools: Path=/, Secure, HttpOnly, SameSite=Lax, an expiry about 15 minutes away, and no Domain.

read -rs SESSION

Why it matters: these are the attributes from the lesson's Set-Cookie example. The __Host- prefix keeps the cookie on this exact host.

  1. Compare the two identifiers:

check "$STAGED" "$SESSION"

Expected: 303 to /login?continue=%2Faccount for the staged value and 200 for the new one.

Why it matters: the identifier that existed before sign-in completed no longer works. That is the defense against session fixation: a new identifier on authentication, and the old one invalidated on the server, not just replaced in the browser.

  1. Connect a separate request to the earlier sign-in:

fresh; curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' -H "Cookie: __Host-btl-oauth-session=$SESSION" "$(authz openid)&prompt=none"

Expected: 303 to your redirect URI with a code, and no password asked.

Why it matters: the cookie, not a new password, tells the server that this request belongs to Ava's earlier sign-in.

  1. In the DevTools Console run document.cookie. The session cookie is not listed.

Why it matters: HttpOnly keeps page scripts from reading the session identifier.

  1. In DevTools, delete the __Host-btl-oauth-session cookie and reload $ISSUER/account: the sign-in page appears. Now run check "$SESSION": still 200.

Why it matters: clearing the browser's copy did not end the session. A stolen copy would still work.

  1. End it on the server. This is the request the Sign out button on /account sends:

curl -si -X POST "$ISSUER/logout" -H "Origin: $ISSUER" -H "Cookie: __Host-btl-oauth-session=$SESSION" | grep -iE '^(HTTP|location|set-cookie)'
check "$SESSION"

Expected: 303 to /login with set-cookie: __Host-btl-oauth-session=; ... Max-Age=0, and then 303 to sign-in for the old value. Audit shows account.sign_out succeeded with reason signed_out.

Why it matters: signing out invalidated the server's session record as well as removing the browser's cookie.

  1. Absolute lifetime: sign in again and either keep using /account or leave the tab alone. Either way, 15 minutes after sign-in, /account sends you to sign in. If you do not want to wait, read the step and move on.

Why it matters: this setting is an absolute timeout counted from sign-in. Activity does not extend it. The lesson's idle timeout is not available as a setting yet (G42).

  1. Sessions ended by the service: sign in as Ava again and copy the new cookie with read -rs SESSION. As yourself, choose Set password on Ava's record and give her a new password. Run check "$SESSION": 303 to sign-in.

Why it matters: the service, not the browser, decides whether a session is still valid. A password change ends Ava's sessions and revokes her tokens.

Planned walkthrough

These steps need an idle timeout setting (G42) and an administrator session list (G30).

  1. Flow policy: set the idle timeout to 5 minutes and the absolute lifetime to 60 minutes.

  2. Sign in as Ava in two private windows. Keep using one; leave the other alone. After 5 minutes the idle window is sent to sign in while the active one continues. After 60 minutes both end.

  3. On Ava's record open Sessions. Each session shows when it was created, when it was last used and which browser holds it. End one session without changing her password; only that browser is signed out, and Audit records who ended it.

Break it

  1. Sign in again, copy the cookie with read -rs SESSION, and send the sign-out request from step 8 without the Origin header. The response still redirects to /login, but check "$SESSION" shows the session is valid.

Why it matters: the server refuses state-changing requests that did not come from its own pages, so another site cannot sign Ava out or act with her cookie.

Check your work

Press Check my progress. It looks for:

  • tenant.oauth.policy.update succeeded (the shorter lifetime).

  • account.second_step succeeded with reason second_step_completed for Ava.

  • oauth.authorize succeeded with reason code_issued for lab-printer and Ava, with no sign-in in between (step 5).

  • account.sign_out succeeded with reason signed_out.

  • tenant.users.credentials.set succeeded for Ava.

Cleanup

  1. Flow policy: restore the browser session lifetime you wrote down.

  2. Store Ava's new password in your password manager and run unset STAGED SESSION.

Missing infrastructure

  • G42 Session idle timeout. The tenant has only an absolute browser session lifetime. With an idle setting, the planned steps show an idle session ending while an active one survives, and the absolute cap ending both.

  • G30 Administrator session listing and kill-session. There is no view of a user's active sessions and no way to end one session. Today step 10 ends all of Ava's sessions with a password change 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