Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 

127 строки
3.9 KiB

  1. import React, { useState, useEffect } from 'react';
  2. import axios from 'axios';
  3. import { QRCodeSVG } from 'qrcode.react'; // Use SVG for sharp rendering
  4. import {apiPath} from "../../auth/utils";
  5. import {
  6. Box,
  7. Button,
  8. Typography,
  9. TextField,
  10. Card,
  11. CardContent,
  12. Alert,
  13. CircularProgress
  14. } from '@mui/material';
  15. const Profile = () => {
  16. const [qrUrl, setQrUrl] = useState('');
  17. const [code, setCode] = useState('');
  18. const [loading, setLoading] = useState(false);
  19. const [message, setMessage] = useState('');
  20. const [error, setError] = useState('');
  21. useEffect(() => {
  22. // Fetch current user or check Redux store for twoFactorEnabled
  23. // Hide "Enable" button if already true
  24. }, []);
  25. const start2FASetup = async () => {
  26. setLoading(true);
  27. setMessage('');
  28. setError('');
  29. try {
  30. const response = await axios.post(`${apiPath}/2fa/setup`, {}, {
  31. headers: { Authorization: `Bearer ${localStorage.getItem('accessToken')}` } // Adjust to your auth method
  32. });
  33. setQrUrl(response.data.otpauthUrl);
  34. } catch (err) {
  35. setError('Failed to start 2FA setup. Please try again.');
  36. } finally {
  37. setLoading(false);
  38. }
  39. };
  40. const verifyCode = async () => {
  41. setLoading(true);
  42. setMessage('');
  43. setError('');
  44. try {
  45. await axios.post(`${apiPath}/2fa/verify-setup`, { code }, {
  46. headers: { Authorization: `Bearer ${localStorage.getItem('accessToken')}` }
  47. });
  48. setMessage('2FA enabled successfully! You will now be prompted for a code on login.');
  49. setQrUrl('');
  50. setCode('');
  51. } catch (err) {
  52. setError('Invalid or expired code. Try again.');
  53. } finally {
  54. setLoading(false);
  55. }
  56. };
  57. return (
  58. <Box sx={{ maxWidth: 800, mx: 'auto', mt: 4 }}>
  59. <Typography variant="h4" gutterBottom>
  60. Profile & Security
  61. </Typography>
  62. <Card sx={{ mt: 3 }}>
  63. <CardContent>
  64. <Typography variant="h6" gutterBottom>
  65. Two-Factor Authentication (2FA)
  66. </Typography>
  67. {!qrUrl ? (
  68. <>
  69. <Typography variant="body1" paragraph>
  70. Add an extra layer of security to your account with 2FA using Microsoft Authenticator.
  71. </Typography>
  72. <Button
  73. variant="contained"
  74. color="primary"
  75. onClick={start2FASetup}
  76. disabled={loading}
  77. >
  78. {loading ? <CircularProgress size={24} /> : 'Enable 2FA'}
  79. </Button>
  80. </>
  81. ) : (
  82. <Box sx={{ textAlign: 'center', my: 4 }}>
  83. <Typography variant="body1" gutterBottom>
  84. Scan this QR code with <strong>Microsoft Authenticator</strong>:
  85. </Typography>
  86. <QRCodeSVG value={qrUrl} size={220} level="M" />
  87. <Typography variant="caption" display="block" sx={{ mt: 2, mb: 3 }}>
  88. After scanning, enter the 6-digit code from the app:
  89. </Typography>
  90. <TextField
  91. label="Verification Code"
  92. value={code}
  93. onChange={(e) => setCode(e.target.value.replace(/\D/g, '').slice(0, 6))}
  94. inputProps={{ maxLength: 6 }}
  95. sx={{ width: 200 }}
  96. />
  97. <Box sx={{ mt: 2 }}>
  98. <Button
  99. variant="contained"
  100. onClick={verifyCode}
  101. disabled={loading || code.length !== 6}
  102. >
  103. {loading ? <CircularProgress size={24} /> : 'Verify & Enable'}
  104. </Button>
  105. </Box>
  106. </Box>
  107. )}
  108. {message && <Alert severity="success" sx={{ mt: 2 }}>{message}</Alert>}
  109. {error && <Alert severity="error" sx={{ mt: 2 }}>{error}</Alert>}
  110. </CardContent>
  111. </Card>
  112. {/* Add other profile fields here later */}
  113. </Box>
  114. );
  115. };
  116. export default Profile;