Commit Graph

4 Commits

Author SHA1 Message Date
68d8cfb7fe Green: treat empty-string 2FA session fields as absent
Use || instead of ?? when picking the TOTP session ID. The server
sends "" for unset 2FA fields (no omitempty), and "" ?? v2
short-circuits to "", which made dual-2FA accounts fall through to
the unsupported passkey branch.
2026-06-10 11:26:15 -07:00
9fd7b6a857 Green: prefer TOTP via twoFactorSessionIDV2 when passkey also enrolled
beginLogin now checks twoFactorSessionID ?? twoFactorSessionIDV2 before
the passkey branch. Accounts with both passkeys and TOTP can now log in
from the CLI using their authenticator app.
2026-06-10 11:05:36 -07:00
dcec9b92ad Phase 3b green: implement login flow (SRP + email OTP + TOTP)
beginLogin(api, email, password):
  1. Fetches SRP attributes for the email
  2. If isEmailMFAEnabled, returns { kind: 'emailOTP' } immediately
  3. Otherwise: derives KEK + login subkey, creates SRP client with
     4096-bit group, runs the two-round handshake (create-session,
     verify-session), verifies server M2
  4. Returns { kind: 'complete' } with AuthorizationResponse, or
     { kind: 'totp' } / { kind: 'passkey' } if 2FA is required

submitTOTP(api, sessionID, code): POST /users/two-factor/verify
requestEmailOTP(api, email): POST /users/ott
submitEmailOTP(api, email, code): POST /users/verify-email

All 64 tests pass including real SRP-6a handshakes against a mock
server built with fast-srp-hap's SrpServer.
2026-05-11 10:11:19 -07:00
75b57cfb29 Phase 3b red: login flow tests with SRP mock server
Adds fast-srp-hap (the same SRP library Ente's web client uses, pinned
to 2.0.4) as a runtime dependency.

Tests build a full mock Ente server using fast-srp-hap's SrpServer to
exercise real SRP-6a math end-to-end. The mock handles:
  GET /users/srp/attributes
  POST /users/srp/create-session
  POST /users/srp/verify-session
  POST /users/two-factor/verify
  POST /users/ott
  POST /users/verify-email

7 tests covering:
  * SRP login completing successfully
  * SRP login requiring TOTP (returns { kind: 'totp' })
  * Wrong password (SRP M1 fails server-side checkM1)
  * Email MFA fallback (returns { kind: 'emailOTP' })
  * submitTOTP
  * requestEmailOTP + submitEmailOTP
2026-05-11 01:04:10 -07:00