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

OPENID CONNECT · LAB

Separate the provider session, the app session and the ID token

Shorten the ID token to one minute and the tenant session to fifteen, then show that an expired ID token ends nothing and that single sign-on lasts exactly as long as the provider's session.

ReadyUses your lab tenant

The lesson

Builds on: Connecting a sign-in to an account.

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

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 ID token lifetime

    Recorded as tenant.oauth.id_token_managers.update succeeded.

  2. Shorten the browser session lifetime

    Recorded as tenant.oauth.policy.update succeeded.

  3. Sign in on the tenant's own page

    Recorded as account.sign_in succeeded (signed_in) about [email protected].

  4. Get a code from the provider session with no page

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

  5. Use the access token once at UserInfo

    Recorded as oidc.userinfo succeeded (userinfo_served).

  6. See prompt=none fail after the provider session ends

    Recorded as oauth.authorize rejected (login_required) for lab-collage.

Setup

Three clocks run in every sign-in: the provider's session, the relying party's session and the ID token. All three are real here, and two of them are tenant settings.

  1. Press Start on this page.

  2. In Lab Photos, open OAuth > ID token managers, open the manager assigned to lab-collage, note its lifetime, and set it to 60 seconds.

  3. Open OAuth > Flow policy, note the browser session lifetime, and set it to 900 seconds, the minimum.

  4. In a private window, open $ISSUER/account and sign out if a session is open. In your terminal, source ~/btl-oidc.sh.

Walkthrough

  1. Watch the provider defend against session fixation. In the private window open $ISSUER/login and the developer tools' cookie view. Note any __Host-btl-oauth-session value (there may be none). Sign in as Ava and look again: a new value, with Secure, HttpOnly, SameSite=Lax and Path=/. Write down the time.

Why it matters: a session identifier that existed before sign-in must never become the signed-in session. The lesson's printer replaces its anonymous session the same way.

  1. Run a sign-in in the same window. No page appears, because the session from step 1 answers.

signin
redeem '<code>'
btl-lab verify "$ID_TOKEN" --issuer "$ISSUER" --audience "$CLIENT_ID" --type id --nonce "$NONCE"
part "$ID_TOKEN" | jq '{iat, exp, auth_time, life: (.exp - .iat), acr, sid}'
FIRST_AUTH_TIME=$(part "$ID_TOKEN" | jq .auth_time)

life is 60. auth_time is the step 1 sign-in, earlier than iat. acr and sid are null.

Why it matters: your session record stores auth_time, and records "none in this ID token" for acr and sid rather than inventing them.

  1. Use the access token once, then drop it.

curl -s -H "Authorization: Bearer $TOKEN" "$ISSUER/oidc/userinfo" | jq '{sub, email}'
unset TOKEN

Why it matters: a sign-in that only needed the profile has no further use for the access token, so it is not kept.

  1. Build your app session record from the validated token. It holds an identifier pointing to server-side state, never the token's claims in a cookie.

NOW=$(date +%s)
jq -n --arg sub "$(part "$ID_TOKEN" | jq -r .sub)" --arg iss "$ISSUER" --argjson at "$FIRST_AUTH_TIME" --argjson now "$NOW" \
  '{session: "s-local-1", account: "acct-8812", issuer: $iss, subject: $sub, auth_time: $at, acr: null, sid: null,
    created: $now, idle_deadline: ($now + 7200), absolute_deadline: ($now + 604800)}' > app-session.json

Why it matters: the session's own idle and absolute deadlines are the relying party's decision, based on what it protects.

  1. Wait 90 seconds and validate the same ID token again: btl-lab verify now fails on exp. Your app-session.json is still within both deadlines.

Why it matters: the ID token's lifetime limits how long you may accept it as evidence of one sign-in. It says nothing about how long anyone stays signed in.

  1. Still inside fifteen minutes of step 1, ask silently:

signin prompt=none

The listener receives a code with no page in between. Redeem it: the new token has a new iat and the same auth_time as FIRST_AUTH_TIME.

Why it matters: the provider's session is independent of your app's session and of any token's exp.

  1. Wait until more than fifteen minutes have passed since step 1 and run signin prompt=none again. The listener prints error=login_required with your state and iss.

Why it matters: single sign-on ends when the provider's session policy says so, and only the provider controls that clock. Your app session in app-session.json is unaffected either way.

Break it

  1. Test the return-page check from the lesson with inputs that must be refused. Resolve each value against your origin and keep it only if the origin is unchanged:

for p in /books/new //other-site.example https://other-site.example/books; do
  node -e 'const u=new URL(process.argv[1],"http://127.0.0.1:8765/");console.log(process.argv[1], u.origin==="http://127.0.0.1:8765"?"keep "+u.pathname:"fall back to /")' "$p"
done

/books/new is kept. The other two leave your origin and fall back to the home page. The correct design records the path before the redirect and never reads the destination from the callback, so this check is the second line of defense. Nothing in the tenant was weakened.

Check your work

Press Check my progress. The checks look for the two lifetime changes, Ava's sign-in on the tenant's own page, a code issued with no new sign-in, one UserInfo call, and a login_required answer once the provider session expired.

In your notes: exp - iat = 60, and the silent token's auth_time equals FIRST_AUTH_TIME.

Cleanup

  1. Restore the ID token lifetime and the browser session lifetime you noted in Setup.

  2. Delete app-session.json.

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