@frank-auth/react
Version:
Flexible and customizable React UI components for Frank Authentication
1 lines • 33.4 kB
Source Map (JSON)
{"version":3,"file":"use-passkeys.cjs","sources":["../../../src/hooks/use-passkeys.ts"],"sourcesContent":["/**\n * @frank-auth/react - usePasskeys Hook\n *\n * Comprehensive passkeys (WebAuthn/FIDO2) hook that provides passkey registration,\n * authentication, and management for passwordless authentication.\n */\n\nimport {useCallback, useEffect, useMemo, useState} from 'react';\n\nimport type {\n PasskeyAuthenticationFinishRequest,\n PasskeyRegistrationFinishRequest,\n PasskeySummary,\n UpdatePasskeyRequest,\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// Passkeys Hook Interface\n// ============================================================================\n\nexport interface UsePasskeysReturn {\n // Passkey state\n passkeys: PasskeySummary[];\n isSupported: boolean;\n isAvailable: boolean;\n isLoaded: boolean;\n isLoading: boolean;\n error: AuthError | null;\n\n // Passkey registration\n beginRegistration: (name?: string) => Promise<PasskeyRegistrationData>;\n finishRegistration: (registrationData: PasskeyRegistrationData, credential: any) => Promise<PasskeySummary>;\n registerPasskey: (name?: string) => Promise<PasskeySummary>;\n\n // Passkey authentication\n beginAuthentication: () => Promise<PasskeyAuthenticationData>;\n finishAuthentication: (authenticationData: PasskeyAuthenticationData, credential: any) => Promise<AuthenticationResult>;\n authenticateWithPasskey: () => Promise<AuthenticationResult>;\n\n // Passkey management\n updatePasskey: (passkeyId: string, updates: UpdatePasskeyRequest) => Promise<PasskeySummary>;\n deletePasskey: (passkeyId: string) => Promise<void>;\n renamePasskey: (passkeyId: string, name: string) => Promise<PasskeySummary>;\n\n // Passkey information\n primaryPasskey: PasskeySummary | null;\n passkeyCount: number;\n\n // Utility methods\n refreshPasskeys: () => Promise<void>;\n checkSupport: () => Promise<boolean>;\n}\n\nexport interface PasskeyRegistrationData {\n challenge: string;\n options: PublicKeyCredentialCreationOptions;\n sessionId?: string;\n}\n\nexport interface PasskeyAuthenticationData {\n challenge: string;\n options: PublicKeyCredentialRequestOptions;\n sessionId?: string;\n}\n\nexport interface AuthenticationResult {\n success: boolean;\n session?: any;\n user?: any;\n error?: string;\n}\n\n// ============================================================================\n// WebAuthn Utilities\n// ============================================================================\n\n/**\n * Check if WebAuthn is supported in the current browser\n */\nfunction isWebAuthnSupported(): boolean {\n return typeof window !== 'undefined' &&\n 'navigator' in window &&\n 'credentials' in navigator &&\n 'create' in navigator.credentials &&\n 'get' in navigator.credentials;\n}\n\n/**\n * Check if platform authenticator (like Touch ID, Face ID) is available\n */\nasync function isPlatformAuthenticatorAvailable(): Promise<boolean> {\n if (!isWebAuthnSupported()) return false;\n\n try {\n return await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();\n } catch {\n return false;\n }\n}\n\n/**\n * Convert base64url to ArrayBuffer\n */\nfunction base64urlToArrayBuffer(base64url: string): ArrayBuffer {\n const base64 = base64url.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64.padEnd(base64.length + (4 - base64.length % 4) % 4, '=');\n const binary = atob(padded);\n const buffer = new ArrayBuffer(binary.length);\n const bytes = new Uint8Array(buffer);\n\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n\n return buffer;\n}\n\n/**\n * Convert ArrayBuffer to base64url\n */\nfunction arrayBufferToBase64url(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n let binary = '';\n\n for (let i = 0; i < bytes.byteLength; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n\n return btoa(binary)\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=/g, '');\n}\n\n/**\n * Convert server credential creation options to browser format\n */\nfunction parseCredentialCreationOptions(options: any): PublicKeyCredentialCreationOptions {\n return {\n ...options,\n challenge: base64urlToArrayBuffer(options.challenge),\n user: {\n ...options.user,\n id: base64urlToArrayBuffer(options.user.id),\n },\n excludeCredentials: options.excludeCredentials?.map((cred: any) => ({\n ...cred,\n id: base64urlToArrayBuffer(cred.id),\n })),\n };\n}\n\n/**\n * Convert server credential request options to browser format\n */\nfunction parseCredentialRequestOptions(options: any): PublicKeyCredentialRequestOptions {\n return {\n ...options,\n challenge: base64urlToArrayBuffer(options.challenge),\n allowCredentials: options.allowCredentials?.map((cred: any) => ({\n ...cred,\n id: base64urlToArrayBuffer(cred.id),\n })),\n };\n}\n\n/**\n * Convert browser credential to server format\n */\nfunction serializeCredential(credential: PublicKeyCredential): any {\n const response = credential.response as AuthenticatorAttestationResponse | AuthenticatorAssertionResponse;\n\n const serialized: any = {\n id: credential.id,\n rawId: arrayBufferToBase64url(credential.rawId),\n type: credential.type,\n response: {\n clientDataJSON: arrayBufferToBase64url(response.clientDataJSON),\n },\n };\n\n if (response instanceof AuthenticatorAttestationResponse) {\n serialized.response.attestationObject = arrayBufferToBase64url(response.attestationObject);\n } else if (response instanceof AuthenticatorAssertionResponse) {\n serialized.response.authenticatorData = arrayBufferToBase64url(response.authenticatorData);\n serialized.response.signature = arrayBufferToBase64url(response.signature);\n\n if (response.userHandle) {\n serialized.response.userHandle = arrayBufferToBase64url(response.userHandle);\n }\n }\n\n return serialized;\n}\n\n// ============================================================================\n// Main usePasskeys Hook\n// ============================================================================\n\n/**\n * Comprehensive passkeys hook for WebAuthn/FIDO2 authentication\n *\n * @example Basic passkey registration\n * ```tsx\n * import { usePasskeys } from '@frank-auth/react';\n *\n * function PasskeySetup() {\n * const {\n * isSupported,\n * isAvailable,\n * registerPasskey,\n * passkeys,\n * isLoading\n * } = usePasskeys();\n *\n * const handleRegisterPasskey = async () => {\n * try {\n * const passkey = await registerPasskey('My Security Key');\n * console.log('Passkey registered:', passkey);\n * } catch (error) {\n * console.error('Registration failed:', error);\n * }\n * };\n *\n * if (!isSupported) {\n * return <div>Passkeys are not supported in this browser</div>;\n * }\n *\n * if (!isAvailable) {\n * return <div>No authenticators available</div>;\n * }\n *\n * return (\n * <div>\n * <h3>Your Passkeys ({passkeys.length})</h3>\n * {passkeys.map(passkey => (\n * <div key={passkey.id}>\n * <span>{passkey.name}</span>\n * <span>Created: {passkey.createdAt}</span>\n * </div>\n * ))}\n * <button onClick={handleRegisterPasskey} disabled={isLoading}>\n * Add New Passkey\n * </button>\n * </div>\n * );\n * }\n * ```\n *\n * @example Passkey authentication\n * ```tsx\n * function PasskeySignIn() {\n * const { authenticateWithPasskey, isSupported } = usePasskeys();\n *\n * const handleSignIn = async () => {\n * try {\n * const result = await authenticateWithPasskey();\n * if (result.success) {\n * console.log('Signed in successfully:', result.user);\n * }\n * } catch (error) {\n * console.error('Authentication failed:', error);\n * }\n * };\n *\n * if (!isSupported) {\n * return <div>Passkey authentication not supported</div>;\n * }\n *\n * return (\n * <button onClick={handleSignIn}>\n * Sign in with Passkey\n * </button>\n * );\n * }\n * ```\n *\n * @example Passkey management\n * ```tsx\n * function PasskeyManagement() {\n * const {\n * passkeys,\n * deletePasskey,\n * renamePasskey,\n * updatePasskey\n * } = usePasskeys();\n *\n * const [editingId, setEditingId] = useState(null);\n * const [newName, setNewName] = useState('');\n *\n * const handleRename = async (passkeyId) => {\n * try {\n * await renamePasskey(passkeyId, newName);\n * setEditingId(null);\n * setNewName('');\n * } catch (error) {\n * console.error('Rename failed:', error);\n * }\n * };\n *\n * const handleDelete = async (passkeyId) => {\n * if (confirm('Are you sure you want to delete this passkey?')) {\n * try {\n * await deletePasskey(passkeyId);\n * } catch (error) {\n * console.error('Delete failed:', error);\n * }\n * }\n * };\n *\n * return (\n * <div>\n * {passkeys.map(passkey => (\n * <div key={passkey.id}>\n * {editingId === passkey.id ? (\n * <div>\n * <input\n * value={newName}\n * onChange={(e) => setNewName(e.target.value)}\n * defaultValue={passkey.name}\n * />\n * <button onClick={() => handleRename(passkey.id)}>\n * Save\n * </button>\n * <button onClick={() => setEditingId(null)}>\n * Cancel\n * </button>\n * </div>\n * ) : (\n * <div>\n * <span>{passkey.name}</span>\n * <span>Last used: {passkey.lastUsedAt}</span>\n * <button onClick={() => {\n * setEditingId(passkey.id);\n * setNewName(passkey.name);\n * }}>\n * Rename\n * </button>\n * <button onClick={() => handleDelete(passkey.id)}>\n * Delete\n * </button>\n * </div>\n * )}\n * </div>\n * ))}\n * </div>\n * );\n * }\n * ```\n */\nexport function usePasskeys(): UsePasskeysReturn {\n const {user, sdk} = useAuth();\n const {apiUrl, publishableKey, features} = useConfig();\n\n const [passkeys, setPasskeys] = useState<PasskeySummary[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<AuthError | null>(null);\n const [isAvailable, setIsAvailable] = useState(false);\n\n // Check WebAuthn support\n const isSupported = useMemo(() => isWebAuthnSupported(), []);\n\n // Check if passkeys are available in the configuration\n const isPasskeysEnabled = useMemo(() => features.passkeys, [features.passkeys]);\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 // Check platform authenticator availability\n const checkSupport = useCallback(async (): Promise<boolean> => {\n if (!isSupported) return false;\n\n try {\n const available = await isPlatformAuthenticatorAvailable();\n setIsAvailable(available);\n return available;\n } catch {\n setIsAvailable(false);\n return false;\n }\n }, [isSupported]);\n\n // Load user's passkeys\n const loadPasskeys = useCallback(async () => {\n if (!sdk.user || !user || !isPasskeysEnabled) return;\n\n try {\n setIsLoading(true);\n setError(null);\n\n const response = await sdk.user.getPasskeys({fields: []});\n setPasskeys(response.data || []);\n } catch (err) {\n console.error('Failed to load passkeys:', err);\n setError({\n code: 'PASSKEYS_LOAD_FAILED',\n message: 'Failed to load passkeys',\n });\n } finally {\n setIsLoading(false);\n }\n }, [sdk.user, user, isPasskeysEnabled]);\n\n // Initialize hook\n useEffect(() => {\n checkSupport();\n loadPasskeys();\n }, [checkSupport, loadPasskeys]);\n\n // Begin passkey registration\n const beginRegistration = useCallback(async (name?: string): Promise<PasskeyRegistrationData> => {\n if (!sdk.user) throw new Error('User not authenticated');\n if (!isSupported) throw new Error('WebAuthn not supported');\n if (!isPasskeysEnabled) throw new Error('Passkeys not enabled');\n\n try {\n setIsLoading(true);\n setError(null);\n\n const response = await sdk.auth.beginPasskeyRegistration({\n name: name || `Passkey ${passkeys.length + 1}`,\n });\n\n const options = parseCredentialCreationOptions(response.options);\n\n return {\n challenge: response.challenge,\n options,\n sessionId: response.sessionId,\n };\n } catch (err) {\n return handleError(err);\n } finally {\n setIsLoading(false);\n }\n }, [sdk.auth, isSupported, isPasskeysEnabled, passkeys.length, handleError]);\n\n // Finish passkey registration\n const finishRegistration = useCallback(async (\n registrationData: PasskeyRegistrationData,\n credential: PublicKeyCredential\n ): Promise<PasskeySummary> => {\n if (!sdk.user) throw new Error('User not authenticated');\n\n try {\n setIsLoading(true);\n setError(null);\n\n const serializedCredential = serializeCredential(credential);\n\n const request: PasskeyRegistrationFinishRequest = {\n sessionId: registrationData.sessionId,\n credential: serializedCredential,\n };\n\n const response = await sdk.auth.finishPasskeyRegistration(request);\n\n // Refresh passkeys list\n await loadPasskeys();\n\n return response.passkey;\n } catch (err) {\n return handleError(err);\n } finally {\n setIsLoading(false);\n }\n }, [sdk.user, loadPasskeys, handleError]);\n\n // Complete passkey registration (convenience method)\n const registerPasskey = useCallback(async (name?: string): Promise<PasskeySummary> => {\n const registrationData = await beginRegistration(name);\n\n try {\n const credential = await navigator.credentials.create({\n publicKey: registrationData.options,\n }) as PublicKeyCredential;\n\n if (!credential) {\n throw new Error('Failed to create credential');\n }\n\n return await finishRegistration(registrationData, credential);\n } catch (err) {\n if (err.name === 'NotAllowedError') {\n throw new Error('User cancelled the registration process');\n } else if (err.name === 'InvalidStateError') {\n throw new Error('This authenticator is already registered');\n } else {\n throw new Error(`Registration failed: ${err.message}`);\n }\n }\n }, [beginRegistration, finishRegistration]);\n\n // Begin passkey authentication\n const beginAuthentication = useCallback(async (): Promise<PasskeyAuthenticationData> => {\n if (!sdk.auth) throw new Error('User not authenticated');\n if (!isSupported) throw new Error('WebAuthn not supported');\n\n try {\n setIsLoading(true);\n setError(null);\n\n const response = await sdk.auth.beginPasskeyAuthentication({});\n\n const options = parseCredentialRequestOptions(response.options);\n\n return {\n challenge: response.challenge,\n options,\n sessionId: response.sessionId,\n };\n } catch (err) {\n return handleError(err);\n } finally {\n setIsLoading(false);\n }\n }, [sdk.auth, isSupported, handleError]);\n\n // Finish passkey authentication\n const finishAuthentication = useCallback(async (\n authenticationData: PasskeyAuthenticationData,\n credential: PublicKeyCredential\n ): Promise<AuthenticationResult> => {\n if (!sdk.user) throw new Error('User not authenticated');\n\n try {\n setIsLoading(true);\n setError(null);\n\n const serializedCredential = serializeCredential(credential);\n\n const request: PasskeyAuthenticationFinishRequest = {\n sessionId: authenticationData.sessionId,\n credential: serializedCredential,\n };\n\n const response = await sdk.auth.finishPasskeyAuthentication(request);\n\n return {\n success: true,\n session: response.session,\n user: response.user,\n };\n } catch (err) {\n return {\n success: false,\n error: err.message,\n };\n } finally {\n setIsLoading(false);\n }\n }, [sdk.auth, handleError]);\n\n // Complete passkey authentication (convenience method)\n const authenticateWithPasskey = useCallback(async (): Promise<AuthenticationResult> => {\n const authenticationData = await beginAuthentication();\n\n try {\n const credential = await navigator.credentials.get({\n publicKey: authenticationData.options,\n }) as PublicKeyCredential;\n\n if (!credential) {\n throw new Error('Failed to get credential');\n }\n\n return await finishAuthentication(authenticationData, credential);\n } catch (err) {\n if (err.name === 'NotAllowedError') {\n return {\n success: false,\n error: 'User cancelled the authentication process',\n };\n } else {\n return {\n success: false,\n error: `Authentication failed: ${err.message}`,\n };\n }\n }\n }, [beginAuthentication, finishAuthentication]);\n\n // Update passkey\n const updatePasskey = useCallback(async (passkeyId: string, updates: UpdatePasskeyRequest): Promise<PasskeySummary> => {\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.updatePasskey(passkeyId, updates);\n\n // Refresh passkeys list\n await loadPasskeys();\n\n return response.passkey;\n } catch (err) {\n return handleError(err);\n } finally {\n setIsLoading(false);\n }\n }, [sdk.user, loadPasskeys, handleError]);\n\n // Delete passkey\n const deletePasskey = useCallback(async (passkeyId: 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.deletePasskey(passkeyId);\n\n // Refresh passkeys list\n await loadPasskeys();\n } catch (err) {\n handleError(err);\n } finally {\n setIsLoading(false);\n }\n }, [sdk.user, loadPasskeys, handleError]);\n\n // Rename passkey (convenience method)\n const renamePasskey = useCallback(async (passkeyId: string, name: string): Promise<PasskeySummary> => {\n return updatePasskey(passkeyId, {name});\n }, [updatePasskey]);\n\n // Refresh passkeys\n const refreshPasskeys = useCallback(async (): Promise<void> => {\n await loadPasskeys();\n }, [loadPasskeys]);\n\n // Computed properties\n const primaryPasskey = useMemo(() => {\n return passkeys.find(passkey => passkey.isPrimary) || passkeys[0] || null;\n }, [passkeys]);\n\n const passkeyCount = useMemo(() => passkeys.length, [passkeys]);\n\n return {\n // Passkey state\n passkeys,\n isSupported,\n isAvailable,\n isLoaded: !!user && isPasskeysEnabled,\n isLoading,\n error,\n\n // Passkey registration\n beginRegistration,\n finishRegistration,\n registerPasskey,\n\n // Passkey authentication\n beginAuthentication,\n finishAuthentication,\n authenticateWithPasskey,\n\n // Passkey management\n updatePasskey,\n deletePasskey,\n renamePasskey,\n\n // Passkey information\n primaryPasskey,\n passkeyCount,\n\n // Utility methods\n refreshPasskeys,\n checkSupport,\n };\n}\n\n// ============================================================================\n// Specialized Passkey Hooks\n// ============================================================================\n\n/**\n * Hook for passkey registration flow\n */\nexport function usePasskeyRegistration() {\n const {\n registerPasskey,\n isSupported,\n isAvailable,\n isLoading,\n error,\n } = usePasskeys();\n\n const [registrationState, setRegistrationState] = useState<'idle' | 'registering' | 'success' | 'error'>('idle');\n\n const register = useCallback(async (name?: string) => {\n if (!isSupported || !isAvailable) {\n setRegistrationState('error');\n throw new Error('Passkeys not supported or available');\n }\n\n try {\n setRegistrationState('registering');\n const passkey = await registerPasskey(name);\n setRegistrationState('success');\n return passkey;\n } catch (err) {\n setRegistrationState('error');\n throw err;\n }\n }, [registerPasskey, isSupported, isAvailable]);\n\n return {\n register,\n state: registrationState,\n isSupported,\n isAvailable,\n isLoading,\n error,\n canRegister: isSupported && isAvailable && !isLoading,\n };\n}\n\n/**\n * Hook for passkey authentication flow\n */\nexport function usePasskeyAuthentication() {\n const {\n authenticateWithPasskey,\n isSupported,\n isAvailable,\n isLoading,\n error,\n } = usePasskeys();\n\n const [authenticationState, setAuthenticationState] = useState<'idle' | 'authenticating' | 'success' | 'error'>('idle');\n\n const authenticate = useCallback(async () => {\n if (!isSupported || !isAvailable) {\n setAuthenticationState('error');\n throw new Error('Passkeys not supported or available');\n }\n\n try {\n setAuthenticationState('authenticating');\n const result = await authenticateWithPasskey();\n\n if (result.success) {\n setAuthenticationState('success');\n } else {\n setAuthenticationState('error');\n }\n\n return result;\n } catch (err) {\n setAuthenticationState('error');\n throw err;\n }\n }, [authenticateWithPasskey, isSupported, isAvailable]);\n\n return {\n authenticate,\n state: authenticationState,\n isSupported,\n isAvailable,\n isLoading,\n error,\n canAuthenticate: isSupported && isAvailable && !isLoading,\n };\n}"],"names":["isWebAuthnSupported","isPlatformAuthenticatorAvailable","base64urlToArrayBuffer","base64url","base64","padded","binary","buffer","bytes","i","arrayBufferToBase64url","parseCredentialCreationOptions","options","cred","parseCredentialRequestOptions","serializeCredential","credential","response","serialized","usePasskeys","user","sdk","useAuth","apiUrl","publishableKey","features","useConfig","passkeys","setPasskeys","useState","isLoading","setIsLoading","error","setError","isAvailable","setIsAvailable","isSupported","useMemo","isPasskeysEnabled","handleError","useCallback","err","authError","checkSupport","available","loadPasskeys","useEffect","beginRegistration","name","finishRegistration","registrationData","serializedCredential","request","registerPasskey","beginAuthentication","finishAuthentication","authenticationData","authenticateWithPasskey","updatePasskey","passkeyId","updates","deletePasskey","renamePasskey","refreshPasskeys","primaryPasskey","passkey","passkeyCount","usePasskeyRegistration","registrationState","setRegistrationState","usePasskeyAuthentication","authenticationState","setAuthenticationState","result"],"mappings":"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"}