UNPKG

@frank-auth/react

Version:

Flexible and customizable React UI components for Frank Authentication

282 lines 8.24 kB
import { AuthError } from '../provider/types'; export interface UseMagicLinkReturn { isLoading: boolean; error: AuthError | null; lastSentEmail: string | null; lastSentAt: Date | null; canResend: boolean; timeUntilResend: number; sendMagicLink: (email: string, options?: MagicLinkOptions) => Promise<MagicLinkSendResult>; verifyMagicLink: (token: string) => Promise<MagicLinkVerifyResult>; resendMagicLink: () => Promise<MagicLinkSendResult>; verifyFromUrl: (url?: string) => Promise<MagicLinkVerifyResult>; extractTokenFromUrl: (url?: string) => string | null; isValidEmail: (email: string) => boolean; clearState: () => void; } export interface MagicLinkOptions { redirectUrl?: string; organizationId?: string; customData?: Record<string, any>; template?: string; expiresIn?: number; locale?: string; } export interface MagicLinkSendResult { success: boolean; email: string; message: string; expiresAt: Date; error?: string; } export interface MagicLinkVerifyResult { success: boolean; user?: any; session?: any; error?: string; requiresAdditionalVerification?: boolean; mfaToken?: string; } export declare const MAGIC_LINK_CONFIG: { readonly DEFAULT_EXPIRES_IN: number; readonly RESEND_COOLDOWN: 60; readonly EMAIL_REGEX: RegExp; readonly TEMPLATES: { readonly SIGN_IN: "magic-link-sign-in"; readonly SIGN_UP: "magic-link-sign-up"; readonly VERIFY_EMAIL: "magic-link-verify-email"; readonly PASSWORD_RESET: "magic-link-password-reset"; }; readonly URL_PARAMS: { readonly TOKEN: "token"; readonly EMAIL: "email"; readonly TYPE: "type"; readonly REDIRECT: "redirect_to"; }; }; /** * Magic link authentication hook for passwordless email authentication * * @example Basic magic link sign-in * ```tsx * import { useMagicLink } from '@frank-auth/react'; * * function MagicLinkSignIn() { * const { * sendMagicLink, * isLoading, * error, * lastSentEmail, * canResend, * resendMagicLink, * isValidEmail * } = useMagicLink(); * * const [email, setEmail] = useState(''); * const [sent, setSent] = useState(false); * * const handleSend = async () => { * if (!isValidEmail(email)) { * alert('Please enter a valid email address'); * return; * } * * try { * const result = await sendMagicLink(email, { * redirectUrl: '/dashboard', * template: 'sign-in' * }); * * if (result.success) { * setSent(true); * } * } catch (error) { * console.error('Failed to send magic link:', error); * } * }; * * const handleResend = async () => { * try { * await resendMagicLink(); * } catch (error) { * console.error('Failed to resend magic link:', error); * } * }; * * if (sent) { * return ( * <div> * <h3>Check your email</h3> * <p>We sent a magic link to {lastSentEmail}</p> * <p>Click the link in your email to sign in.</p> * * {canResend ? ( * <button onClick={handleResend} disabled={isLoading}> * Resend magic link * </button> * ) : ( * <p>You can resend the link in a few seconds</p> * )} * * {error && <p style={{color: 'red'}}>{error.message}</p>} * </div> * ); * } * * return ( * <div> * <h3>Sign in with magic link</h3> * <input * type="email" * value={email} * onChange={(e) => setEmail(e.target.value)} * placeholder="Enter your email address" * disabled={isLoading} * /> * <button onClick={handleSend} disabled={isLoading || !email}> * {isLoading ? 'Sending...' : 'Send magic link'} * </button> * * {error && <p style={{color: 'red'}}>{error.message}</p>} * </div> * ); * } * ``` * * @example Magic link verification page * ```tsx * import { useEffect, useState } from 'react'; * import { useMagicLink } from '@frank-auth/react'; * import { useSearchParams, useNavigate } from 'react-router-dom'; * * function MagicLinkVerify() { * const { verifyFromUrl, isLoading } = useMagicLink(); * const [searchParams] = useSearchParams(); * const navigate = useNavigate(); * const [status, setStatus] = useState('verifying'); * * useEffect(() => { * const verify = async () => { * try { * const result = await verifyFromUrl(); * * if (result.success) { * setStatus('success'); * * // Check for MFA requirement * if (result.requiresAdditionalVerification) { * navigate('/mfa', { state: { mfaToken: result.mfaToken } }); * } else { * // Redirect to dashboard or intended page * const redirectTo = searchParams.get('redirect_to') || '/dashboard'; * navigate(redirectTo); * } * } else { * setStatus('error'); * } * } catch (error) { * console.error('Magic link verification failed:', error); * setStatus('error'); * } * }; * * verify(); * }, [verifyFromUrl, navigate, searchParams]); * * if (isLoading || status === 'verifying') { * return <div>Verifying magic link...</div>; * } * * if (status === 'success') { * return <div>Success! Redirecting...</div>; * } * * return ( * <div> * <h3>Invalid or expired magic link</h3> * <p>The magic link you clicked is invalid or has expired.</p> * <button onClick={() => navigate('/sign-in')}> * Try again * </button> * </div> * ); * } * ``` * * @example Magic link with organization invitation * ```tsx * function OrganizationInvite({ invitationToken, organizationId }) { * const { sendMagicLink } = useMagicLink(); * const [email, setEmail] = useState(''); * * const handleAcceptInvite = async () => { * try { * await sendMagicLink(email, { * organizationId, * customData: { * invitationToken, * action: 'accept_invitation' * }, * template: 'organization-invite', * redirectUrl: `/organizations/${organizationId}/welcome` * }); * } catch (error) { * console.error('Failed to send invitation magic link:', error); * } * }; * * return ( * <div> * <h3>Join Organization</h3> * <p>Enter your email to receive a secure link to join the organization.</p> * <input * type="email" * value={email} * onChange={(e) => setEmail(e.target.value)} * placeholder="Enter your email address" * /> * <button onClick={handleAcceptInvite}> * Send invitation link * </button> * </div> * ); * } * ``` */ export declare function useMagicLink(): UseMagicLinkReturn; /** * Hook for magic link sign-in flow */ export declare function useMagicLinkSignIn(): { signIn: (email: string, redirectUrl?: string) => Promise<MagicLinkSendResult>; resend: () => Promise<MagicLinkSendResult>; reset: () => void; state: "idle" | "verified" | "email_sent"; sentTo: string | null; canResend: boolean; isLoading: boolean; error: AuthError | null; isValidEmail: (email: string) => boolean; }; /** * Hook for magic link verification flow */ export declare function useMagicLinkVerification(): { verify: (token?: string) => Promise<MagicLinkVerifyResult>; state: "idle" | "success" | "error" | "verifying"; result: MagicLinkVerifyResult | null; isVerifying: boolean; isSuccess: boolean; isError: boolean; error: string | AuthError | undefined; requiresMFA: boolean | undefined; mfaToken: string | undefined; }; /** * Hook for magic link password reset flow */ export declare function useMagicLinkPasswordReset(): { sendResetLink: (email: string) => Promise<MagicLinkSendResult>; isValidEmail: (email: string) => boolean; }; //# sourceMappingURL=use-magic-link.d.ts.map