File size: 4,775 Bytes
9a92a42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import { useState } from 'react';
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { AlertCircle, CheckCircle2, Loader2, QrCode } from 'lucide-react';
import authApi from '@/api/auth.api';

interface TwoFactorSetupProps {
  onComplete?: () => void;
}

export default function TwoFactorSetup({ onComplete }: TwoFactorSetupProps) {
  const [step, setStep] = useState<'initial' | 'setup' | 'verify' | 'success'>('initial');
  const [qrCode, setQrCode] = useState<string>('');
  const [otp, setOtp] = useState<string>('');
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const startSetup = async () => {
    setIsLoading(true);
    setError(null);
    try {
      const data = await authApi.setup2FA();
      setQrCode(data.qrCode);
      setStep('setup');
    } catch (err: any) {
      setError(err.response?.data?.message || "Failed to start 2FA setup.");
    } finally {
      setIsLoading(false);
    }
  };

  const verifySetup = async () => {
    if (!otp) return;
    setIsLoading(true);
    setError(null);
    try {
      await authApi.verify2FA(otp);
      setStep('success');
    } catch (err: any) {
      setError(err.response?.data?.message || "Invalid OTP. Please try again.");
    } finally {
      setIsLoading(false);
    }
  };

  const handleComplete = () => {
    if (onComplete) {
      onComplete();
    } else {
      setStep('initial');
    }
  };

  return (
    <Card className="w-full max-w-md mx-auto">
      <CardHeader>
        <CardTitle className="flex items-center gap-2">
          <QrCode className="w-5 h-5" />
          Two-Factor Authentication
        </CardTitle>
        <CardDescription>
          Secure your account with Google Authenticator.
        </CardDescription>
      </CardHeader>
      <CardContent>
        {step === 'initial' && (
          <div className="space-y-4">
            <p className="text-sm text-muted-foreground">
              Two-factor authentication adds an extra layer of security to your account. 
              Once enabled, you'll need to enter a code from your Google Authenticator app when logging in.
            </p>
            <Button onClick={startSetup} disabled={isLoading} className="w-full">
              {isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
              Setup / Reset 2FA
            </Button>
            {error && <p className="text-sm text-red-500 mt-2">{error}</p>}
          </div>
        )}

        {step === 'setup' && (
          <div className="space-y-6">
            <div className="flex flex-col items-center justify-center p-4 border rounded-lg bg-white">
              <img src={qrCode} alt="2FA QR Code" className="w-48 h-48" />
            </div>
            <div className="space-y-2">
              <Label>1. Scan QR Code</Label>
              <p className="text-xs text-muted-foreground">
                Open Google Authenticator and scan the QR code above.
              </p>
            </div>
            <div className="space-y-2">
              <Label>2. Enter Code</Label>
              <Input 
                placeholder="Enter 6-digit code" 
                value={otp}
                onChange={(e) => setOtp(e.target.value)}
                maxLength={6}
              />
            </div>
            {error && (
              <div className="flex items-center gap-2 text-sm text-red-500">
                <AlertCircle className="w-4 h-4" />
                {error}
              </div>
            )}
            <Button onClick={verifySetup} disabled={isLoading || otp.length !== 6} className="w-full">
              {isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
              Verify & Enable
            </Button>
          </div>
        )}

        {step === 'success' && (
          <div className="flex flex-col items-center justify-center py-6 space-y-4 text-center">
            <div className="w-12 h-12 rounded-full bg-green-100 flex items-center justify-center text-green-600">
              <CheckCircle2 className="w-6 h-6" />
            </div>
            <div className="space-y-1">
              <h3 className="font-medium">2FA Enabled Successfully</h3>
              <p className="text-sm text-muted-foreground">
                Your account is now secured with two-factor authentication.
              </p>
            </div>
            <Button variant="outline" onClick={handleComplete} className="w-full">
              Done
            </Button>
          </div>
        )}
      </CardContent>
    </Card>
  );
}