'use client' import { useEffect, useState } from 'react' import { useRouter } from '../contexts/RouterContext' import { useAllauthAPI } from '../contexts/APIContext' import { useAllauthConfig } from '../contexts/ConfigContext' import { DjangoFlowPaths } from '../config' import { AuthCard } from './AuthCard' import { AuthDjangoForm } from './AuthDjangoForm' interface AllauthRouterProps { /** Called after successful completion of any flow */ onComplete?: () => void /** Called when user wants to go back to login */ onLoginClick?: () => void } /** * AllauthRouter handles Django-initiated flows (email verification, password reset, OAuth). * * Mount this at a catch-all route matching your basePath config: * app/auth/[...path]/page.tsx -> * * The path determines which flow to render: * /auth/verify-email/[key] -> Email verification * /auth/reset-password?key=xxx -> Password reset form * /auth/oauth/callback -> OAuth completion */ export function AllauthRouter({ onComplete, onLoginClick }: AllauthRouterProps) { const router = useRouter() const config = useAllauthConfig() // Parse the path segments after basePath // The router provides getParam('path') which returns the catch-all segments const pathParam = router.getParam('path') const pathSegments = Array.isArray(pathParam) ? pathParam : pathParam ? [pathParam] : [] const path = pathSegments.length > 0 ? `/${pathSegments.join('/')}` : '/' // Determine which flow based on path if (path.startsWith(DjangoFlowPaths.VERIFY_EMAIL)) { const key = pathSegments[1] || router.searchParams.get('key') return ( ) } if (path.startsWith(DjangoFlowPaths.RESET_PASSWORD)) { const key = pathSegments[1] || router.searchParams.get('key') return ( ) } if (path.startsWith(DjangoFlowPaths.OAUTH_ERROR)) { return ( ) } // Unknown path return ( ) } // ---------------------------------------------------------------------------- // Email Verification View // ---------------------------------------------------------------------------- interface EmailVerifyViewProps { verificationKey: string | null | undefined onComplete?: () => void onLoginClick?: () => void } function EmailVerifyView({ verificationKey, onComplete, onLoginClick }: EmailVerifyViewProps) { const api = useAllauthAPI() const [status, setStatus] = useState<'loading' | 'success' | 'error'>('loading') const [error, setError] = useState('') useEffect(() => { if (!verificationKey) { setStatus('error') setError('Invalid verification link') return } const verify = async () => { const res = await api.account.emails.verification.confirmKey(verificationKey) if (res.status === 200) { setStatus('success') if (onComplete) { setTimeout(onComplete, 2000) } } else { setStatus('error') setError(res.errors?.[0]?.message || 'Invalid or expired verification link') } } verify() }, [verificationKey, api, onComplete]) if (status === 'loading') { return } if (status === 'success') { return ( ) } return ( ) } // ---------------------------------------------------------------------------- // Password Reset View // ---------------------------------------------------------------------------- interface PasswordResetViewProps { resetKey: string | null | undefined onComplete?: () => void onLoginClick?: () => void } function PasswordResetView({ resetKey, onComplete, onLoginClick }: PasswordResetViewProps) { const [success, setSuccess] = useState(false) if (!resetKey) { return ( ) } if (success) { return ( ) } return ( { setSuccess(true) // Give user time to see success message before redirect if (onComplete) { setTimeout(onComplete, 2000) } }} footerLinks={onLoginClick ? [ { href: '#', label: 'Back to Sign In', onClick: onLoginClick }, ] : []} /> ) } // ---------------------------------------------------------------------------- // OAuth Error View // ---------------------------------------------------------------------------- interface OAuthErrorViewProps { onLoginClick?: () => void } function OAuthErrorView({ onLoginClick }: OAuthErrorViewProps) { const router = useRouter() const error = router.searchParams.get('error') || 'An error occurred during authentication' return ( ) }