Skip to content

feat(auth): ruled sidebar app shell, NIST session timeouts, real session revocation - #60

Merged
CodeWithEugene merged 2 commits into
mainfrom
feat/session-security
Oct 1, 2026
Merged

CodeWithEugene merged 2 commits into
mainfrom
feat/session-security

Conversation

@CodeWithEugene

Copy link
Copy Markdown
Owner

App shell (signed-in app and admin)

  • Replaces the half-finished sidebar: the phone top bar was rendering on desktop and pushing page content out of view, and the sidebar had no phone styles.
  • The app now sits in the marketing ruled frame: hairline side rules, sidebar as the first column, one header line running from the logo cell across the top bar. Square nav items with a brand tick on the current page; theme switch and user menu in the top bar.
  • Cards inside the app are square, flat and hairline-outlined. Below 1024px a top bar and drawer carry the nav.

Session timeouts (NIST SP 800-63B-4)

  • Everyone: 1 hour idle, 24 hours overall (AAL2, since every account controls payout destinations or deposits). Admins: 15 minutes idle, 12 hours overall (AAL3). Enforced in the jwt callback.
  • middleware.ts (Node.js runtime) slides the session cookie on real requests from signed-in visitors. It does no access control; the layout guards still decide access.
  • In-app warning two minutes before an idle sign-out, with Stay Signed In; activity is shared across tabs.

Session revocation

  • New User.sessionVersion (migration 9_session_version, additive with default 0). Every token carries the version it was issued under; a stale version ends the session and clears the cookie.
  • Password change, password reset, account deactivation and the new Settings "Sign Out Everywhere" end every session on every device. Previously JWT sessions survived all of these for up to 30 days.
  • Deleted or revoked accounts get no session instead of a blank one.
  • The session check is cached per request (React.cache), so a page does it once instead of once per guard.

Existing sessions carry no sign-in time, so everyone signs in once after deploy.

Test plan

  • tsc, lint, 486 tests (new: session policy, session version, revocation integration), build
  • Production build, local: page load re-issues the cookie with a 1 hour expiry; revoked token gets Max-Age=0 and a redirect to /signin; Sign Out Everywhere in the browser ended a second, separately signed-in session
  • Shell checked at 1440px and 375px, light and dark, drawer open/close/Escape

…ocation

- Sessions end after 1 hour idle / 24 hours overall (NIST SP 800-63B-4
  AAL2); admins after 15 minutes / 12 hours (AAL3). Enforced in the jwt
  callback; Node.js middleware slides the cookie on real requests.
- User.sessionVersion makes JWT sessions revocable: password change,
  password reset, deactivation and the new "Sign Out Everywhere" end every
  session on every device.
- Deleted or revoked accounts get no session at all instead of a blank one.
- The session check is cached per request (one check per page, not per guard).
- In-app inactivity warning two minutes before sign-out.
@CodeWithEugene
CodeWithEugene merged commit 12e055f into main Oct 1, 2026
4 checks passed

This branch was successfully deployed

1 active deployment
Preview — 9e6dc5b1 Deployed Oct 1, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant