// Base64-encoded binary, in either standard or URL-safe form. Decoded with // crypto.fromBase64 which accepts either variant. export type Base64 = string; // The blob the server returns alongside the auth token after a successful // login. Together with the user's password, this is everything quak needs // to derive the master key, secret key, and auth token. export interface KeyAttributes { kekSalt: Base64; encryptedKey: Base64; keyDecryptionNonce: Base64; publicKey: Base64; encryptedSecretKey: Base64; secretKeyDecryptionNonce: Base64; memLimit: number; opsLimit: number; masterKeyEncryptedWithRecoveryKey?: Base64; masterKeyDecryptionNonce?: Base64; recoveryKeyEncryptedWithMasterKey?: Base64; recoveryKeyDecryptionNonce?: Base64; } // Server-issued attributes needed to start an SRP-6a handshake. Returned // from GET /users/srp/attributes?email=. export interface SRPAttributes { srpUserID: string; srpSalt: Base64; memLimit: number; opsLimit: number; kekSalt: Base64; isEmailMFAEnabled: boolean; } // The body of a successful login response. Exactly one of the following // situations applies, and the caller dispatches on the populated fields: // - both keyAttributes and encryptedToken present: login is complete // - twoFactorSessionID present: caller must submit a TOTP code (TOTP-only // account) // - passkeySessionID + twoFactorSessionIDV2 present: account has both // passkeys and TOTP; the V2 field is set instead of twoFactorSessionID // so that older clients keep using the passkey flow // - only passkeySessionID present: caller must complete a passkey ceremony export interface AuthorizationResponse { id: number; keyAttributes?: KeyAttributes; encryptedToken?: Base64; twoFactorSessionID?: string; twoFactorSessionIDV2?: string; passkeySessionID?: string; } // Discriminated union returned by the high-level login functions to tell // the caller what to do next. export type LoginChallenge = | { kind: "complete"; response: AuthorizationResponse } | { kind: "totp"; sessionID: string } | { kind: "passkey"; sessionID: string } | { kind: "emailOTP" };