Fleet sessions live one day, idle out after twelve hours (ADR-0064) - #1508
Merged
Merged
Conversation
The elcano_session cookie was a stateless HMAC valid for fourteen days from login with no notion of inactivity: a laptop left open on Friday was still signed in the next Friday. Elcano Auth v2 makes Fleet's session an application session that renews silently through the OIDC handoff while the user's 30-day central session is live, so its job is now to bound a stolen cookie and re-check the account with Auth, not to decide how often people log in. It now carries an idle deadline next to the absolute one, both enforced on every request, and the proxy re-mints the cookie on activity. Problem SessionPayload had a single exp set to mint + 14 days. Nothing recorded activity, so idle sessions never ended early, and fourteen days is out of line with the one-day / twelve-hour convention Explorer and Lens now follow (auth/docs/AUTH_V2_IMPLEMENTATION.md, "Application session conventions"). Being stateless, Fleet had no last_seen_at to touch, so an idle limit could only live inside the cookie. Fix - SessionPayload gains `idle`. exp is the absolute deadline (sessionAbsoluteSeconds, one day), copied on every re-mint and never extended; idle is min(now + sessionIdleSeconds (12h), exp). - verifySessionToken refuses a token whose idle or exp has passed and refuses a signed token with no idle claim. Pre-deploy cookies are not grandfathered, the same call ADR-0041 made for claimless cookies: a cookie signed for fourteen days flat is exactly what this removes. Central-Auth users are signed back in without a prompt; password users log in once more. - refreshSessionCookie re-signs the payload with a later idle when the last mint is over sessionTouchSeconds (60) old, preserving email, exp, epoch, source, issuer and subject, with Max-Age set to the remaining absolute life and Secure following the request. It does nothing for elcano_auth sessions (Fleet cannot re-mint Auth's cookie), under a minute, or once idle already sits on exp. One minute is the touch convention shared with Auth, Explorer and Lens. - The request proxy calls it on every authenticated pass-through, pages and /api alike, and never on redirects, 401s, public routes or bearer-only requests. - Both mint paths share signSessionPayload so neither can emit a cookie the verifier refuses; their cookie Max-Age is the absolute lifetime. - The mocked e2e cookie minter mirrors the new claim. - ADR-0064 records the decision and amends ADR-0041; DEPLOYMENT.md's "Ending a session" note no longer says fourteen days. Tests - auth.test.ts: mint deadlines; idle refusal ahead of exp; refusal of a signed token without idle; no re-mint under a minute; re-mint after a minute keeps every identity claim and sets httpOnly/lax/secure/path and Max-Age = remaining absolute; activity never moves exp; re-minting stops once idle is capped; elcano_auth sessions untouched; Secure follows a plain-HTTP dev request. Each fails on the previous code. - proxy.test.ts: the cookie is touched exactly once with (request, response, session) on an authenticated pass-through and never on redirects, 401s, public routes or bearer requests. - npm run typecheck, npm run lint (oxlint), npx vitest run (full web suite). npm run build and the mocked Playwright suite are left to CI. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TLDR
The
elcano_sessioncookie was a stateless HMAC valid for fourteen days with no notion of inactivity. Under Elcano Auth v2 it is an application session that renews silently through the OIDC handoff while the user's 30-day central session is live, so its job is to bound a stolen cookie and re-check the account with Auth, not to decide how often people log in. It now carries an idle deadline next to the absolute one (one day / twelve hours, the convention Explorer and Lens follow), both are enforced on every request, and the proxy re-mints the cookie on activity. Companion: ElcanoTek/auth#30, ElcanoTek/explorer#23, ElcanoTek/lens#24.Problem
SessionPayloadhad a singleexpat mint + 14 days. Nothing recorded activity, so idle sessions never ended early, and fourteen days is out of line with the one-day / twelve-hour application-session convention inauth/docs/AUTH_V2_IMPLEMENTATION.md. Fleet is stateless, so an idle limit can only live inside the cookie.Fix
SessionPayloadgainsidle.expis the absolute deadline (sessionAbsoluteSeconds, one day), copied on re-mint and never extended;idleismin(now + 12h, exp).verifySessionTokenrefuses a token whoseidleorexphas passed, and a signed token with noidleclaim. Pre-deploy cookies are not grandfathered, the same call ADR-0041 made: every user is signed out once at deploy. Central-Auth users are signed back in without a prompt; password users log in once more.refreshSessionCookiere-signs with a lateridlewhen the last mint is oversessionTouchSeconds(60) old, preserving email, exp, epoch, source, issuer, subject, withMax-Age= remaining absolute life andSecurefollowing the request. No-op forelcano_authsessions, under a minute, or onceidlesits onexp./api), never on redirects, 401s, public routes or bearer-only requests.signSessionPayload; the mocked e2e cookie minter mirrors the new claim.DEPLOYMENT.md"Ending a session" updated.Tests
auth.test.ts: mint deadlines; idle refusal ahead of exp; refusal withoutidle; no re-mint under a minute; re-mint after a minute keeps every identity claim and sets httpOnly/lax/secure/path and Max-Age; activity never moves exp; re-minting stops once capped;elcano_authuntouched; Secure follows a plain-HTTP request. Each fails on the previous code.proxy.test.ts: cookie touched exactly once with(request, response, session)on an authenticated pass-through, never otherwise.npm run typecheck,npm run lint,npx vitest run: 1625 passed. The 4 failures inuseDashboardData.test.ts(tag-catalogue freshness) fail identically on an untouchedorigin/mainworktree and are unrelated.npm run buildand the mocked Playwright suite are left to CI.🤖 Generated with Claude Code