fix(oidc): silent SSO on /authorize via hub access_token cookie #89

Merged
YousifShkara merged 1 commit from fix/oidc-silent-sso-via-access-token into main 2026-06-10 02:24:09 +02:00
Owner

Two user-visible bugs collapsed into one root cause: bunyip's /oauth2/authorize only consulted the op_session row when deciding whether to mint a code. If op_session was missing or stale, it 302'd straight to /login - even though the hub access_token cookie (HS256 JWT signed by JwtService) was sitting in the request headers, perfectly valid, identifying the same user.

Concrete impact:

  • DMARC-21: A user comes back to drillmark after a few days. The op_session DB row has expired (7-day TTL) but the 30-day refresh_token cookie is keeping the 15-min access_token cookie fresh on bunyip-web visits. drillmark fires /authorize -> no op_session -> 302 /login. The user types creds for no reason; on bad days the SPA's 60s poll then collapses the chain into the redirect loop they filed.

  • Bunyip app launcher: User is in bunyip-web with a valid access_token cookie (they just logged in). Clicks "Open Drillmark" or "Open Mokosh". The new-tab navigation lands on the RP, which fires /authorize, which finds no op_session, which 302s to /login. They re-enter the password they typed five seconds ago.

This patch adds a silent-SSO fallback to authorize: when no op_session resolves, read the access_token cookie, verify it through JwtService::verify_access_token, and on success mint a fresh op_session for that user and attach the new bunyip_op_session cookie to the eventual code-mint 302. The existing "no cookie -> /login" path is untouched and runs whenever the silent path declines (no access_token cookie present, JWT expired, signature failed).

Security posture is unchanged from the existing /v1/auth/login -> create_op_session path: AuthCookies::clear already removes the access_token cookie on logout, and revoke_op_sessions runs synchronously alongside, so a logged-out user has neither input available to the silent branch. The 15-min access_token TTL bounds the worst-case window for a stolen cookie identically to every other consumer of that cookie.

#DMARC-21

Two user-visible bugs collapsed into one root cause: bunyip's `/oauth2/authorize` only consulted the op_session row when deciding whether to mint a code. If op_session was missing or stale, it 302'd straight to /login - even though the hub access_token cookie (HS256 JWT signed by JwtService) was sitting in the request headers, perfectly valid, identifying the same user. Concrete impact: - DMARC-21: A user comes back to drillmark after a few days. The op_session DB row has expired (7-day TTL) but the 30-day refresh_token cookie is keeping the 15-min access_token cookie fresh on bunyip-web visits. drillmark fires /authorize -> no op_session -> 302 /login. The user types creds for no reason; on bad days the SPA's 60s poll then collapses the chain into the redirect loop they filed. - Bunyip app launcher: User is in bunyip-web with a valid access_token cookie (they just logged in). Clicks "Open Drillmark" or "Open Mokosh". The new-tab navigation lands on the RP, which fires /authorize, which finds no op_session, which 302s to /login. They re-enter the password they typed five seconds ago. This patch adds a silent-SSO fallback to `authorize`: when no op_session resolves, read the `access_token` cookie, verify it through `JwtService::verify_access_token`, and on success mint a fresh op_session for that user and attach the new bunyip_op_session cookie to the eventual code-mint 302. The existing "no cookie -> /login" path is untouched and runs whenever the silent path declines (no access_token cookie present, JWT expired, signature failed). Security posture is unchanged from the existing `/v1/auth/login -> create_op_session` path: AuthCookies::clear already removes the access_token cookie on logout, and revoke_op_sessions runs synchronously alongside, so a logged-out user has neither input available to the silent branch. The 15-min access_token TTL bounds the worst-case window for a stolen cookie identically to every other consumer of that cookie. #DMARC-21
fix(oidc): silent SSO on /authorize via hub access_token cookie
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 1m32s
ab463fd5e2
Two user-visible bugs collapsed into one root cause: bunyip's `/oauth2/authorize` only consulted the op_session row when deciding whether to mint a code. If op_session was missing or stale, it 302'd straight to /login - even though the hub access_token cookie (HS256 JWT signed by JwtService) was sitting in the request headers, perfectly valid, identifying the same user.

Concrete impact:

  - DMARC-21: A user comes back to drillmark after a few days. The op_session DB row has expired (7-day TTL) but the 30-day refresh_token cookie is keeping the 15-min access_token cookie fresh on bunyip-web visits. drillmark fires /authorize -> no op_session -> 302 /login. The user types creds for no reason; on bad days the SPA's 60s poll then collapses the chain into the redirect loop they filed.

  - Bunyip app launcher: User is in bunyip-web with a valid access_token cookie (they just logged in). Clicks "Open Drillmark" or "Open Mokosh". The new-tab navigation lands on the RP, which fires /authorize, which finds no op_session, which 302s to /login. They re-enter the password they typed five seconds ago.

This patch adds a silent-SSO fallback to `authorize`: when no op_session resolves, read the `access_token` cookie, verify it through `JwtService::verify_access_token`, and on success mint a fresh op_session for that user and attach the new bunyip_op_session cookie to the eventual code-mint 302. The existing "no cookie -> /login" path is untouched and runs whenever the silent path declines (no access_token cookie present, JWT expired, signature failed).

Security posture is unchanged from the existing `/v1/auth/login -> create_op_session` path: AuthCookies::clear already removes the access_token cookie on logout, and revoke_op_sessions runs synchronously alongside, so a logged-out user has neither input available to the silent branch. The 15-min access_token TTL bounds the worst-case window for a stolen cookie identically to every other consumer of that cookie.

#DMARC-21
YousifShkara deleted branch fix/oidc-silent-sso-via-access-token 2026-06-10 02:24:09 +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!89
No description provided.