Skip to content

Make login sessions persistent — stop re-login on every browser session #925

Description

@peterdrier

Context

Members have to log in again essentially every time they visit the site. For a ~500-user community app used in bursts (shift signups, event weeks), that's real friction — especially on mobile where the OAuth round-trip is clunky.

Problem / Motivation

Two compounding causes, confirmed in code:

  1. Every production sign-in issues a browser-session cookie. All sign-in paths pass isPersistent: false, so the auth cookie has no Expires attribute and dies when the browser fully closes:
    • AccountController.cs:70ExternalLoginSignInAsync(..., isPersistent: false, ...) (the main Google login path)
    • AccountController.cs:198, 234, 291, 471, 537 — the account-linking / first-login / merge variants
    • (The password path at AccountController.cs:595 already uses isPersistent: true — the inconsistency shows the intent was never "expire aggressively".)
  2. No explicit cookie lifetime is configured. ConfigureApplicationCookie (Program.cs:141-148) sets HttpOnly/Secure/SameSite but no ExpireTimeSpan/SlidingExpiration, so even a persistent cookie would ride on Identity's implicit default (14-day sliding) rather than a deliberate policy.

Proposed Solution

  • Flip the production sign-in calls to isPersistent: true. No "Remember me" checkbox — the Google OAuth flow has no credential form to hang it on, and default-persistent matches user expectation for this kind of app.
  • Set an explicit, deliberate policy in ConfigureApplicationCookie: ExpireTimeSpan = 7 days, SlidingExpiration = true (any visit inside the window extends it; absence for 7 days requires re-login).
  • Revocation stays intact: explicit logout works unchanged; suspended/non-Active users are already bounced per-request by MembershipRequiredFilter regardless of cookie lifetime; Identity's security-stamp validation (30-min default) kills cookies on server-side invalidation.
  • Gate/kiosk sessions already have explicit sign-out affordances ("End shift", kiosk logout command), so shared-device risk is managed; no carve-out needed.
  • DevLoginController — implementer's choice; persistence there is harmless (dev/preview only).

Decision (Peter, 2026-07-13): 7-day sliding window for now; can lengthen later if re-login friction is still felt.

  • Dev-login persistence — left to the implementer.

Acceptance Criteria

  • Logging in via Google, closing the browser, and returning the next day does not require re-login
  • Auth cookie carries an explicit expiry (7 days) and renews on activity (sliding)
  • Explicit logout still ends the session immediately
  • A suspended (non-Active UserState) user with a live cookie is still redirected to /User/Status on next request

Key files: src/Humans.Web/Controllers/AccountController.cs, src/Humans.Web/Program.cs, src/Humans.Web/Controllers/DevLoginController.cs

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions