Configuration
- Ajouter le plugin
twoFactor()à la configuration serveur avecissuer - Ajouter le plugin
twoFactorClient()à la configuration client - Exécuter
npx @better-auth/cli migrate - Vérifier : vérifier que la colonne
twoFactorSecretexiste sur la table user
import { betterAuth } from "better-auth";
import { twoFactor } from "better-auth/plugins";
export const auth = betterAuth({
appName: "My App",
plugins: [
twoFactor({
issuer: "My App",
}),
],
});
Configuration côté client
import { createAuthClient } from "better-auth/client";
import { twoFactorClient } from "better-auth/client/plugins";
export const authClient = createAuthClient({
plugins: [
twoFactorClient({
onTwoFactorRedirect() {
window.location.href = "/2fa";
},
}),
],
});
Activation de l'authentification 2FA pour les utilisateurs
Requiert une vérification de mot de passe. Retourne l'URI TOTP (pour un code QR) et les codes de secours.
const enable2FA = async (password: string) => {
const { data, error } = await authClient.twoFactor.enable({
password,
});
if (data) {
// data.totpURI — générer un code QR à partir de ceci
// data.backupCodes — afficher à l'utilisateur
}
};
twoFactorEnabled n'est défini sur true que lorsque la première vérification TOTP réussit. À remplacer par skipVerificationOnEnable: true (non recommandé).
TOTP (Application d'authentification)
Affichage du code QR
import QRCode from "react-qr-code";
const TotpSetup = ({ totpURI }: { totpURI: string }) => {
return <QRCode value={totpURI} />;
};
Vérification des codes TOTP
Accepte les codes d'une période avant/après l'heure actuelle :
const verifyTotp = async (code: string) => {
const { data, error } = await authClient.twoFactor.verifyTotp({
code,
trustDevice: true,
});
};
Options de configuration TOTP
twoFactor({
totpOptions: {
digits: 6, // 6 ou 8 chiffres (défaut : 6)
period: 30, // Période de validité du code en secondes (défaut : 30)
},
});
OTP (Email/SMS)
Configuration de la livraison OTP
import { betterAuth } from "better-auth";
import { twoFactor } from "better-auth/plugins";
import { sendEmail } from "./email";
export const auth = betterAuth({
plugins: [
twoFactor({
otpOptions: {
sendOTP: async ({ user, otp }, ctx) => {
await sendEmail({
to: user.email,
subject: "Your verification code",
text: `Your code is: ${otp}`,
});
},
period: 5, // Validité du code en minutes (défaut : 3)
digits: 6, // Nombre de chiffres (défaut : 6)
allowedAttempts: 5, // Nombre maximum de tentatives de vérification (défaut : 5)
},
}),
],
});
Envoi et vérification OTP
Envoyer : authClient.twoFactor.sendOtp(). Vérifier : authClient.twoFactor.verifyOtp({ code, trustDevice: true }).
Sécurité du stockage OTP
Configurer comment les codes OTP sont stockés dans la base de données :
twoFactor({
otpOptions: {
storeOTP: "encrypted", // Options : "plain", "encrypted", "hashed"
},
});
Pour un chiffrement personnalisé :
twoFactor({
otpOptions: {
storeOTP: {
encrypt: async (token) => myEncrypt(token),
decrypt: async (token) => myDecrypt(token),
},
},
});
Codes de secours
Générés automatiquement lors de l'activation de l'authentification 2FA. Chaque code ne peut être utilisé qu'une seule fois.
Affichage des codes de secours
const BackupCodes = ({ codes }: { codes: string[] }) => {
return (
<div>
<p>Save these codes in a secure location:</p>
<ul>
{codes.map((code, i) => (
<li key={i}>{code}</li>
))}
</ul>
</div>
);
};
Regénération des codes de secours
Invalide tous les codes précédents :
const regenerateBackupCodes = async (password: string) => {
const { data, error } = await authClient.twoFactor.generateBackupCodes({
password,
});
// data.backupCodes contient les nouveaux codes
};
Utilisation des codes de secours pour la récupération
const verifyBackupCode = async (code: string) => {
const { data, error } = await authClient.twoFactor.verifyBackupCode({
code,
trustDevice: true,
});
};
Configuration des codes de secours
twoFactor({
backupCodeOptions: {
amount: 10, // Nombre de codes à générer (défaut : 10)
length: 10, // Longueur de chaque code (défaut : 10)
storeBackupCodes: "encrypted", // Options : "plain", "encrypted"
},
});
Gestion de l'authentification 2FA lors de la connexion
La réponse inclut twoFactorRedirect: true quand l'authentification 2FA est requise :
Flux de connexion
- Appeler
signIn.email({ email, password }) - Vérifier
context.data.twoFactorRedirectdansonSuccess - Si
true, rediriger vers la page de vérification/2fa - Vérifier via TOTP, OTP ou code de secours
- Un cookie de session est créé lors de la vérification réussie
const signIn = async (email: string, password: string) => {
const { data, error } = await authClient.signIn.email(
{ email, password },
{
onSuccess(context) {
if (context.data.twoFactorRedirect) {
window.location.href = "/2fa";
}
},
}
);
};
Côté serveur : vérifier "twoFactorRedirect" in response lors de l'utilisation de auth.api.signInEmail.
Appareils approuvés
Passer trustDevice: true lors de la vérification. Durée de confiance par défaut : 30 jours (trustDeviceMaxAge). Se réinitialise à chaque connexion.
Considérations de sécurité
Gestion des sessions
Flux : identifiants → session supprimée → cookie 2FA temporaire (10 min par défaut) → vérification → session créée.
twoFactor({
twoFactorCookieMaxAge: 600, // 10 minutes en secondes (défaut)
});
Limitation du débit
Intégrée : 3 requêtes par 10 secondes pour tous les endpoints 2FA. OTP dispose d'une limitation supplémentaire sur les tentatives :
twoFactor({
otpOptions: {
allowedAttempts: 5, // Nombre maximum de tentatives par code OTP (défaut : 5)
},
});
Chiffrement au repos
Secrets TOTP : chiffrés avec le secret d'authentification. Codes de secours : chiffrés par défaut. OTP : configurable ("plain", "encrypted", "hashed"). Utilise une comparaison en temps constant pour la vérification.
L'authentification 2FA ne peut être activée que pour les comptes avec identifiants (email/mot de passe).
Désactivation de l'authentification 2FA
Requiert une confirmation de mot de passe. Révoque les enregistrements d'appareils approuvés :
const disable2FA = async (password: string) => {
const { data, error } = await authClient.twoFactor.disable({
password,
});
};
Exemple de configuration complète
import { betterAuth } from "better-auth";
import { twoFactor } from "better-auth/plugins";
import { sendEmail } from "./email";
export const auth = betterAuth({
appName: "My App",
plugins: [
twoFactor({
// Paramètres TOTP
issuer: "My App",
totpOptions: {
digits: 6,
period: 30,
},
// Paramètres OTP
otpOptions: {
sendOTP: async ({ user, otp }) => {
await sendEmail({
to: user.email,
subject: "Your verification code",
text: `Your code is: ${otp}`,
});
},
period: 5,
allowedAttempts: 5,
storeOTP: "encrypted",
},
// Paramètres des codes de secours
backupCodeOptions: {
amount: 10,
length: 10,
storeBackupCodes: "encrypted",
},
// Paramètres de session
twoFactorCookieMaxAge: 600, // 10 minutes
trustDeviceMaxAge: 30 * 24 * 60 * 60, // 30 jours
}),
],
});