Authentication · intermediate

How to verify session cookie behavior across the login redirect

This guide explains how to verify that a session cookie is actually attached to the redirect callback and the subsequent API boundary after login. The failure mode is precise: the browser must send the session cookie at the intended callback and API boundaries, and SameSite classification quietly determines whether that delivery happens at all. We treat verification, not configuration, as the deliverable.

The symptoms

  • After a successful credential POST, the user lands on the callback or post-login page and is treated as anonymous: protected UI elements re-render the login form or return to the login screen.
  • API requests issued immediately after the redirect from the OAuth or SAML callback return 401 or 302 to /login, even though the login IdP exchange succeeded server-side.
  • The Set-Cookie header is present in the login response in DevTools, but Application > Cookies shows the cookie with no value on the callback origin, or the cookie exists with a value but is not sent on the next cross-site navigation.
  • Behavior differs between top-level navigations and embedded iframes or popup flows, where the cookie is sent in one and not the other.
  • The session works for several minutes of normal in-app navigation and then fails on a hard reload or when a tab is restored, indicating the cookie was set but not marked for the right scope.

Likely causes

  • SameSite=None was not paired with Secure, so modern browsers reject the cookie on cross-site callback delivery and silently drop it.
  • SameSite=Lax is set on the session cookie, but the post-login flow relies on a cross-site POST or an embedded iframe callback, which Lax does not include on initial navigation.
  • Domain or Path scoping places the session cookie on a subdomain or path that does not match the API origin, so the callback page has the cookie but the API call from a different path or host does not.
  • The cookie is set on the IdP or auth-frontend origin rather than the application origin, and the redirect cross-origin never carries it forward.
  • Partitioned or third-party cookie controls in the browser drop a cookie that is not explicitly marked SameSite=None; Secure, so the callback's first-party storage is empty.

First ten minutes

  1. 01Open DevTools and reproduce the login once with the Network panel open, filtering by the login endpoint, the callback URL, and the first authenticated API call. Record the exact origin of each response.
  2. 02Inspect the Set-Cookie response header on the login response. Write down the cookie name, Domain, Path, Expires/Max-Age, SameSite, Secure, HttpOnly, and Partitioned attributes verbatim.
  3. 03In Application > Cookies, locate the cookie on the callback origin and on the API origin. Note which origins hold the cookie, with which value, and which do not.
  4. 04Observe whether the first authenticated request after the redirect actually includes a Cookie header. Compare the request headers of a navigation that works versus one that does not.
  5. 05Disable browser extensions, private/incognito mode flags, and any privacy or tracking protection settings for this site, then repeat once to rule out client-side suppression of cookies.
  6. 06Capture a HAR or the raw response headers of the login exchange and the callback navigation so the verification is repeatable and is not dependent on a single live run.

Evidence to collect

  • The exact Set-Cookie attribute string from the login response, including casing of SameSite, presence of Secure, and Domain/Path values.
  • The request headers of the post-callback API call, specifically whether a Cookie header is present and which cookies it carries.
  • The list of cookies visible in Application > Cookies for the application origin versus the API origin versus the IdP origin after the login completes.
  • A HAR file or saved response trace from one passing flow and one failing flow, so the redirect chain and Set-Cookie events can be diffed.
  • Any browser warnings shown in the console or DevTools about cookies being blocked because of SameSite or because they were not marked Secure.

Where to look

  • DevTools > Network > the login response that issues Set-Cookie, and the 302 or 303 that follows it to the callback.
  • DevTools > Application > Storage > Cookies, comparing entries on the application origin, the API origin, and any parent or subdomain origin used by the auth flow.
  • DevTools > Console, where browsers surface SameSite=None; Secure requirement failures and partitioned-cookie refusals.
  • The HTTP boundary between the IdP and the application, specifically the redirect that lands on the application origin after token exchange.
  • The HTTP boundary between the callback page and the first authenticated API call, where the cookie must travel in-band with the request.

Diagnostic steps

  1. 01Confirm the cookie attributes on the login response: read the SameSite, Secure, Domain, and Path fields directly from the response header and note their values.
  2. 02Confirm the cookie's scope matches both the callback URL and the API URL: if Domain is example.com but the API lives at api.example.com, the cookie will not attach; if Path is /auth/ but the API is at /api/, the cookie will not attach.
  3. 03Confirm whether the flow is cross-site from the browser's perspective: if the IdP origin differs from the application origin by eTLD+1, the post-login redirect is a cross-site navigation and SameSite=Lax cookies are not sent on the initial request to the application origin.
  4. 04Confirm the navigation type: a top-level redirect allows Lax cookies to be set on the response, while an embedded iframe or popup callback constrains which cookies are allowed and may require SameSite=None; Secure.
  5. 05Confirm the request method of the first API call: GETs to the application origin after a top-level redirect can use a Lax cookie, but POSTs or subresource requests rely on the cookie already being present from a prior navigation or on SameSite=None; Secure.
  6. 06Confirm browser-side defenses: open the same flow in a private window with default settings and in a non-private window, and compare whether the cookie appears in Application > Cookies and whether it is sent on the next request.
  7. 07Confirm Partitioned behavior: if the cookie is marked Partitioned, it is bound to the top-level site of the navigation; if the callback is opened in a different top-level site than the API calls, the cookie will not be sent on those calls.

Common mistakes

  • Assuming that "the cookie was set" implies "the cookie was sent": Set-Cookie on the login response is only half of the contract; the request that follows must include a Cookie header with the same name and value.
  • Changing SameSite to None without also setting Secure, which causes modern browsers to drop the cookie entirely rather than relaxing its scope.
  • Configuring SameSite on a cookie the application does not control, for example a session cookie set by an upstream IdP, and then concluding that the application is misconfigured.
  • Conflating the cookie's Domain attribute with the application's logical domain: a cookie set on .example.com is sent to api.example.com, but a cookie set on example.com is not.
  • Relying on Lax semantics for an embedded iframe callback or for a cross-site POST that must carry the session cookie; Lax does not cover either of these cases.
  • Drawing conclusions from a single browser vendor: cookie delivery differs between top-level and third-party contexts, and between strict tracking protection modes.

Safe fixes

  • If the cookie has SameSite=None without Secure, mark it Secure and ensure the entire login and callback chain is served over HTTPS, then re-verify the cookie appears in Application > Cookies on the application origin.
  • If the flow is cross-site and the callback must set or carry the session cookie, evaluate whether SameSite=None; Secure is appropriate for the IdP or auth-frontend's session cookie, since Lax is intentionally restrictive for cross-site contexts.
  • If the cookie's Domain does not match the API origin, narrow or correct the Domain attribute so the cookie is in scope for both the callback page and the API calls that follow.
  • If the cookie's Path does not include the API path, set Path=/ so the cookie is in scope for every route, or align Path with the actual API base path.
  • If the cookie is Partitioned and the API calls are made from a different top-level site than the callback, either remove the Partitioned attribute or align the top-level sites so the partition key matches.
  • Only after evidence: introduce a second, verification-only cookie with a known value, set via Set-Cookie, and confirm it is present at the boundary before relying on the production session cookie's attributes.

Prove the fix

  1. 01In DevTools > Network, the login response's Set-Cookie header shows the intended attribute set, and the post-redirect request to the callback origin includes a Cookie header carrying that cookie.
  2. 02In Application > Cookies on the application origin, the session cookie is present with a non-empty value immediately after the callback completes, and on the API origin if the API is a different host or path that should also be in scope.
  3. 03The first authenticated API request issued after the redirect is sent with the session cookie in its Cookie header and returns a 2xx response rather than 401 or a 302 to the login page.
  4. 04The DevTools console does not report any SameSite or Secure warnings for the session cookie on the login response.
  5. 05A repeat of the same flow in a private window with default browser settings, and in a second browser engine, produces the same cookie attributes, the same cookie delivery, and the same authenticated result.
  6. 06A HAR file from one verified pass is checked into the verification artifact set so future regressions in cookie delivery are detectable against a known good trace.

Prevention and next steps

  • Treat cookie attributes as part of the login contract: the Set-Cookie attribute set, the callback origin, and the API origin must all be reviewed together whenever the auth flow changes.
  • Add an automated check that parses the login response's Set-Cookie header and asserts the presence of the expected SameSite, Secure, Domain, and Path values before deploying auth changes.
  • Keep a canonical HAR of a passing login flow under version control and diff it against HARs from staging or production whenever cookie behavior changes.
  • Document which origin owns the session cookie and which origins must receive it, and ensure the cookie's Domain and Path are explicit rather than left to framework defaults.

Safe commands and checks

browser_devtools_network_filter_login_response: filter the Network panel by the login endpoint and the callback URL, then export the HAR for offline inspection of Set-Cookie and Cookie request headers.
browser_devtools_application_cookies_export: in Application > Storage > Cookies, select the application origin and the API origin, and record the visible cookie names, values, and attributes for the session cookie.
browser_devtools_console_filter_cookie_warnings: filter the Console by cookie and SameSite warnings to capture any browser-side rejections of the session cookie on the login response.
browser_repeat_login_private_window: repeat the login in a private window with default settings, re-export the HAR, and diff it against the verified-good HAR to confirm parity.
browser_repeat_login_second_engine: repeat the login in a second browser engine, re-export the HAR, and confirm the same cookie attributes and same first authenticated API request succeed.
server_log_filter_set_cookie: in the auth service's access log, filter for the login endpoint's responses and confirm each response issued the expected Set-Cookie attribute set, with no truncation by intermediaries.