Files
pezkuwichain e8dd9f6c99 feat(auth): actually ask for 2FA at login
2FA could be enabled but was never enforced. TwoFactorSetup wrote a secret and
backup codes, and login never checked any of it — TwoFactorVerify existed as a
component referenced from nowhere. Turning 2FA on protected nothing.

Optional, as decided: a user without 2FA is never blocked, and a failure while
checking is treated as "not required" so a transient error cannot become a login
wall for people who never enabled it.

The challenge is enforced in ProtectedRoute rather than only after the password
form, so a deep link or a restored tab cannot walk straight past it. Telegram
sessions are exempt — they authenticate through signed initData, so there is no
password to second-factor.

Recovery is by backup code, which the verify endpoint already supported and
consumes on use. Cancelling the challenge signs the user out; leaving them signed
in but unchallenged would defeat the point of asking.

Verification is remembered in sessionStorage, not localStorage: 2FA is asked once
per browser session and again after the tab closes. Persisting it would mean
being challenged once per device, ever.

Scope, stated plainly rather than implied: this is a client-side gate. It stops
someone holding the password from reaching the app in a browser, which is what
2FA is for here. It is not server-side enforcement — privileged operations still
verify authority independently (wallet signature for admin actions, JWT for an
account's own data), and those remain what actually protects funds and state.
2026-07-31 22:02:40 -07:00
..