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

Front-channel logout through a hidden frame

Plan a front-channel logout page that the tenant loads in a hidden frame with iss and sid, and today prove that a cross-site frame does not carry the tenant's SameSite=Lax session cookie.

PlannedUses your lab tenant

The lesson

Builds on: Local logout and provider sessions.

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

Your tenant has no frontchannel_logout_uri registration and no signed-out page with frames yet (G17). The browser behavior that makes front-channel logout unreliable can be shown for real today.

  1. source ~/btl-oidc.sh, sign Ava in to Lab Photos in your lab browser (a normal window, not private, so the frame test in Do today uses the same cookies), and run btl-lab callback before each request.

  2. Once G17 exists: on lab-collage and on a second app such as lab-tmp-slideshow, register the front-channel logout URI http://127.0.0.1:8765/logout/front-channel with "session required" on. It shares the scheme, host and port of their redirect URIs.

Planned walkthrough

  1. Sign in to both clients through one tenant session, and store iss and sid from each ID token in that app's local session record:

part "$ID_TOKEN" | jq '{iss, sub, sid}'

Why it matters: the sid stored at sign-in is what a later logout request is matched against.

  1. Sign out from lab-collage with RP-initiated logout (Ask the provider to end its session). The tenant's signed-out page embeds one hidden frame per client.

  2. The listener logs GET /logout/front-channel?iss=...&sid=... once for each client. Your handler checks iss exactly, ends the sessions whose stored iss and sid match, and answers with Cache-Control: no-store and Content-Security-Policy: frame-ancestors $ISSUER.

Why it matters: matching sid limits an ordinary GET to the session the provider named, so a stranger's page cannot sign anyone out.

  1. Run it again and close the tab the moment the signed-out page appears. One client is never told, and the tenant cannot know.

Why it matters: nobody retries a front-channel request, because nobody learns that it failed.

Do today

  1. Read the tenant's own session cookie in the developer tools: __Host-btl-oauth-session with Secure, HttpOnly and SameSite=Lax.

  2. A silent check at the top level:

signin prompt=none

Open the URL directly in the signed-in window. The listener receives a code.

  1. The same request in a cross-site frame. Write a fresh request into a local page and open the file in the same browser:

printf '<p>Framed silent check</p><iframe src="%s" width="600" height="200"></iframe>\n' "$(signin prompt=none)" > frame.html

The frame shows nothing useful, because tenant pages refuse to be framed (X-Frame-Options: DENY and frame-ancestors 'none'). The request still reached the tenant, though, and Audit shows oauth.authorize rejected login_required for lab-collage although Ava is signed in.

Why it matters: inside a frame on another site the tenant is a third party, so the browser keeps its Lax cookie at home. A front-channel logout page framed by a provider on another site loses its cookies the same way, and only a server-side lookup by iss and sid can still find the session.

  1. Close the loop with the lesson: write down which of your own apps keep their sessions on the server (they could still act on iss and sid) and which keep them only in the browser (they could not).

Break it

Planned, once G17 exists: load your front-channel logout page with a sid that belongs to no session. Nothing matches and nothing ends, and the page still answers 200 with Cache-Control: no-store.

Check your work

Today: the top-level code and the framed login_required rejection for lab-collage in Audit, a few seconds apart, while Ava stayed signed in.

Once G17 exists, both local session records are gone after one sign-out, and the tenant records the logout.

Cleanup

Delete frame.html. Once G17 exists, remove the front-channel logout URIs.

Missing infrastructure

  • G17 (OIDC logout). frontchannel_logout_uri and frontchannel_logout_session_required per client, sid in ID tokens, per-session tracking of signed-in clients, and a signed-out page that renders one hidden frame per client. With it, the planned walkthrough runs as written.

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