/** * MFA (Two-Factor Authentication) API */ import apiClient from './client'; // ============================================================================ // Types // ============================================================================ export interface MFAStatus { mfa_enabled: boolean; mfa_method: 'NONE' | 'SMS' | 'TOTP' | 'BOTH'; methods: ('SMS' | 'TOTP' | 'BACKUP')[]; phone_last_4: string | null; phone_verified: boolean; totp_verified: boolean; backup_codes_count: number; backup_codes_generated_at: string | null; trusted_devices_count: number; } export interface TOTPSetupResponse { success: boolean; secret: string; qr_code: string; // Data URL for QR code image provisioning_uri: string; message: string; } export interface MFAEnableResponse { success: boolean; message: string; mfa_method: string; backup_codes?: string[]; backup_codes_message?: string; } export interface BackupCodesResponse { success: boolean; backup_codes: string[]; message: string; warning: string; } export interface BackupCodesStatus { count: number; generated_at: string | null; } export interface TrustedDevice { id: number; name: string; ip_address: string; created_at: string; last_used_at: string; expires_at: string; is_current: boolean; } export interface MFALoginResponse { mfa_required: boolean; user_id?: number; mfa_methods?: string[]; phone_last_4?: string; } export interface MFAVerifyResponse { success: boolean; access: string; refresh: string; user: { id: number; email: string; username: string; first_name: string; last_name: string; full_name: string; role: string; business_subdomain: string | null; mfa_enabled: boolean; }; } // ============================================================================ // MFA Status // ============================================================================ /** * Get current MFA status */ export const getMFAStatus = async (): Promise => { const response = await apiClient.get('/auth/mfa/status/'); return response.data; }; // ============================================================================ // SMS Setup // ============================================================================ /** * Send phone verification code */ export const sendPhoneVerification = async (phone: string): Promise<{ success: boolean; message: string }> => { const response = await apiClient.post('/auth/mfa/phone/send/', { phone }); return response.data; }; /** * Verify phone number with code */ export const verifyPhone = async (code: string): Promise<{ success: boolean; message: string }> => { const response = await apiClient.post('/auth/mfa/phone/verify/', { code }); return response.data; }; /** * Enable SMS MFA (requires verified phone) */ export const enableSMSMFA = async (): Promise => { const response = await apiClient.post('/auth/mfa/sms/enable/'); return response.data; }; // ============================================================================ // TOTP Setup (Authenticator App) // ============================================================================ /** * Initialize TOTP setup (returns QR code and secret) */ export const setupTOTP = async (): Promise => { const response = await apiClient.post('/auth/mfa/totp/setup/'); return response.data; }; /** * Verify TOTP code to complete setup */ export const verifyTOTPSetup = async (code: string): Promise => { const response = await apiClient.post('/auth/mfa/totp/verify/', { code }); return response.data; }; // ============================================================================ // Backup Codes // ============================================================================ /** * Generate new backup codes (invalidates old ones) */ export const generateBackupCodes = async (): Promise => { const response = await apiClient.post('/auth/mfa/backup-codes/'); return response.data; }; /** * Get backup codes status */ export const getBackupCodesStatus = async (): Promise => { const response = await apiClient.get('/auth/mfa/backup-codes/status/'); return response.data; }; // ============================================================================ // Disable MFA // ============================================================================ /** * Disable MFA (requires password or valid MFA code) */ export const disableMFA = async (credentials: { password?: string; mfa_code?: string }): Promise<{ success: boolean; message: string }> => { const response = await apiClient.post('/auth/mfa/disable/', credentials); return response.data; }; // ============================================================================ // MFA Login Challenge // ============================================================================ /** * Send MFA code for login (SMS only) */ export const sendMFALoginCode = async (userId: number, method: 'SMS' | 'TOTP' = 'SMS'): Promise<{ success: boolean; message: string; method: string }> => { const response = await apiClient.post('/auth/mfa/login/send/', { user_id: userId, method }); return response.data; }; /** * Verify MFA code to complete login */ export const verifyMFALogin = async ( userId: number, code: string, method: 'SMS' | 'TOTP' | 'BACKUP', trustDevice: boolean = false ): Promise => { const response = await apiClient.post('/auth/mfa/login/verify/', { user_id: userId, code, method, trust_device: trustDevice, }); return response.data; }; // ============================================================================ // Trusted Devices // ============================================================================ /** * List trusted devices */ export const listTrustedDevices = async (): Promise<{ devices: TrustedDevice[] }> => { const response = await apiClient.get('/auth/mfa/devices/'); return response.data; }; /** * Revoke a specific trusted device */ export const revokeTrustedDevice = async (deviceId: number): Promise<{ success: boolean; message: string }> => { const response = await apiClient.delete(`/auth/mfa/devices/${deviceId}/`); return response.data; }; /** * Revoke all trusted devices */ export const revokeAllTrustedDevices = async (): Promise<{ success: boolean; message: string; count: number }> => { const response = await apiClient.delete('/auth/mfa/devices/revoke-all/'); return response.data; };