← Debugging Guides

阅读中文版本

Third-Party Cookies Blocked — Iframe Login State Silently Lost

Debug cross-site iframes losing session cookies as browsers phase out third-party cookies, verify with partitioned storage tests, and migrate to token patterns.

intermediatedebuggingbrowsercookiesauthentication

Symptoms

  • An embedded iframe (payment form, SSO widget, embedded app) logs in successfully but shows logged-out state on next interaction
  • Reproduces in Safari and Chrome with third-party cookie blocking, works in profiles where blocking is disabled
  • No errors appear: the cookie simply is not sent with the iframe requests

Possible causes

  • The session cookie is set in a third-party context, which modern browsers block or partition by default
  • SameSite=Lax or Strict cookies are never sent in cross-site iframe requests regardless of third-party policy
  • Storage APIs in the iframe are partitioned, so prior "localStorage session" reads fail silently
  • The embedded flow depends on top-level navigation semantics that iframe contexts do not provide

Diagnosis steps

Compare cookie behavior across profiles

Chrome: Settings → Privacy → "Block third-party cookies" toggle; Safari default blocks

Interpretation

Symptom present with blocking on and absent with it off is the third-party cookie dependency proven in two minutes.

Inspect what the iframe requests actually carry

DevTools → Network → filter the iframe origin → check the Cookies column on its requests

Interpretation

Requests without the session cookie confirm non-delivery; SameSite column shows whether policy or blocking is the reason.

Audit cookie attributes

curl -sI https://widget.example.com/login | grep -i "set-cookie"

Interpretation

Missing SameSite=None, missing Secure, or a session scoped to top-level context explains the silence.

Root cause

Browsers isolate cookies by top-level site: a cookie set inside a cross-site iframe belongs to a third-party context, which is blocked or partitioned by default now. The iframe therefore performs authenticated requests without any session cookie attached, and the server treats each request as anonymous.

Fix

Move session establishment out of cookie-in-iframe: pass short-lived tokens via postMessage to the iframe origin, use Storage Access API where user interaction is acceptable, or restructure the flow so authentication happens in a top-level popup and returns a token. Set SameSite=None; Secure only for cookies that genuinely must cross sites.

Risk note

Tokens via postMessage widen the attack surface if origins are not strictly validated on both ends. Allowlist exact origins and validate every message.

Verify the fix

With third-party blocking forced on, the iframe flow authenticates via token exchange, requests carry the token, and the session persists across reloads in Safari and Chrome.

Symptom walkthrough

The silence is the signature — no error, no failed request, just anonymous behavior:

DevTools → Network → iframe requests to widget.example.com
  POST /api/session    Cookies: (none)

The iframe logged in moments ago, and now its API calls carry no cookie at all. The two-minute experiment settles attribution: toggle the browser third-party cookie setting. Works with blocking off, anonymous with blocking on — the dependency is proven. The SameSite column adds the second layer: even without blocking, Lax and Strict cookies never ride cross-site iframe requests at all.

Fix in practice

Token handoff replaces cookie inheritance:

// parent (app.example.com):
const iframe = document.querySelector('iframe');
iframe.addEventListener('load', () => {
  iframe.contentWindow.postMessage(
    { type: 'auth', token: shortLivedToken },
    'https://widget.example.com'   // exact origin, never '*'
  );
});

// iframe (widget.example.com):
window.addEventListener('message', (event) => {
  if (event.origin !== 'https://app.example.com') return; // validate
  if (event.data?.type === 'auth') startSession(event.data.token);
});

For storage-dependent widgets, the Storage Access API requests user permission for unpartitioned access — acceptable when a visible interaction exists. And for heavy flows, restructure: top-level popup for authentication, postMessage the token back, iframe consumes it. Both ends of every postMessage must validate origins; a wildcard target turns a session fix into a session leak.