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.
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