feat(mobile): security settings for lock timeout and biometric unlock - #551
Conversation
Adds /settings/security, where the user tunes the app-lock policy: how long the app may sit idle before locking (5 / 15 / 30 minutes, or never) and whether unlocking must present a biometric factor. The timeout uses the same veil_idle_lock_minutes key as the web wallet, so the choice carries between clients. lib/appLock.ts holds the policy and the idle watcher the lock screen (backlog Miracle656#28) consumes. Changing the timeout takes effect immediately rather than at the next lock: watchers subscribe to the settings store and reschedule, including firing straight away when a shortened timeout leaves the deadline already in the past. Two things differ from the web wallet's lib/idle-lock.ts. Activity is reported explicitly through noteActivity(), because React Native has no global mouse/keyboard event stream to listen to. And backgrounding is not treated as activity the way visibilitychange is on the web -- JS timers are frozen or killed while the app is away, so the watcher records when it left the foreground and locks on return if the idle period already elapsed. Without that, a phone could sit in a pocket for an hour and come back unlocked. The biometric requirement is stored as a preference rather than gated on a capability check: whether the device has usable biometrics can change between now and the unlock, so the lock screen resolves that when it prompts and falls back to the passkey rather than locking the user out of their funds.
|
@Elizabethxxx is attempting to deploy a commit to the miracle656's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
@Elizabethxxx Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
Two gaps that stopped the settings taking effect: - hydrateLockSettings() was never called, so the app started on the defaults every launch and a saved timeout only applied once the user re-picked it. Called from app/_layout.tsx alongside hydrateNetwork(). - /settings/security was unreachable. The settings tab rendered its rows as Pressables that only set local state, so the "Security & lock" row went nowhere. Rows now take an optional href and navigate when present. tsc clean; jest 15 suites / 266 tests; expo lint clean.
|
Merging. Two things had to be fixed for the settings to actually do anything:
Verified: On the acceptance criterion — changing timeout changes lock behavior — the settings half is now complete and persisted, but the consumer isn't here yet: backlog #28's actual lock screen is #596, still open. So Also worth noting for whoever picks up #596: |
Lets users tune the app-lock behaviour: the inactivity timeout and whether unlocking requires biometrics.
closes #491
What is here
frontend/mobile/lib/appLock.ts— the policy store andcreateIdleWatcher, the countdown the lock screen (backlog feat(sdk): expose guardian recovery methods (setGuardian, initiateRecovery, completeRecovery) #28) wires to.frontend/mobile/hooks/useAppLock.ts— subscribes a component to the policy, following the same shape as the existinguseTheme.frontend/mobile/app/settings/security.tsx— the screen: four timeout options and a biometric switch.Acceptance: changing the timeout changes lock behaviour
A running watcher subscribes to the settings store, so a change reschedules it immediately rather than taking effect at the next lock. Both directions are covered:
Design notes
The timeout shares the web wallet's storage key (
veil_idle_lock_minutes) and offers the same four options. The setting is shared across clients, so an option that existed on only one of them would be silently rewritten by the other.Backgrounding is not activity. The web wallet treats
visibilitychangeas activity and resets the timer. On a phone the equivalent means something stronger: JS timers are frozen or killed while the app is away, so a timer alone would let a phone sit in a pocket for an hour and come back unlocked. The watcher records when the app left the foreground and, on return, locks if the idle period already elapsed.Activity is reported explicitly via
noteActivity(), since React Native has no global mouse/keyboard/scroll stream to listen to the waywindowdoes.The biometric requirement is stored as a preference, not gated on a capability check. Whether a device has usable biometrics can change between setting the toggle and the unlock, so the lock screen resolves that when it prompts and falls back to the passkey rather than locking a user out of their own funds. The screen says so.
Deferral is carried over from the web watcher:
shouldDeferpostpones a lock rather than interrupting an in-flight transaction.Scope
This is the settings half of the lock system — it decides when a lock is due and how strong the unlock must be. Presenting the lock screen and performing the unlock is backlog #28, which consumes
createIdleWatcherandgetLockSettingsrather than reimplementing either. That is why the behaviour is demonstrated by tests against the watcher instead of by a lock screen appearing.Checks
npm run typecheckandnpm testpass infrontend/mobile.