feat: Implement MFA Step-Up for OAuth

This commit is contained in:
gpt-engineer-app[bot]
2025-10-14 13:52:11 +00:00
parent 7aa219efe5
commit ccfa83faee
6 changed files with 225 additions and 9 deletions

View File

@@ -5,6 +5,12 @@ import type { Profile } from '@/types/database';
import { toast } from '@/hooks/use-toast';
import { authLog, authWarn, authError } from '@/lib/authLogger';
export interface CheckAalResult {
needsStepUp: boolean;
hasMfaEnrolled: boolean;
currentLevel: 'aal1' | 'aal2' | null;
}
interface AuthContextType {
user: User | null;
session: Session | null;
@@ -15,6 +21,7 @@ interface AuthContextType {
signOut: () => Promise<void>;
verifySession: () => Promise<boolean>;
clearPendingEmail: () => void;
checkAalStepUp: () => Promise<CheckAalResult>;
}
const AuthContext = createContext<AuthContextType | undefined>(undefined);
@@ -221,6 +228,29 @@ function AuthProviderComponent({ children }: { children: React.ReactNode }) {
setPendingEmail(null);
};
const checkAalStepUp = async (): Promise<CheckAalResult> => {
if (!session?.user) {
return { needsStepUp: false, hasMfaEnrolled: false, currentLevel: null };
}
try {
const { data: { currentLevel } } =
await supabase.auth.mfa.getAuthenticatorAssuranceLevel();
const { data: factors } = await supabase.auth.mfa.listFactors();
const hasMfaEnrolled = factors?.totp?.some(f => f.status === 'verified') || false;
return {
needsStepUp: hasMfaEnrolled && currentLevel === 'aal1',
hasMfaEnrolled,
currentLevel: currentLevel as 'aal1' | 'aal2' | null,
};
} catch (error) {
authError('[Auth] Failed to check AAL status:', error);
return { needsStepUp: false, hasMfaEnrolled: false, currentLevel: null };
}
};
const value = {
user,
session,
@@ -231,6 +261,7 @@ function AuthProviderComponent({ children }: { children: React.ReactNode }) {
signOut,
verifySession,
clearPendingEmail,
checkAalStepUp,
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;