'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 (
)
}