diff --git a/frontend-prep/src/pages/User/ProfilePage.tsx b/frontend-prep/src/pages/User/ProfilePage.tsx
index ef3fd2ee..2e4710fb 100644
--- a/frontend-prep/src/pages/User/ProfilePage.tsx
+++ b/frontend-prep/src/pages/User/ProfilePage.tsx
@@ -1,11 +1,11 @@
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import Navbar from '../../components/Navbar';
-import { isUserAuthenticated, extractUsernameFromToken, getMyProfile, updateMyProfile, getTelegramStatus, generateTelegramLinkToken, unlinkTelegram } from '../../api/api';
+import { isUserAuthenticated, extractUsernameFromToken, getMyProfile, updateMyProfile, getTelegramStatus, generateTelegramLinkToken, unlinkTelegram, get2FAStatus, toggle2FA, getPublicSettings } from '../../api/api';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
faUser, faMapMarkerAlt, faPhone, faCommentDots,
- faSave, faCheckCircle, faExclamationTriangle, faPaperPlane, faUnlink, faTimes, faLock,
+ faSave, faCheckCircle, faExclamationTriangle, faPaperPlane, faUnlink, faTimes, faLock, faShieldAlt,
} from '@fortawesome/free-solid-svg-icons';
import './ProfilePage.css';
@@ -37,6 +37,11 @@ export default function ProfilePage() {
const [tgEnabled, setTgEnabled] = useState(false);
const [tgLoading, setTgLoading] = useState(false);
+ // 2FA
+ const [twoFAEnabled, setTwoFAEnabled] = useState(false);
+ const [twoFAAdminEnabled, setTwoFAAdminEnabled] = useState(false);
+ const [twoFALoading, setTwoFALoading] = useState(false);
+
// Modal confirmation infos par défaut
const [showSaveModal, setShowSaveModal] = useState(false);
@@ -50,6 +55,12 @@ export default function ProfilePage() {
// Statut Telegram
getTelegramStatus().then((s) => { setTgLinked(s.linked); setTgEnabled(s.enabled); });
+ // Statut 2FA
+ Promise.all([get2FAStatus(), getPublicSettings()]).then(([status, pub]) => {
+ setTwoFAEnabled(status.two_fa_enabled);
+ setTwoFAAdminEnabled(pub.two_fa_enabled);
+ });
+
// Charger depuis backend
getMyProfile().then((res) => {
if (res.success && res.client) {
@@ -99,9 +110,23 @@ export default function ProfilePage() {
if (!window.confirm('Délier votre compte Telegram ? Vous ne recevrez plus de notifications.')) return;
await unlinkTelegram();
setTgLinked(false);
+ setTwoFAEnabled(false);
showSuccess('Compte Telegram délié');
};
+ const handleToggle2FA = async () => {
+ const newVal = !twoFAEnabled;
+ setTwoFALoading(true);
+ const res = await toggle2FA(newVal);
+ setTwoFALoading(false);
+ if (res.success) {
+ setTwoFAEnabled(newVal);
+ showSuccess(newVal ? 'Double authentification activée' : 'Double authentification désactivée');
+ } else {
+ showError(res.error || 'Erreur lors de la modification');
+ }
+ };
+
const saveContact = async () => {
setSavingContact(true);
const res = await updateMyProfile({ nom: nom.trim(), prenom: prenom.trim(), telephone: telephone.trim() });
@@ -276,6 +301,34 @@ export default function ProfilePage() {
)}
)}
+
+ {/* Section 2FA — visible uniquement si l'admin l'a activé ET Telegram est lié */}
+ {twoFAAdminEnabled && tgLinked && (
+
+
+
+ Double authentification (2FA)
+
+
+ À chaque connexion, un code vous sera envoyé sur Telegram avant d'accéder à votre compte.
+
+
+
+ {twoFAEnabled && (
+
+ Activée
+
+ )}
+
+
+ )}
{showSaveModal && (
diff --git a/mobile/src/api/api.ts b/mobile/src/api/api.ts
index d7ad039f..7f9de019 100644
--- a/mobile/src/api/api.ts
+++ b/mobile/src/api/api.ts
@@ -787,6 +787,7 @@ export interface PublicSettings {
crypto_only: boolean;
nowpayments_currencies: string[];
telegram_notifications_enabled: boolean;
+ two_fa_enabled: boolean;
}
export const getPublicSettings = async (): Promise