feat(auth): uniform 30-min / 1-day (30-day-remember) session lifetimes (BUNYIP-381) #375

Merged
nrupard merged 1 commit from feat/BUNYIP-381-token-lifetimes into main 2026-07-16 19:14:42 +02:00
Owner

Summary

Reworks the legacy cookie-session token policy to a single uniform lifetime for all roles, and removes the BUNYIP-137 admin idle cap that was logging admins out after ~30 minutes idle (the real cause of the "Bunyip auth logs me out after a few minutes" report - MAPPS-375's SPA-OIDC-reuse guess was wrong; this supersedes it).

  • Access token: 30 minutes (JWT exp set in main.rs, + AuthCookies::access_token Max-Age).
  • Refresh token: 1 day, or 30 days with "remember me" (AuthCookies::refresh_token Max-Age + the DB absolute cap, now remember-based instead of role-based).

How

  • remember threads from the login handler -> AuthService::login -> the absolute deadline (refresh_absolute_ttl(remember) -> 30d/1d), which create_tokens carries verbatim across rotation, so the ceiling is absolute (from login), not rolling. Non-login mints (magic-link, 2FA/approval completion, invite acceptance) default to the 1-day cap.
  • The admin-only 30-min idle cap and 12h absolute cap are gone: refresh_idle_ttl / session_idle_expired deleted, and the idle-revoke gate removed from refresh_tokens.

Security tradeoff (operator-directed)

This intentionally removes BUNYIP-137's admin session hardening: admin cookie sessions go from a 30-min-idle / 12h-absolute leash to the same uniform 1-day (30-day remember) refresh as everyone else, with no idle timeout. Deliberate per the operator's decision. Revisit if privileged-session exposure becomes a concern.

The OIDC /oauth2/token path (per-client TTLs on oidc_clients) is untouched.

Known limitation

A 2FA-cleared login does not carry remember through the 2FA challenge, so 2FA + "remember me" yields the 1-day cap rather than 30 days. Threading remember through the challenge is a small follow-up if it matters.

Testing

just check-container green: fmt + clippy -D warnings + cargo test --workspace --all-targets (275 domain / 73 api / 104 web, 0 failed). BUNYIP-137 policy tests updated to the new uniform policy; cookie-property tests updated to 30 min / 1 day.

## Summary Reworks the legacy cookie-session token policy to a single uniform lifetime for all roles, and removes the BUNYIP-137 admin idle cap that was logging admins out after ~30 minutes idle (the real cause of the "Bunyip auth logs me out after a few minutes" report - MAPPS-375's SPA-OIDC-reuse guess was wrong; this supersedes it). - **Access token: 30 minutes** (JWT `exp` set in `main.rs`, + `AuthCookies::access_token` Max-Age). - **Refresh token: 1 day, or 30 days with "remember me"** (`AuthCookies::refresh_token` Max-Age + the DB absolute cap, now remember-based instead of role-based). ## How - `remember` threads from the login handler -> `AuthService::login` -> the absolute deadline (`refresh_absolute_ttl(remember)` -> 30d/1d), which `create_tokens` carries verbatim across rotation, so the ceiling is absolute (from login), not rolling. Non-login mints (magic-link, 2FA/approval completion, invite acceptance) default to the 1-day cap. - The admin-only 30-min idle cap and 12h absolute cap are gone: `refresh_idle_ttl` / `session_idle_expired` deleted, and the idle-revoke gate removed from `refresh_tokens`. ## Security tradeoff (operator-directed) This intentionally removes BUNYIP-137's admin session hardening: admin cookie sessions go from a 30-min-idle / 12h-absolute leash to the same uniform 1-day (30-day remember) refresh as everyone else, with no idle timeout. Deliberate per the operator's decision. Revisit if privileged-session exposure becomes a concern. The OIDC `/oauth2/token` path (per-client TTLs on `oidc_clients`) is untouched. ## Known limitation A 2FA-cleared login does not carry `remember` through the 2FA challenge, so 2FA + "remember me" yields the 1-day cap rather than 30 days. Threading `remember` through the challenge is a small follow-up if it matters. ## Testing `just check-container` green: fmt + clippy `-D warnings` + `cargo test --workspace --all-targets` (275 domain / 73 api / 104 web, 0 failed). BUNYIP-137 policy tests updated to the new uniform policy; cookie-property tests updated to 30 min / 1 day.
feat(auth): uniform 30-min / 1-day (30-day-remember) session lifetimes, drop admin idle leash (BUNYIP-381)
All checks were successful
E2E / Playwright against deployment (pull_request) Successful in 21s
Check / fmt + clippy + build + tests (pull_request) Successful in 30m23s
Create release / Create release from merged PR (pull_request) Has been skipped
5b78d6c690
Replaces the BUNYIP-137 role-based cookie-session policy that logged admins out after 30 minutes idle. Now uniform for all roles:
- Access token: 30 minutes (JWT exp set in main.rs + access_token cookie Max-Age).
- Refresh token: 1 day, or 30 days with "remember me" (refresh cookie Max-Age + the DB absolute cap, now remember-based instead of role-based).

`remember` is threaded from the login handler through AuthService::login into the absolute deadline (refresh_absolute_ttl(remember)), which create_tokens carries verbatim across rotation, so the ceiling is absolute from login, not rolling. Non-login mints (magic-link, 2FA / approval completion, invite acceptance) default to the 1-day cap.

The 30-min admin idle cap and 12h admin absolute cap are gone: refresh_idle_ttl / session_idle_expired are deleted and the idle gate removed from refresh_tokens. This intentionally drops BUNYIP-137's admin session hardening (operator-directed; see BUNYIP-381 for the accepted tradeoff). This is the real fix for the "Bunyip auth logs me out after a few minutes" report; MAPPS-375's SPA-OIDC-reuse guess was wrong. The OIDC /oauth2/token path (per-client TTLs) is untouched.

Known limitation: a 2FA-cleared login does not carry `remember` through the challenge, so 2FA + "remember me" yields the 1-day cap rather than 30 days. A follow-up if it matters.

check-container green.

#BUNYIP-381

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
nrupard scheduled this pull request to auto merge when all checks succeed 2026-07-16 18:56:34 +02:00
nrupard deleted branch feat/BUNYIP-381-token-lifetimes 2026-07-16 19:14:42 +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!375
No description provided.