'use client';
import { useMfa } from 'authsafe-nextjs/client';interface UseMfaReturn {
methods: MfaMethod[];
isLoading: boolean;
error: Error | null;
fetchMethods: () => Promise<void>;
registerMethod: (type: MfaMethodType) => Promise<any>;
confirmMethod: (data: any) => Promise<any>;
removeMethod: (methodId: string) => Promise<void>;
makePrimary: (methodId: string) => Promise<void>;
}methods - List of configured MFA methods
interface MfaMethod {
id: string;
type: MfaMethodType;
isEnabled: boolean;
isPrimary: boolean;
}
type MfaMethodType = 'TOTP' | 'WEBAUTHN' | 'EMAIL_OTP' | 'BACKUP_CODE';isLoading - Whether MFA operation is in progress
error - Error from last MFA operation
fetchMethods() - Fetch all MFA methods for current user
registerMethod(type) - Start registration for new MFA method
confirmMethod(data) - Confirm MFA method registration
removeMethod(methodId) - Remove an MFA method
makePrimary(methodId) - Set method as primary
'use client';
import { useMfa } from 'authsafe-nextjs/client';
import { useEffect } from 'react';
export function MfaList() {
const { methods, isLoading, fetchMethods } = useMfa();
useEffect(() => {
fetchMethods();
}, []);
if (isLoading) {
return <div>Loading MFA methods...</div>;
}
return (
<div>
<h2>Your MFA Methods</h2>
{methods.length === 0 ? (
<p>No MFA methods configured</p>
) : (
<ul>
{methods.map((method) => (
<li key={method.id}>
{method.type} {method.isPrimary && '(Primary)'}
</li>
))}
</ul>
)}
</div>
);
}'use client';
import { useMfa } from 'authsafe-nextjs/client';
import { useState } from 'react';
export function EnableTOTP() {
const { registerMethod, confirmMethod } = useMfa();
const [qrCode, setQrCode] = useState<string | null>(null);
const [code, setCode] = useState('');
const handleRegister = async () => {
const result = await registerMethod('TOTP');
setQrCode(result.qrCode);
};
const handleConfirm = async () => {
await confirmMethod({ code });
alert('TOTP enabled!');
setQrCode(null);
};
if (!qrCode) {
return <button onClick={handleRegister}>Enable TOTP</button>;
}
return (
<div>
<h3>Scan this QR code with your authenticator app</h3>
<img src={qrCode} alt="TOTP QR Code" />
<input
type="text"
value={code}
onChange={(e) => setCode(e.target.value)}
placeholder="Enter 6-digit code"
/>
<button onClick={handleConfirm}>Verify</button>
</div>
);
}'use client';
import { useMfa } from 'authsafe-nextjs/client';
import { useState } from 'react';
export function EnableEmailOTP() {
const { registerMethod, confirmMethod } = useMfa();
const [sent, setSent] = useState(false);
const [code, setCode] = useState('');
const handleSend = async () => {
await registerMethod('EMAIL_OTP');
setSent(true);
};
const handleVerify = async () => {
await confirmMethod({ code });
alert('Email OTP enabled!');
setSent(false);
};
if (!sent) {
return <button onClick={handleSend}>Enable Email OTP</button>;
}
return (
<div>
<p>Check your email for verification code</p>
<input
type="text"
value={code}
onChange={(e) => setCode(e.target.value)}
placeholder="Enter code from email"
/>
<button onClick={handleVerify}>Verify</button>
</div>
);
}'use client';
import { useMfa } from 'authsafe-nextjs/client';
export function RemoveMfaButton({ methodId }: { methodId: string }) {
const { removeMethod } = useMfa();
const handleRemove = async () => {
if (confirm('Are you sure you want to remove this MFA method?')) {
await removeMethod(methodId);
alert('MFA method removed');
}
};
return <button onClick={handleRemove}>Remove</button>;
}'use client';
import { useMfa } from 'authsafe-nextjs/client';
export function MakePrimaryButton({ methodId }: { methodId: string }) {
const { makePrimary } = useMfa();
const handleMakePrimary = async () => {
await makePrimary(methodId);
alert('Primary method updated');
};
return <button onClick={handleMakePrimary}>Make Primary</button>;
}'use client';
import { useMfa } from 'authsafe-nextjs/client';
import { useEffect, useState } from 'react';
export function MfaSettings() {
const {
methods,
isLoading,
error,
fetchMethods,
registerMethod,
confirmMethod,
removeMethod,
makePrimary,
} = useMfa();
const [registering, setRegistering] = useState<string | null>(null);
const [qrCode, setQrCode] = useState<string | null>(null);
const [verificationCode, setVerificationCode] = useState('');
useEffect(() => {
fetchMethods();
}, []);
const handleRegister = async (type: 'TOTP' | 'EMAIL_OTP') => {
setRegistering(type);
const result = await registerMethod(type);
if (type === 'TOTP') {
setQrCode(result.qrCode);
}
};
const handleConfirm = async () => {
await confirmMethod({ code: verificationCode });
setRegistering(null);
setQrCode(null);
setVerificationCode('');
await fetchMethods();
};
if (isLoading && methods.length === 0) {
return <div>Loading...</div>;
}
if (error) {
return <div className="text-red-600">Error: {error.message}</div>;
}
return (
<div className="max-w-2xl mx-auto p-6">
<h1 className="text-2xl font-bold mb-6">Multi-Factor Authentication</h1>
{/* Existing Methods */}
{methods.length > 0 && (
<div className="mb-8">
<h2 className="text-lg font-semibold mb-4">Active Methods</h2>
<div className="space-y-3">
{methods.map((method) => (
<div
key={method.id}
className="flex items-center justify-between p-4 border rounded-lg"
>
<div>
<span className="font-medium">{method.type}</span>
{method.isPrimary && (
<span className="ml-2 px-2 py-1 text-xs bg-blue-100 text-blue-800 rounded">
Primary
</span>
)}
</div>
<div className="space-x-2">
{!method.isPrimary && (
<button
onClick={() => makePrimary(method.id)}
className="px-3 py-1 text-sm text-blue-600 hover:bg-blue-50 rounded"
>
Make Primary
</button>
)}
<button
onClick={() => removeMethod(method.id)}
className="px-3 py-1 text-sm text-red-600 hover:bg-red-50 rounded"
>
Remove
</button>
</div>
</div>
))}
</div>
</div>
)}
{/* Registration UI */}
{registering ? (
<div className="border rounded-lg p-6">
<h3 className="text-lg font-semibold mb-4">Set up {registering}</h3>
{qrCode && (
<div className="mb-4">
<p className="mb-2">
Scan this QR code with your authenticator app:
</p>
<img src={qrCode} alt="QR Code" className="max-w-xs" />
</div>
)}
{registering === 'EMAIL_OTP' && (
<p className="mb-4">Check your email for the verification code.</p>
)}
<input
type="text"
value={verificationCode}
onChange={(e) => setVerificationCode(e.target.value)}
placeholder="Enter verification code"
className="w-full px-3 py-2 border rounded mb-4"
/>
<div className="flex gap-2">
<button
onClick={handleConfirm}
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
>
Verify
</button>
<button
onClick={() => {
setRegistering(null);
setQrCode(null);
setVerificationCode('');
}}
className="px-4 py-2 bg-gray-200 rounded hover:bg-gray-300"
>
Cancel
</button>
</div>
</div>
) : (
<div>
<h3 className="text-lg font-semibold mb-4">Add New Method</h3>
<div className="flex gap-4">
<button
onClick={() => handleRegister('TOTP')}
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
>
Add Authenticator App
</button>
<button
onClick={() => handleRegister('EMAIL_OTP')}
className="px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700"
>
Add Email OTP
</button>
</div>
</div>
)}
</div>
);
}useMfa() hook requires the user to be authenticated. Always check isAuthenticated before showing MFA settings:
'use client';
import { useAuth, useMfa } from 'authsafe-nextjs/client';
export function MfaPage() {
const { isAuthenticated } = useAuth();
if (!isAuthenticated) {
return <div>Please sign in to manage MFA settings</div>;
}
return <MfaSettings />;
}