change and add into components, widgets and pages
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
import { useState } from 'react';
|
||||
import { pb } from '@/services/pocketbase';
|
||||
import * as OTPAuth from 'otpauth';
|
||||
|
||||
interface MfaConfig {
|
||||
userId: string;
|
||||
totpSecret: string;
|
||||
qrUrl: string;
|
||||
isFirstSetup: boolean;
|
||||
email: string;
|
||||
}
|
||||
|
||||
export const useLoginFlow = (onLoginSuccess: () => void) => {
|
||||
const [step, setStep] = useState<1 | 2>(1);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
const [mfaConfig, setMfaConfig] = useState<MfaConfig | null>(null);
|
||||
|
||||
// --- SSO ZITADEL ---
|
||||
const loginWithZitadel = async () => {
|
||||
setError('');
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const authData = await pb.collection('aegis_users').authWithOAuth2({ provider: 'oidc' });
|
||||
if (authData) onLoginSuccess();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
setError("Échec de la connexion via GISE Identity.");
|
||||
pb.authStore.clear();
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// --- LOCAL LOGIN (Étape 1) ---
|
||||
const loginWithLocal = async (email: string, pass: string) => {
|
||||
setError('');
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const authData = await pb.collection('aegis_users').authWithPassword(email, pass);
|
||||
|
||||
if (authData.record.mfa_enabled) {
|
||||
let secret = authData.record.totp_secret;
|
||||
let qrUrl = '';
|
||||
let isFirstSetup = false;
|
||||
|
||||
// Génération d'un nouveau secret si première fois
|
||||
if (!secret) {
|
||||
const totp = new OTPAuth.TOTP({
|
||||
issuer: 'AEGIS by GISE',
|
||||
label: email,
|
||||
algorithm: 'SHA1',
|
||||
digits: 6,
|
||||
period: 30,
|
||||
secret: new OTPAuth.Secret({ size: 20 })
|
||||
});
|
||||
secret = totp.secret.base32;
|
||||
qrUrl = totp.toString();
|
||||
isFirstSetup = true;
|
||||
}
|
||||
|
||||
setMfaConfig({ userId: authData.record.id, totpSecret: secret, qrUrl, isFirstSetup, email });
|
||||
setStep(2);
|
||||
} else {
|
||||
onLoginSuccess();
|
||||
}
|
||||
} catch (err) {
|
||||
setError("Identifiants incorrects ou accès révoqué.");
|
||||
pb.authStore.clear();
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// --- VALIDATION MFA (Étape 2) ---
|
||||
const verifyMfa = async (mfaCode: string) => {
|
||||
if (!mfaConfig) return;
|
||||
setError('');
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
const totp = new OTPAuth.TOTP({
|
||||
issuer: 'AEGIS by GISE',
|
||||
label: mfaConfig.email,
|
||||
algorithm: 'SHA1',
|
||||
digits: 6,
|
||||
period: 30,
|
||||
secret: OTPAuth.Secret.fromBase32(mfaConfig.totpSecret)
|
||||
});
|
||||
|
||||
const isValid = totp.validate({ token: mfaCode, window: 1 }) !== null;
|
||||
|
||||
if (isValid) {
|
||||
if (mfaConfig.isFirstSetup) {
|
||||
await pb.collection('aegis_users').update(mfaConfig.userId, { totp_secret: mfaConfig.totpSecret });
|
||||
}
|
||||
onLoginSuccess();
|
||||
} else {
|
||||
setError("Code de sécurité invalide ou expiré.");
|
||||
}
|
||||
} catch (err) {
|
||||
setError("Erreur critique lors de la vérification.");
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const cancelMfa = () => {
|
||||
pb.authStore.clear();
|
||||
setStep(1);
|
||||
setMfaConfig(null);
|
||||
setError('');
|
||||
};
|
||||
|
||||
return { step, isLoading, error, mfaConfig, loginWithZitadel, loginWithLocal, verifyMfa, cancelMfa };
|
||||
};
|
||||
Reference in New Issue
Block a user