Skip to content

fix(settings): list devices without Better Auth's freshness gate - #597

Merged
Deodat-Lawson merged 1 commit into
mainfrom
claude/session-freshness-check-9194c7
Nov 8, 2026
Merged

Deodat-Lawson merged 1 commit into
mainfrom
claude/session-freshness-check-9194c7

Conversation

@Deodat-Lawson

@Deodat-Lawson Deodat-Lawson commented Nov 8, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Settings › Account showed "Session is not fresh" instead of the device list for anyone who signed in more than a day ago. Better Auth's list-sessions sits behind freshSessionMiddleware (freshAge, 1 day) because it returns each session's token.
  • New /api/profile/sessions: GET lists the caller's active sessions without tokens (id + current flag); DELETE { id } ends one of the caller's own sessions by id. It refuses the current session and cross-site requests. With no secret in the response, the freshness gate isn't needed.
  • Unlinking a sign-in and setting a password keep the freshness gate. When it fires they now show a translated warn note with a Sign in again button (signs out → /signin?next=/employer/settings?show=sign-in) instead of the raw error. /api/profile/sign-in now returns code: "SESSION_NOT_FRESH" for that case.
  • Fixes found along the way: session expiry said "expires just now" (past-tense formatter on a future date → time.until, now "expires in 7 days"); StatusNote warn tone used literal light colours and stayed bright in dark mode — now --warn-* tokens (also affects the existing warn notes in Models).

Related

None.

Checklist

  • pnpm check passes (lint + typecheck) — eslint + tsc on the web app, 0 errors
  • pnpm --filter @launchstack/web test passes — ran profile, i18n and brand suites (218 passed), not the whole suite
  • Changeset added — n/a (apps/web only)
  • New env vars documented — n/a (none)
  • UI changes exercised in a browser (not just a green build)
  • Docs updated if behavior changed — n/a

Testing

  • 8 new route tests in apps/web/__tests__/api/profile/sessions.route.test.ts: listing works on a 10-day-old session, order (current first, then most recent), expired sessions dropped, no token in the response, 401 signed out; DELETE by id works, refuses the current session, another user's session (404), cross-site (403) and a missing id (400).
  • Live dev server against the Docker backend, seeded owner with the session aged 3 days in the DB:
    • Better Auth list-sessions → 403 SESSION_NOT_FRESH; the new route → 200, no tokens.
    • Device list renders in Settings › Account; ending a second session from the UI signs that session out.
    • Unlink on a stale session shows the translated note (checked light + dark); Sign in again lands on the sign-in page with the right next. A freshly signed-in session passes the gate.
  • Prettier, eslint, tsc clean; npx jest src/i18n confirms en/zh-CN keys match.

Notes for reviewers

  • The security argument for dropping freshness on listing: Better Auth's gate protects the tokens it returns. This route never returns tokens and only deletes sessions it found under the caller's own userId, so another user's id reads exactly like an ended session (404).
  • Not exercised: a successful unlink after signing in again in the browser (the Google link used for the UI test was a temporary fake row).

🤖 Generated with Claude Code


Note

Medium Risk
New session list/revoke API touches auth session lifecycle and CSRF checks; design avoids exposing tokens but server-side delete still must stay scoped to the caller's sessions only.

Overview
Fixes Settings › Account showing "Session is not fresh" for sign-ins older than a day by replacing Better Auth's token-bearing list-sessions with a new /api/profile/sessions API.

GET returns active sessions as ids plus a current flag (no tokens), sorted with this browser first. DELETE { id } revokes another of the caller's sessions server-side, blocks ending the current session, cross-site requests, and foreign ids (404). Account now loads and ends sessions through this route instead of the auth client.

Unlink and set password still require a fresh sign-in; /api/profile/sign-in adds code: "SESSION_NOT_FRESH", and the UI shows a translated warn note with Sign in again (sign out → sign-in with return to settings). Session rows use time.until for expiry wording; StatusNote warn styling moves to --warn-* tokens (en/zh strings and route tests included).

Reviewed by Cursor Bugbot for commit 94baae3. Bugbot is set up for automated code reviews on this repo. Configure here.

Settings › Account showed "Session is not fresh" to anyone who signed in
more than a day ago, because Better Auth's list-sessions requires a fresh
session (it returns each session's token).

- Add /api/profile/sessions: GET lists the caller's active sessions with
  no tokens; DELETE ends one by id (never the current one, same-origin only).
- Account section uses the new route; unlink and set-password keep the
  freshness gate and now show a translated "Sign in again" note instead
  of the raw error (set-password returns code SESSION_NOT_FRESH).
- Session expiry reads "expires in 7 days" instead of "just now".
- StatusNote warn tone uses --warn-* tokens so it follows dark mode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Nov 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
pdr-ai-v2 Ignored Ignored Nov 8, 2026 1:05am UTC

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 8, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-08T01:06:49.823990Z 94baae3 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@cursor

cursor Bot commented Nov 8, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: 8103fba5-7b13-4b07-9193-067f979c1023)

@Deodat-Lawson
Deodat-Lawson merged commit 9b66254 into main Nov 8, 2026
15 checks passed
@Deodat-Lawson
Deodat-Lawson deleted the claude/session-freshness-check-9194c7 branch November 8, 2026 02:22
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