fix(oidc): clear stale op_session cookie on authorize when sid -> no row #80

Merged
YousifShkara merged 1 commit from fix/stale-oidc-cookie-renders-404 into main 2026-06-09 04:50:13 +02:00
Owner

Users coming back to the platform after a few idle days were intermittently landing on a 404-shaped error from /oauth2/authorize (both Drillmark and Mokosh affected). The visible symptom is that logging out from Bunyip and signing back in clears the problem.

Trace: when a returning user's bunyip_op_session cookie is still in their browser (the 7-day Max-Age has not elapsed) but the corresponding op_sessions row is gone (the row also hard-expires at 7 days, was revoked by a logout-elsewhere, or got cleared by a recent migration), every /oauth2/authorize call sends the stale sid, load_op_session returns None, the handler 302s to /login?...&checked=1, and the browser still holds the stale cookie. login_get's BUNYIP-56 checked branch correctly renders the form, but the user is left in a state where any subsequent navigation to /oauth2/authorize (a separate tab, a back-button click, a different RP launcher) keeps repeating the same dance with no progress. Some browsers eventually surface that as a generic 404-shaped error page; the underlying URL stays on the authorize URL the screenshot captured.

The fix is to remove the stale cookie from the equation immediately. When /oauth2/authorize finds an op_session cookie that does not resolve to a live row, the 302-to-login response now also sets two Set-Cookie clears: a no-domain clear (matches any hostname-scoped api.<tld> cookie that pre-dates the COOKIE_DOMAIN config) and a Domain=.<tld> clear (matches the live shape). Access_token and refresh_token cookies are left alone: the hub session may still be valid and /login needs them to identify the returning user. The Set-Cookie clears land before the browser follows the redirect, so the next request goes through the clean "no cookie" branch.

Adds AuthCookies::clear_op_session_only(secure, cookie_domain) so the same pattern can be reused (logout handlers, future error paths) without re-deriving the two-axis clear shape.

Also adds structured tracing::info at the redirect site: client_id, redirect_uri, has_op_session_cookie, has_access_token, and which branch fired. The next time this happens we can correlate via request_id, not by guessing from the user's screenshot.

Tests pin the new helper covers both the no-domain and the with-domain shapes, asserts that access_token / refresh_token are never touched, and confirms domain-scoped + no-domain clears both appear when COOKIE_DOMAIN is set.

Follow-up: file a YT issue tracking long-term work to make BUNYIP-56's "checked" loop-breaker compose cleanly with this cookie-clear so the two together cover every stale-session path.

Users coming back to the platform after a few idle days were intermittently landing on a 404-shaped error from `/oauth2/authorize` (both Drillmark and Mokosh affected). The visible symptom is that logging out from Bunyip and signing back in clears the problem. Trace: when a returning user's `bunyip_op_session` cookie is still in their browser (the 7-day Max-Age has not elapsed) but the corresponding `op_sessions` row is gone (the row also hard-expires at 7 days, was revoked by a logout-elsewhere, or got cleared by a recent migration), every `/oauth2/authorize` call sends the stale sid, `load_op_session` returns `None`, the handler 302s to `/login?...&checked=1`, and the browser still holds the stale cookie. `login_get`'s BUNYIP-56 `checked` branch correctly renders the form, but the user is left in a state where any subsequent navigation to `/oauth2/authorize` (a separate tab, a back-button click, a different RP launcher) keeps repeating the same dance with no progress. Some browsers eventually surface that as a generic 404-shaped error page; the underlying URL stays on the authorize URL the screenshot captured. The fix is to remove the stale cookie from the equation immediately. When `/oauth2/authorize` finds an op_session cookie that does not resolve to a live row, the 302-to-login response now also sets two Set-Cookie clears: a no-domain clear (matches any hostname-scoped `api.<tld>` cookie that pre-dates the COOKIE_DOMAIN config) and a `Domain=.<tld>` clear (matches the live shape). Access_token and refresh_token cookies are left alone: the hub session may still be valid and `/login` needs them to identify the returning user. The Set-Cookie clears land before the browser follows the redirect, so the next request goes through the clean "no cookie" branch. Adds `AuthCookies::clear_op_session_only(secure, cookie_domain)` so the same pattern can be reused (logout handlers, future error paths) without re-deriving the two-axis clear shape. Also adds structured `tracing::info` at the redirect site: `client_id`, `redirect_uri`, `has_op_session_cookie`, `has_access_token`, and which branch fired. The next time this happens we can correlate via request_id, not by guessing from the user's screenshot. Tests pin the new helper covers both the no-domain and the with-domain shapes, asserts that `access_token` / `refresh_token` are never touched, and confirms domain-scoped + no-domain clears both appear when COOKIE_DOMAIN is set. Follow-up: file a YT issue tracking long-term work to make BUNYIP-56's "checked" loop-breaker compose cleanly with this cookie-clear so the two together cover every stale-session path.
fix(oidc): clear stale op_session cookie on authorize when sid -> no row
All checks were successful
Create release / Create release from merged PR (pull_request) Has been skipped
Check / fmt / clippy / build / test (pull_request) Successful in 2m35s
df379de71b
Users coming back to the platform after a few idle days were intermittently landing on a 404-shaped error from `/oauth2/authorize` (both Drillmark and Mokosh affected). The visible symptom is that logging out from Bunyip and signing back in clears the problem.

Trace: when a returning user's `bunyip_op_session` cookie is still in their browser (the 7-day Max-Age has not elapsed) but the corresponding `op_sessions` row is gone (the row also hard-expires at 7 days, was revoked by a logout-elsewhere, or got cleared by a recent migration), every `/oauth2/authorize` call sends the stale sid, `load_op_session` returns `None`, the handler 302s to `/login?...&checked=1`, and the browser still holds the stale cookie. `login_get`'s BUNYIP-56 `checked` branch correctly renders the form, but the user is left in a state where any subsequent navigation to `/oauth2/authorize` (a separate tab, a back-button click, a different RP launcher) keeps repeating the same dance with no progress. Some browsers eventually surface that as a generic 404-shaped error page; the underlying URL stays on the authorize URL the screenshot captured.

The fix is to remove the stale cookie from the equation immediately. When `/oauth2/authorize` finds an op_session cookie that does not resolve to a live row, the 302-to-login response now also sets two Set-Cookie clears: a no-domain clear (matches any hostname-scoped `api.<tld>` cookie that pre-dates the COOKIE_DOMAIN config) and a `Domain=.<tld>` clear (matches the live shape). Access_token and refresh_token cookies are left alone: the hub session may still be valid and `/login` needs them to identify the returning user. The Set-Cookie clears land before the browser follows the redirect, so the next request goes through the clean "no cookie" branch.

Adds `AuthCookies::clear_op_session_only(secure, cookie_domain)` so the same pattern can be reused (logout handlers, future error paths) without re-deriving the two-axis clear shape.

Also adds structured `tracing::info` at the redirect site: `client_id`, `redirect_uri`, `has_op_session_cookie`, `has_access_token`, and which branch fired. The next time this happens we can correlate via request_id, not by guessing from the user's screenshot.

Tests pin the new helper covers both the no-domain and the with-domain shapes, asserts that `access_token` / `refresh_token` are never touched, and confirms domain-scoped + no-domain clears both appear when COOKIE_DOMAIN is set.

Follow-up: file a YT issue tracking long-term work to make BUNYIP-56's "checked" loop-breaker compose cleanly with this cookie-clear so the two together cover every stale-session path.
YousifShkara deleted branch fix/stale-oidc-cookie-renders-404 2026-06-09 04:50:13 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/bunyip!80
No description provided.