You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Re-anchor the hash-key hard-expiry backstop on user activity/foreground instead of on sign-in, so an actively-used wallet never hits a surprise full re-authentication.
Soft auto-lock (AUTO_LOCK_TIMER): user-configurable (1m–24h, default 12h); activity-based (resets on interaction / foreground within the window) with a fast unlock. This is the primary mechanism.
Hash-key hard-expiry (HASH_KEY_EXPIRATION_MS): a coarser security backstop that discards cached derived-key material and forces a full password re-auth (HASH_KEY_EXPIRED, not the fast path). It is currently anchored at sign-in and only re-stamped on credential-verified moments.
Problem
Because the hard-expiry counts from the last unlock (not last activity), a loosely-active user on the 24h preset — whose usage gaps stay just under 24h, so the soft-lock never fires and never resets the hard-expiry clock — can hit a surprise full re-auth. At the current 72h value that requires ~3 continuous days of that pattern (we widened it from 48h → 72h, i.e. 3× the max preset, as a stopgap in #905), but it is not fully eliminated for the most regular 24h users, and it can in principle fire mid-session (the expiry is checked on every auth tick).
Proposed change
Re-anchor expiresAt on foreground/activity so:
An actively-used wallet keeps pushing the deadline out → never hard-expires from normal use.
A genuinely idle/away device (no foreground for the backstop window) still forces a full re-auth on next open — preserving the security bound as "N hours of inactivity" (matches the Freighter extension's session model).
Once re-anchored, the soft and hard timers become two thresholds on the same clock, and no fixed HASH_KEY_EXPIRATION_MS margin above the max preset is needed.
Design decisions to settle (the real cost is here, not LOC)
Anchor granularity. Refreshing on every gesture is too many secure-storage writes. Practical anchor is the background→foreground transition (or a throttled "refresh if expiresAt is older than X"). Decide + document.
Write throttling.getAuthStatus runs ~every 5s while foregrounded — the refresh must be gated to the transition (or throttled) so it doesn't hammer the keychain.
Product/security sign-off on the posture change. Re-anchoring means an actively-used wallet never hard-expires; the bound becomes "N hours of no use." Confirm this is the intended posture (it matches the extension).
Scope / effort estimate
Small–medium, contained; most plumbing already exists (foreground detection in useAuthCheck, secure-storage timestamp read/write like backgroundedAt, and the clock-rollback generatedAt guard added in #905):
Core: re-stamp expiresAt/generatedAt on foreground (throttled) and remove the current "deliberately NOT refreshed here" logic in getAuthStatus.
Files: src/ducks/auth.ts, src/hooks/useAuthCheck.ts, a small src/services/autoLock.ts helper.
Summary
Re-anchor the hash-key hard-expiry backstop on user activity/foreground instead of on sign-in, so an actively-used wallet never hits a surprise full re-authentication.
Follow-up to #905 (configurable auto-lock).
Background
#905 added two independent lock timers:
AUTO_LOCK_TIMER): user-configurable (1m–24h, default 12h); activity-based (resets on interaction / foreground within the window) with a fast unlock. This is the primary mechanism.HASH_KEY_EXPIRATION_MS): a coarser security backstop that discards cached derived-key material and forces a full password re-auth (HASH_KEY_EXPIRED, not the fast path). It is currently anchored at sign-in and only re-stamped on credential-verified moments.Problem
Because the hard-expiry counts from the last unlock (not last activity), a loosely-active user on the 24h preset — whose usage gaps stay just under 24h, so the soft-lock never fires and never resets the hard-expiry clock — can hit a surprise full re-auth. At the current 72h value that requires ~3 continuous days of that pattern (we widened it from 48h → 72h, i.e. 3× the max preset, as a stopgap in #905), but it is not fully eliminated for the most regular 24h users, and it can in principle fire mid-session (the expiry is checked on every auth tick).
Proposed change
Re-anchor
expiresAton foreground/activity so:Once re-anchored, the soft and hard timers become two thresholds on the same clock, and no fixed
HASH_KEY_EXPIRATION_MSmargin above the max preset is needed.Design decisions to settle (the real cost is here, not LOC)
expiresAtis older than X"). Decide + document.getAuthStatusruns ~every 5s while foregrounded — the refresh must be gated to the transition (or throttled) so it doesn't hammer the keychain.Scope / effort estimate
Small–medium, contained; most plumbing already exists (foreground detection in
useAuthCheck, secure-storage timestamp read/write likebackgroundedAt, and the clock-rollbackgeneratedAtguard added in #905):expiresAt/generatedAton foreground (throttled) and remove the current "deliberately NOT refreshed here" logic ingetAuthStatus.src/ducks/auth.ts,src/hooks/useAuthCheck.ts, a smallsrc/services/autoLock.tshelper.Estimate: ~0.5–1 day implementation + tests; no UI or new user-facing surface.
Acceptance criteria
HASH_KEY_EXPIREDfrom normal use, on any preset including 24h.generatedAtguard from App Configurable Auto lock #905).References
HASH_KEY_EXPIRATION_MS:freighter-mobile/src/config/constants.ts
Lines 92 to 107 in e1becc7
isHashKeyExpired(+ rollback guard) and the sign-in-anchored stamping / "not refreshed on foreground" logic insrc/ducks/auth.ts.