refactor(auth): remove AALChecker and AALStatusDisplay components

This commit is contained in:
sharath 2025-07-19 22:10:12 +00:00
parent 0765c728ae
commit 7010de6c2c
No known key found for this signature in database
2 changed files with 0 additions and 366 deletions

View File

@ -1,144 +0,0 @@
"use client";
import { useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { useGetAAL } from '@/hooks/react-query/phone-verification';
import { useAuth } from '@/components/AuthProvider';
import { Loader2 } from 'lucide-react';
interface AALCheckerProps {
children: React.ReactNode;
redirectTo?: string;
}
/**
* AALChecker component that validates MFA requirements after authentication.
*
* This component follows the standard Supabase AAL flow AND enforces phone verification
* requirements for new users (created after cutoff date):
*
* For new users:
* - If no MFA enrolled: Force phone verification enrollment
* - If MFA enrolled but not verified: Redirect to verification
* - If MFA verified: Allow access
*
* For existing users (grandfathered):
* - Follow standard AAL flow without forcing enrollment
* - aal1 -> aal1: Allow access (optional MFA)
* - aal1 -> aal2: Redirect to verification
* - aal2 -> aal2: Allow access
* - aal2 -> aal1: Force reauthentication
*/
export function AALChecker({ children, redirectTo = '/auth/phone-verification' }: AALCheckerProps) {
const { user, isLoading: authLoading } = useAuth();
const { data: aalData, isLoading: aalLoading, error: aalError } = useGetAAL();
const router = useRouter();
useEffect(() => {
// Only check AAL if user is authenticated and AAL data is available
if (!authLoading && user && aalData && !aalLoading) {
const { action_required, current_level, next_level, verification_required } = aalData;
console.log('AAL Check:', {
action_required,
current_level,
next_level,
phone_verification_required: verification_required,
message: aalData.message
});
// Handle new users who need phone verification
if (verification_required) {
if (current_level === "aal1" && next_level === "aal1") {
// New user has no MFA enrolled - force enrollment
console.log('New user without MFA enrolled, redirecting to phone verification:', redirectTo);
router.push(redirectTo);
return;
}
// If new user has MFA enrolled, follow standard AAL flow below
}
// Standard AAL flow (for all users)
switch (action_required) {
case 'verify_mfa':
// User has MFA enrolled but needs to verify it
console.log('Redirecting to MFA verification:', redirectTo);
router.push(redirectTo);
break;
case 'reauthenticate':
// User has stale JWT due to MFA changes, force reauthentication
console.log('MFA state changed, forcing reauthentication');
router.push('/auth?message=Please sign in again due to security changes');
break;
case 'none':
// No action required, user can proceed
console.log('AAL check passed, no action required');
break;
case 'unknown':
default:
// Unknown AAL state, log and allow access (fail open)
console.warn('Unknown AAL state:', { current_level, next_level, action_required });
break;
}
}
}, [user, authLoading, aalData, aalLoading, router, redirectTo]);
// Show loading while checking authentication or AAL status
if (authLoading || (user && aalLoading)) {
return (
<div className="flex items-center justify-center min-h-screen">
<Loader2 className="h-8 w-8 animate-spin text-primary" />
<span className="ml-2 text-sm text-muted-foreground">Checking authentication...</span>
</div>
);
}
// If not authenticated, don't render children (let auth redirect handle it)
if (!user) {
return null;
}
// If AAL check failed, allow access (fail open for UX)
if (aalError) {
console.error('AAL check failed:', aalError);
}
// Check if new user needs phone verification enrollment
if (aalData?.verification_required &&
aalData?.current_level === "aal1" &&
aalData?.next_level === "aal1") {
return (
<div className="flex items-center justify-center min-h-screen">
<Loader2 className="h-8 w-8 animate-spin text-primary" />
<span className="ml-2 text-sm text-muted-foreground">Setting up required verification...</span>
</div>
);
}
// If AAL check indicates MFA verification is needed, don't render children
// (the useEffect above will handle the redirect)
if (aalData?.action_required === 'verify_mfa') {
return (
<div className="flex items-center justify-center min-h-screen">
<Loader2 className="h-8 w-8 animate-spin text-primary" />
<span className="ml-2 text-sm text-muted-foreground">Redirecting to verification...</span>
</div>
);
}
// If AAL check indicates reauthentication is needed, don't render children
if (aalData?.action_required === 'reauthenticate') {
return (
<div className="flex items-center justify-center min-h-screen">
<Loader2 className="h-8 w-8 animate-spin text-primary" />
<span className="ml-2 text-sm text-muted-foreground">Redirecting to sign in...</span>
</div>
);
}
// AAL check passed or no action required, render children
return <>{children}</>;
}

View File

@ -1,222 +0,0 @@
"use client";
import { useGetAAL } from '@/hooks/react-query/phone-verification';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { Loader2, Shield, ShieldAlert, ShieldCheck, ShieldX, Phone } from 'lucide-react';
/**
* AALStatusDisplay component shows the current AAL status and what action is required.
* Useful for debugging and understanding the MFA flow.
*/
export function AALStatusDisplay() {
const { data: aalData, isLoading: aalLoading, error: aalError } = useGetAAL();
if (aalLoading) {
return (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Shield className="w-5 h-5" />
MFA Status
</CardTitle>
</CardHeader>
<CardContent>
<div className="flex items-center gap-2">
<Loader2 className="w-4 h-4 animate-spin" />
<span>Checking MFA status...</span>
</div>
</CardContent>
</Card>
);
}
if (aalError) {
return (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<ShieldX className="w-5 h-5 text-red-500" />
MFA Status Error
</CardTitle>
</CardHeader>
<CardContent>
<Alert variant="destructive">
<AlertDescription>
Failed to check MFA status: {aalError instanceof Error ? aalError?.message : 'Unknown error'}
</AlertDescription>
</Alert>
</CardContent>
</Card>
);
}
if (!aalData) {
return null;
}
const getStatusIcon = () => {
// Check if new user needs phone verification enrollment
if (aalData?.phone_verification_required &&
aalData.current_level === "aal1" &&
aalData.next_level === "aal1") {
return <Phone className="w-5 h-5 text-orange-500" />;
}
switch (aalData?.action_required) {
case 'none':
return <ShieldCheck className="w-5 h-5 text-green-500" />;
case 'verify_mfa':
return <ShieldAlert className="w-5 h-5 text-yellow-500" />;
case 'reauthenticate':
return <ShieldX className="w-5 h-5 text-red-500" />;
default:
return <Shield className="w-5 h-5 text-gray-500" />;
}
};
const getStatusColor = () => {
// Check if new user needs phone verification enrollment
if (aalData?.phone_verification_required &&
aalData.current_level === "aal1" &&
aalData.next_level === "aal1") {
return 'bg-orange-100 text-orange-800';
}
switch (aalData?.action_required) {
case 'none':
return 'bg-green-100 text-green-800';
case 'verify_mfa':
return 'bg-yellow-100 text-yellow-800';
case 'reauthenticate':
return 'bg-red-100 text-red-800';
default:
return 'bg-gray-100 text-gray-800';
}
};
const getActionDescription = () => {
const current = aalData?.current_level;
const next = aalData?.next_level;
const isNewUser = aalData?.phone_verification_required;
if (current === 'aal1' && next === 'aal1') {
if (isNewUser) {
return 'As a new user, you are required to set up phone verification for enhanced security.';
} else {
return 'MFA is not enrolled for this account. You can optionally set up MFA for enhanced security.';
}
} else if (current === 'aal1' && next === 'aal2') {
return 'MFA is enrolled but not verified. Please complete MFA verification to access all features.';
} else if (current === 'aal2' && next === 'aal2') {
return 'MFA is fully verified and active. Your account has enhanced security.';
} else if (current === 'aal2' && next === 'aal1') {
return 'MFA settings have changed. Please sign in again to refresh your session.';
} else {
return `Unknown AAL combination: ${current}${next}`;
}
};
const getEffectiveAction = () => {
// Check if new user needs phone verification enrollment
if (aalData?.phone_verification_required &&
aalData.current_level === "aal1" &&
aalData.next_level === "aal1") {
return 'ENROLL MFA';
}
return aalData?.action_required?.replace('_', ' ').toUpperCase() || 'UNKNOWN';
};
return (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
{getStatusIcon()}
Multi-Factor Authentication Status
</CardTitle>
<CardDescription>
Current security level and required actions
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div>
<label className="text-sm font-medium text-gray-600">Current Level</label>
<Badge variant="outline" className="ml-2">
{aalData.current_level?.toUpperCase() || 'Unknown'}
</Badge>
</div>
<div>
<label className="text-sm font-medium text-gray-600">Next Level</label>
<Badge variant="outline" className="ml-2">
{aalData.next_level?.toUpperCase() || 'Unknown'}
</Badge>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="text-sm font-medium text-gray-600">Action Required</label>
<Badge className={`ml-2 ${getStatusColor()}`}>
{getEffectiveAction()}
</Badge>
</div>
<div>
<label className="text-sm font-medium text-gray-600">User Type</label>
<Badge variant="outline" className="ml-2">
{aalData?.phone_verification_required ? 'NEW USER' : 'EXISTING USER'}
</Badge>
</div>
</div>
{aalData && (
<div>
<label className="text-sm font-medium text-gray-600">Phone Verification Required</label>
<Badge
variant={aalData.phone_verification_required ? "destructive" : "secondary"}
className="ml-2"
>
{aalData.phone_verification_required ? 'YES' : 'NO'}
</Badge>
{aalData.user_created_at && (
<p className="text-xs text-gray-500 mt-1">
Account created: {new Date(aalData.user_created_at).toLocaleDateString()}
</p>
)}
</div>
)}
{aalData.current_authentication_methods && aalData.current_authentication_methods.length > 0 && (
<div>
<label className="text-sm font-medium text-gray-600">Authentication Methods</label>
<div className="flex flex-wrap gap-1 mt-1">
{aalData.current_authentication_methods.map((method, index) => (
<Badge key={index} variant="secondary" className="text-xs">
{method}
</Badge>
))}
</div>
</div>
)}
<Alert>
<AlertDescription>
{aalData.message || getActionDescription()}
</AlertDescription>
</Alert>
<div className="text-xs text-gray-500 bg-gray-50 p-3 rounded">
<strong>AAL Flow Reference:</strong>
<ul className="mt-1 space-y-1">
<li> aal1 aal1: No MFA enrolled{aalData?.phone_verification_required ? ' (new users must enroll)' : ' (optional for existing users)'}</li>
<li> aal1 aal2: MFA enrolled, verification required</li>
<li> aal2 aal2: MFA verified and active</li>
<li> aal2 aal1: MFA disabled, reauthentication needed</li>
</ul>
</div>
</CardContent>
</Card>
);
}