@frank-auth/react
Version:
Flexible and customizable React UI components for Frank Authentication
1 lines • 32.6 kB
Source Map (JSON)
{"version":3,"file":"use-mfa.cjs","sources":["../../../src/hooks/use-mfa.ts"],"sourcesContent":["/**\n * @frank-auth/react - useMFA Hook\n *\n * Multi-Factor Authentication hook that provides comprehensive MFA management\n * including TOTP, SMS, email, backup codes, and WebAuthn authentication.\n */\n\nimport {useCallback, useEffect, useMemo, useState} from 'react';\n\nimport type {\n MFAMethod,\n MFAVerifyRequest,\n MFAVerifyResponse,\n SetupMFARequest,\n VerifyMFASetupRequest,\n} from '@frank-auth/client';\n\nimport {useAuth} from './use-auth';\nimport {useConfig} from '../provider/config-provider';\n\nimport type {AuthError} from '../provider/types';\n\n// ============================================================================\n// MFA Hook Interface\n// ============================================================================\n\nexport interface UseMFAReturn {\n // MFA state\n mfaMethods: MFAMethod[];\n isEnabled: boolean;\n isRequired: boolean;\n primaryMethod: MFAMethod | null;\n backupCodes: string[];\n isLoaded: boolean;\n isLoading: boolean;\n error: AuthError | null;\n\n // MFA setup\n setupTOTP: () => Promise<MFASetupData>;\n setupSMS: (phoneNumber: string) => Promise<MFASetupData>;\n setupEmail: (email?: string) => Promise<MFASetupData>;\n setupWebAuthn: () => Promise<MFASetupData>;\n\n // MFA verification during setup\n verifySetup: (method: string, code: string, methodId?: string) => Promise<MFAMethod>;\n\n // MFA verification during authentication\n verifyMFA: (method: string, code: string, token: string) => Promise<MFAVerifyResponse>;\n\n // Method management\n removeMFAMethod: (methodId: string) => Promise<void>;\n setPrimaryMethod: (methodId: string) => Promise<void>;\n regenerateBackupCodes: () => Promise<string[]>;\n\n // MFA status checking\n hasTOTP: boolean;\n hasSMS: boolean;\n hasEmail: boolean;\n hasWebAuthn: boolean;\n hasBackupCodes: boolean;\n\n // Method availability\n availableMethods: MFAMethodType[];\n\n // Convenience methods\n disable: () => Promise<void>;\n enable: () => Promise<void>;\n refreshMethods: () => Promise<void>;\n}\n\nexport interface MFASetupData {\n method: string;\n qrCode?: string;\n secret?: string;\n backupCodes?: string[];\n challenge?: any;\n verificationRequired: boolean;\n}\n\nexport type MFAMethodType = 'totp' | 'sms' | 'email' | 'webauthn' | 'backup_codes';\n\n// ============================================================================\n// MFA Method Configurations\n// ============================================================================\n\nexport const MFA_METHOD_CONFIGS = {\n totp: {\n name: 'Authenticator App',\n description: 'Use an authenticator app like Google Authenticator or Authy',\n icon: '📱',\n setupSteps: [\n 'Install an authenticator app on your phone',\n 'Scan the QR code or enter the secret key',\n 'Enter the 6-digit code from your app',\n ],\n },\n sms: {\n name: 'SMS',\n description: 'Receive codes via text message',\n icon: '💬',\n setupSteps: [\n 'Enter your phone number',\n 'Wait for the verification code',\n 'Enter the code to confirm',\n ],\n },\n email: {\n name: 'Email',\n description: 'Receive codes via email',\n icon: '✉️',\n setupSteps: [\n 'Confirm your email address',\n 'Wait for the verification code',\n 'Enter the code to confirm',\n ],\n },\n webauthn: {\n name: 'Security Key',\n description: 'Use a hardware security key or biometric authentication',\n icon: '🔐',\n setupSteps: [\n 'Insert your security key or prepare biometric authentication',\n 'Follow your browser\\'s authentication prompts',\n 'Confirm the registration',\n ],\n },\n backup_codes: {\n name: 'Backup Codes',\n description: 'Single-use codes for emergency access',\n icon: '🔢',\n setupSteps: [\n 'Save these codes in a secure location',\n 'Each code can only be used once',\n 'Generate new codes when running low',\n ],\n },\n} as const;\n\n// ============================================================================\n// Main useMFA Hook\n// ============================================================================\n\n/**\n * Multi-Factor Authentication hook providing comprehensive MFA management\n *\n * @example Basic MFA setup\n * ```tsx\n * import { useMFA } from '@frank-auth/react';\n *\n * function MFASetup() {\n * const {\n * isEnabled,\n * setupTOTP,\n * verifySetup,\n * mfaMethods,\n * isLoading\n * } = useMFA();\n *\n * const [setupData, setSetupData] = useState(null);\n * const [verificationCode, setVerificationCode] = useState('');\n *\n * const handleSetupTOTP = async () => {\n * try {\n * const data = await setupTOTP();\n * setSetupData(data);\n * } catch (error) {\n * console.error('Setup failed:', error);\n * }\n * };\n *\n * const handleVerifySetup = async () => {\n * try {\n * await verifySetup('totp', verificationCode);\n * alert('MFA setup complete!');\n * setSetupData(null);\n * } catch (error) {\n * console.error('Verification failed:', error);\n * }\n * };\n *\n * if (isEnabled) {\n * return (\n * <div>\n * <h3>MFA is enabled</h3>\n * <p>Active methods: {mfaMethods.length}</p>\n * </div>\n * );\n * }\n *\n * return (\n * <div>\n * {!setupData ? (\n * <button onClick={handleSetupTOTP} disabled={isLoading}>\n * Setup Authenticator App\n * </button>\n * ) : (\n * <div>\n * <img src={setupData.qrCode} alt=\"QR Code\" />\n * <p>Secret: {setupData.secret}</p>\n * <input\n * value={verificationCode}\n * onChange={(e) => setVerificationCode(e.target.value)}\n * placeholder=\"Enter 6-digit code\"\n * />\n * <button onClick={handleVerifySetup}>\n * Verify & Enable\n * </button>\n * </div>\n * )}\n * </div>\n * );\n * }\n * ```\n *\n * @example MFA verification during login\n * ```tsx\n * function MFAVerification({ mfaToken, onSuccess }) {\n * const { verifyMFA, availableMethods } = useMFA();\n * const [selectedMethod, setSelectedMethod] = useState('totp');\n * const [code, setCode] = useState('');\n *\n * const handleVerify = async () => {\n * try {\n * const result = await verifyMFA(selectedMethod, code, mfaToken);\n * if (result.success) {\n * onSuccess(result.session);\n * }\n * } catch (error) {\n * console.error('MFA verification failed:', error);\n * }\n * };\n *\n * return (\n * <div>\n * <h3>Enter your verification code</h3>\n * <select\n * value={selectedMethod}\n * onChange={(e) => setSelectedMethod(e.target.value)}\n * >\n * {availableMethods.map(method => (\n * <option key={method} value={method}>\n * {MFA_METHOD_CONFIGS[method].name}\n * </option>\n * ))}\n * </select>\n * <input\n * value={code}\n * onChange={(e) => setCode(e.target.value)}\n * placeholder=\"Enter code\"\n * />\n * <button onClick={handleVerify}>Verify</button>\n * </div>\n * );\n * }\n * ```\n *\n * @example MFA method management\n * ```tsx\n * function MFAManagement() {\n * const {\n * mfaMethods,\n * removeMFAMethod,\n * setPrimaryMethod,\n * regenerateBackupCodes,\n * backupCodes\n * } = useMFA();\n *\n * return (\n * <div>\n * <h3>Your MFA Methods</h3>\n * {mfaMethods.map(method => (\n * <div key={method.id}>\n * <span>{method.type} - {method.name}</span>\n * {method.isPrimary && <span>(Primary)</span>}\n * <button onClick={() => setPrimaryMethod(method.id)}>\n * Set as Primary\n * </button>\n * <button onClick={() => removeMFAMethod(method.id)}>\n * Remove\n * </button>\n * </div>\n * ))}\n *\n * <h4>Backup Codes</h4>\n * <ul>\n * {backupCodes.map((code, index) => (\n * <li key={index}>{code}</li>\n * ))}\n * </ul>\n * <button onClick={regenerateBackupCodes}>\n * Generate New Backup Codes\n * </button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useMFA(): UseMFAReturn {\n const {user, session, reload, userType, sdk} = useAuth();\n const {apiUrl, publishableKey, features} = useConfig();\n\n const [mfaMethods, setMFAMethods] = useState<MFAMethod[]>([]);\n const [backupCodes, setBackupCodes] = useState<string[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<AuthError | null>(null);\n\n // Check if MFA is available\n const isMFAAvailable = useMemo(() => features.mfa, [features.mfa]);\n\n // Error handler\n const handleError = useCallback((err: any) => {\n const authError: AuthError = {\n code: err.code || 'UNKNOWN_ERROR',\n message: err.message || 'An unknown error occurred',\n details: err.details,\n field: err.field,\n };\n setError(authError);\n throw authError;\n }, []);\n\n // Load MFA methods and backup codes\n const loadMFAData = useCallback(async () => {\n if (!sdk.user || !user || !isMFAAvailable) return;\n\n try {\n setIsLoading(true);\n setError(null);\n\n // Load MFA methods\n const methods = await sdk.user.getMFAMethods({\n orgId: sdk.user.getOrganizationId(),\n userId: sdk.user.getUserData()\n });\n setMFAMethods(methods.data || []);\n\n // Load backup codes if MFA is enabled\n if (user.mfaEnabled) {\n try {\n const codes = await sdk.user.getBackupCodes();\n setBackupCodes(codes.codes || []);\n } catch (backupError) {\n // Backup codes might not be set up yet\n console.warn('Could not load backup codes:', backupError);\n }\n }\n } catch (err) {\n console.error('Failed to load MFA data:', err);\n setError({\n code: 'MFA_LOAD_FAILED',\n message: 'Failed to load MFA data',\n });\n } finally {\n setIsLoading(false);\n }\n }, [sdk.user, user, isMFAAvailable]);\n\n useEffect(() => {\n loadMFAData();\n }, [loadMFAData]);\n\n // MFA setup methods\n const setupTOTP = useCallback(async (): Promise<MFASetupData> => {\n if (!sdk.user) throw new Error('User not authenticated');\n if (!isMFAAvailable) throw new Error('MFA not available');\n\n try {\n setIsLoading(true);\n setError(null);\n\n const setupRequest: SetupMFARequest = {\n method: 'totp',\n };\n\n const response = await sdk.user.setupMFA(setupRequest);\n\n return {\n method: 'totp',\n qrCode: response.qrCode,\n secret: response.secret,\n backupCodes: response.backupCodes,\n verificationRequired: true,\n };\n } catch (err) {\n return handleError(err);\n } finally {\n setIsLoading(false);\n }\n }, [sdk.user, isMFAAvailable, handleError]);\n\n const setupSMS = useCallback(async (phoneNumber: string): Promise<MFASetupData> => {\n if (!sdk.user) throw new Error('User not authenticated');\n if (!isMFAAvailable) throw new Error('MFA not available');\n\n try {\n setIsLoading(true);\n setError(null);\n\n const setupRequest: SetupMFARequest = {\n method: 'sms',\n phoneNumber,\n };\n\n const response = await sdk.user.setupMFA(setupRequest);\n\n return {\n method: 'sms',\n verificationRequired: true,\n };\n } catch (err) {\n return handleError(err);\n } finally {\n setIsLoading(false);\n }\n }, [sdk.user, isMFAAvailable, handleError]);\n\n const setupEmail = useCallback(async (email?: string): Promise<MFASetupData> => {\n if (!sdk.user) throw new Error('User not authenticated');\n if (!isMFAAvailable) throw new Error('MFA not available');\n\n try {\n setIsLoading(true);\n setError(null);\n\n const setupRequest: SetupMFARequest = {\n method: 'email',\n email: email || user?.primaryEmailAddress,\n };\n\n const response = await sdk.user.setupMFA(setupRequest);\n\n return {\n method: 'email',\n verificationRequired: true,\n };\n } catch (err) {\n return handleError(err);\n } finally {\n setIsLoading(false);\n }\n }, [sdk.user, user, isMFAAvailable, handleError]);\n\n const setupWebAuthn = useCallback(async (): Promise<MFASetupData> => {\n if (!isMFAAvailable) throw new Error('MFA not available');\n\n try {\n setIsLoading(true);\n setError(null);\n\n const setupRequest: SetupMFARequest = {\n method: 'webauthn',\n };\n\n const response = await sdk.auth.setupMFA(setupRequest);\n\n return {\n method: 'webauthn',\n challenge: response.challenge,\n verificationRequired: true,\n };\n } catch (err) {\n return handleError(err);\n } finally {\n setIsLoading(false);\n }\n }, [sdk.user, isMFAAvailable, handleError]);\n\n // MFA verification during setup\n const verifySetup = useCallback(async (method: string, code: string, methodId?: string): Promise<MFAMethod> => {\n if (!sdk.user) throw new Error('User not authenticated');\n\n try {\n setIsLoading(true);\n setError(null);\n\n const verifyRequest: VerifyMFASetupRequest = {\n method,\n code,\n methodId,\n generateBackupCodes: false\n };\n\n const response = await sdk.user.verifyMFASetup(verifyRequest);\n\n // Refresh MFA data and user state\n await loadMFAData();\n await reload();\n\n return response.method;\n } catch (err) {\n return handleError(err);\n } finally {\n setIsLoading(false);\n }\n }, [sdk.user, loadMFAData, reload, handleError]);\n\n // MFA verification during authentication\n const verifyMFA = useCallback(async (method: string, code: string, token: string): Promise<MFAVerifyResponse> => {\n if (!sdk.user) throw new Error('User not authenticated');\n\n try {\n setIsLoading(true);\n setError(null);\n\n const verifyRequest: MFAVerifyRequest = {\n method,\n code,\n mfaToken: token,\n context: 'login',\n };\n\n const response = await sdk.auth.verifyMFA(verifyRequest);\n\n return response;\n } catch (err) {\n return handleError(err);\n } finally {\n setIsLoading(false);\n }\n }, [sdk.user, handleError]);\n\n // Method management\n const removeMFAMethod = useCallback(async (methodId: string): Promise<void> => {\n if (!sdk.user) throw new Error('User not authenticated');\n\n try {\n setIsLoading(true);\n setError(null);\n\n await sdk.user.removeMFAMethod(methodId);\n\n // Refresh MFA data\n await loadMFAData();\n await reload();\n } catch (err) {\n handleError(err);\n } finally {\n setIsLoading(false);\n }\n }, [sdk.user, loadMFAData, reload, handleError]);\n\n const setPrimaryMethod = useCallback(async (methodId: string): Promise<void> => {\n if (!sdk.user) throw new Error('User not authenticated');\n\n try {\n setIsLoading(true);\n setError(null);\n\n await sdk.user.setPrimaryMFAMethod(methodId);\n\n // Refresh MFA data\n await loadMFAData();\n } catch (err) {\n handleError(err);\n } finally {\n setIsLoading(false);\n }\n }, [sdk.user, loadMFAData, handleError]);\n\n const regenerateBackupCodes = useCallback(async (): Promise<string[]> => {\n if (!sdk.user) throw new Error('User not authenticated');\n\n try {\n setIsLoading(true);\n setError(null);\n\n const response = await sdk.user.regenerateMFABackupCodes();\n\n setBackupCodes(response.codes);\n return response.codes;\n } catch (err) {\n return handleError(err);\n } finally {\n setIsLoading(false);\n }\n }, [sdk.user, handleError]);\n\n // MFA status and method checking\n const isEnabled = useMemo(() => user?.mfaEnabled || false, [user]);\n const isRequired = useMemo(() => {\n // Check organization settings or user-specific requirements\n return false; // This would be determined by organization policy\n }, []);\n\n const primaryMethod = useMemo(() => {\n return mfaMethods.find(method => method.isPrimary) || null;\n }, [mfaMethods]);\n\n const hasTOTP = useMemo(() =>\n mfaMethods.some(method => method.type === 'totp'),\n [mfaMethods]\n );\n\n const hasSMS = useMemo(() =>\n mfaMethods.some(method => method.type === 'sms'),\n [mfaMethods]\n );\n\n const hasEmail = useMemo(() =>\n mfaMethods.some(method => method.type === 'email'),\n [mfaMethods]\n );\n\n const hasWebAuthn = useMemo(() =>\n mfaMethods.some(method => method.type === 'webauthn'),\n [mfaMethods]\n );\n\n const hasBackupCodes = useMemo(() =>\n backupCodes.length > 0,\n [backupCodes]\n );\n\n // Available methods (configured methods)\n const availableMethods = useMemo((): MFAMethodType[] => {\n const methods: MFAMethodType[] = [];\n\n if (hasTOTP) methods.push('totp');\n if (hasSMS) methods.push('sms');\n if (hasEmail) methods.push('email');\n if (hasWebAuthn) methods.push('webauthn');\n if (hasBackupCodes) methods.push('backup_codes');\n\n return methods;\n }, [hasTOTP, hasSMS, hasEmail, hasWebAuthn, hasBackupCodes]);\n\n // Convenience methods\n const disable = useCallback(async (): Promise<void> => {\n if (!sdk.user) throw new Error('User not authenticated');\n\n try {\n setIsLoading(true);\n setError(null);\n\n await sdk.user.disableMFA();\n\n // Clear local state\n setMFAMethods([]);\n setBackupCodes([]);\n\n await reload();\n } catch (err) {\n handleError(err);\n } finally {\n setIsLoading(false);\n }\n }, [sdk.user, reload, handleError]);\n\n const enable = useCallback(async (): Promise<void> => {\n if (!sdk.user) throw new Error('User not authenticated');\n\n try {\n setIsLoading(true);\n setError(null);\n\n await sdk.user.enableMFA();\n await reload();\n } catch (err) {\n handleError(err);\n } finally {\n setIsLoading(false);\n }\n }, [sdk.user, reload, handleError]);\n\n const refreshMethods = useCallback(async (): Promise<void> => {\n await loadMFAData();\n }, [loadMFAData]);\n\n return {\n // MFA state\n mfaMethods,\n isEnabled,\n isRequired,\n primaryMethod,\n backupCodes,\n isLoaded: !!user && isMFAAvailable,\n isLoading,\n error,\n\n // MFA setup\n setupTOTP,\n setupSMS,\n setupEmail,\n setupWebAuthn,\n\n // MFA verification\n verifySetup,\n verifyMFA,\n\n // Method management\n removeMFAMethod,\n setPrimaryMethod,\n regenerateBackupCodes,\n\n // MFA status checking\n hasTOTP,\n hasSMS,\n hasEmail,\n hasWebAuthn,\n hasBackupCodes,\n\n // Method availability\n availableMethods,\n\n // Convenience methods\n disable,\n enable,\n refreshMethods,\n };\n}\n\n// ============================================================================\n// Specialized MFA Hooks\n// ============================================================================\n\n/**\n * Hook for TOTP (Time-based One-Time Password) management\n */\nexport function useTOTP() {\n const {\n setupTOTP,\n verifySetup,\n hasTOTP,\n mfaMethods,\n removeMFAMethod,\n isLoading,\n error,\n } = useMFA();\n\n const totpMethod = useMemo(() =>\n mfaMethods.find(method => method.type === 'totp'),\n [mfaMethods]\n );\n\n return {\n isEnabled: hasTOTP,\n method: totpMethod,\n setup: setupTOTP,\n verify: (code: string) => verifySetup('totp', code),\n remove: totpMethod ? () => removeMFAMethod(totpMethod.id) : undefined,\n isLoading,\n error,\n };\n}\n\n/**\n * Hook for SMS MFA management\n */\nexport function useSMSMFA() {\n const {\n setupSMS,\n verifySetup,\n hasSMS,\n mfaMethods,\n removeMFAMethod,\n isLoading,\n error,\n } = useMFA();\n\n const smsMethod = useMemo(() =>\n mfaMethods.find(method => method.type === 'sms'),\n [mfaMethods]\n );\n\n return {\n isEnabled: hasSMS,\n method: smsMethod,\n setup: setupSMS,\n verify: (code: string) => verifySetup('sms', code),\n remove: smsMethod ? () => removeMFAMethod(smsMethod.id) : undefined,\n phoneNumber: smsMethod?.phoneNumber || null,\n isLoading,\n error,\n };\n}\n\n/**\n * Hook for backup codes management\n */\nexport function useBackupCodes() {\n const {\n backupCodes,\n regenerateBackupCodes,\n hasBackupCodes,\n isLoading,\n error,\n } = useMFA();\n\n const unusedCodes = useMemo(() =>\n backupCodes.filter(code => !code.used),\n [backupCodes]\n );\n\n const usedCodes = useMemo(() =>\n backupCodes.filter(code => code.used),\n [backupCodes]\n );\n\n return {\n codes: backupCodes,\n unusedCodes,\n usedCodes,\n hasBackupCodes,\n regenerate: regenerateBackupCodes,\n remainingCodes: unusedCodes.length,\n totalCodes: backupCodes.length,\n isRunningLow: unusedCodes.length <= 2,\n isLoading,\n error,\n };\n}"],"names":["MFA_METHOD_CONFIGS","useMFA","user","session","reload","userType","sdk","useAuth","apiUrl","publishableKey","features","useConfig","mfaMethods","setMFAMethods","useState","backupCodes","setBackupCodes","isLoading","setIsLoading","error","setError","isMFAAvailable","useMemo","handleError","useCallback","err","authError","loadMFAData","methods","codes","backupError","useEffect","setupTOTP","setupRequest","response","setupSMS","phoneNumber","setupEmail","email","setupWebAuthn","verifySetup","method","code","methodId","verifyRequest","verifyMFA","token","removeMFAMethod","setPrimaryMethod","regenerateBackupCodes","isEnabled","isRequired","primaryMethod","hasTOTP","hasSMS","hasEmail","hasWebAuthn","hasBackupCodes","availableMethods","disable","enable","refreshMethods","useTOTP","totpMethod","useSMSMFA","smsMethod","useBackupCodes","unusedCodes","usedCodes"],"mappings":"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"}