Auth : Challenge MFA à la connexion¶
Module Beta : opt-in officiel publié sur PyPI depuis 1.0.0-beta.9
forge-mvc-mfa est marqué Development Status :: 4 - Beta (publication PyPI
préparée par MFA-PYPI-READY-001).
Le secret TOTP est chiffré au repos via Fernet (cryptography) avec la clé
FORGE_MFA_SECRET_KEY. Le chiffrement est obligatoire : démarrer sans cette
variable d'environnement lève MfaSecretKeyMissing.
Installation :
forge-mvc-mfa n'est pas inclus dans forge-mvc[all] : activer la MFA est
un choix de sécurité explicite de l'application (les extras core couvrent
uniquement RBAC, workflow et stats ; installer le paquet directement). MFA
reste opt-in : le core Forge ne dépend pas de forge-mvc-mfa.
Module extrait : le code MFA vit dans
forge-mvc-mfa. Voirpackages/forge-mvc-mfa/README.mdpour
l'installation et l'API utilisateur. Cette page documente le flux
de challenge MFA pour mémoire et référence rapide.
Le challenge MFA s'intercale entre la validation du mot de passe et l'ouverture de la session.
Flux général :
mot de passe correct + MFA désactivé → session ouverte (comportement inchangé)
mot de passe correct + MFA activé → état temporaire créé → /login/mfa
code MFA valide → état temporaire supprimé → session ouverte
code MFA invalide → état temporaire conservé → retour formulaire
État temporaire de challenge¶
| Clé de session | Rôle |
|---|---|
_auth_mfa_user_id |
Identifiant de l'utilisateur en attente de MFA |
_auth_mfa_started_at |
Timestamp ISO de début (expiration 10 min par défaut) |
L'état temporaire n'est pas une session authentifiée. L'utilisateur n'a aucun accès aux routes protégées tant que le code MFA n'est pas validé.
API forge_mvc_mfa¶
| Fonction | Comportement |
|---|---|
start_mfa_challenge(request, user) |
Stocke user.id et timestamp en session. Ne connecte pas l'utilisateur. Lève InvalidAuthUserError si utilisateur inactif. |
has_pending_mfa_challenge(request, max_age_minutes=10) |
Retourne True si challenge en cours et non expiré. |
get_mfa_challenge_user_id(request) |
Retourne l'identifiant en attente ou None. |
verify_mfa_challenge(request, code, factors, recovery_codes=None) |
Vérifie code TOTP ou de récupération. Retourne MfaChallengeResult ou None. Supprime le challenge en cas de succès. |
clear_mfa_challenge(request) |
Supprime les clés de challenge. Idempotent. |
Exemple d'intégration MVC¶
# Dans le contrôleur de login, après vérification du mot de passe :
from forge_mvc_mfa import is_mfa_enabled, start_mfa_challenge
from core.auth.user import AuthUser
mfa_factors = get_active_mfa_factors(user_id)
if is_mfa_enabled(mfa_factors):
auth_user = AuthUser(id=user_id, email=email, password_hash=hash, is_active=True)
start_mfa_challenge(request, auth_user)
return redirect("/login/mfa")
# Sinon : ouvrir la session directement
Limites actuelles (AUTH-MFA-004)¶
Forge ne fournit pas encore dans ce flux : remember device, WebAuthn, SMS, email MFA, gestion des codes de récupération dans le formulaire de connexion.
Référence par module¶
L'API détaillée est documentée page par page, un fichier par module :
| Module | Page | Contenu |
|---|---|---|
mfa.py |
Le cœur MFA | facteurs, TOTP, challenge, revalidation |
recovery.py |
Les codes de récupération | génération et vérification des codes de secours |
secret_crypto.py |
Le chiffrement des secrets | chiffrement Fernet du secret TOTP, validation de la clé |
totp_replay.py |
La protection anti-rejeu | refus de la réutilisation d'un code TOTP |
Politique de stockage des secrets MFA¶
Statut actuel¶
forge-mvc-mfa est en Beta. Le secret TOTP est
chiffré au repos via Fernet (bibliothèque cryptography).
Le module est opt-in, non inclus dans forge-mvc[all], et doit être configuré avec
FORGE_MFA_SECRET_KEY avant tout déploiement.
Développement et tests¶
En développement et en environnement de test isolé :
- le secret TOTP est chiffré dans
auth_mfa_factors.totp_secret(Fernet, préfixeenc:) ; - la clé de chiffrement est lue depuis
FORGE_MFA_SECRET_KEY, requise même en dev ; - les codes de récupération sont stockés sous forme hashée (
hash_recovery_code(), SHA-256 +secrets.compare_digest).
Conditions requises même en développement :
FORGE_MFA_SECRET_KEYpositionné dans l'environnement ;- accès à la table
auth_mfa_factorslimité à l'utilisateur applicatif ; - secrets jamais loggés (
totp_secretetrecovery_codesont dans les champs redactés desanitize_auth_audit_metadata()) ; - base de données non exposée publiquement.
Production¶
Le module est en Beta. Le chiffrement Fernet est en place (depuis SEC-MFA-SECRET-ENCRYPTION-001).
Certaines exigences avancées (rotation de clé, sauvegarde/restauration, revue
sécurité formelle) restent à la charge de l'application avant un usage critique.
Protection additionnelle recommandée en production :
- restreindre les droits d'accès à la table
auth_mfa_factorsau strict minimum applicatif ; - stocker
FORGE_MFA_SECRET_KEYdans un gestionnaire de secrets (Vault, AWS Secrets Manager…) ; - appeler
validate_mfa_secret_key_config()au démarrage applicatif (cf.
Validation au démarrage ci-dessous) ; - chiffrement du disque de la base de données ;
- ne pas exporter
auth_mfa_factorsdans des dumps non chiffrés ; - documenter la procédure de rotation et de sauvegarde/restauration de la clé.
Validation au démarrage¶
MFA-SECRET-KEY-BOOT-VALIDATION-001 ajoute la fonction
validate_mfa_secret_key_config() qui échoue tôt sur une configuration
dangereuse, plutôt qu'au moment où un utilisateur tente de s'enrôler ou
de se connecter avec MFA.
from forge_mvc_mfa import validate_mfa_secret_key_config
# Au démarrage applicatif (app.py, wsgi.py, ou tout bootstrap équivalent).
validate_mfa_secret_key_config()
Refusé explicitement :
FORGE_MFA_SECRET_KEYabsente, vide, ou n'ayant que des espaces ;- valeurs placeholder évidentes :
change-me,changeme,default,
secret,dev,development,test,testing,placeholder,
xxx,your-key-here… (insensible à la casse, strippée) ; - clé non Fernet (mauvaise longueur ou base64 invalide).
Exceptions levées : MfaSecretKeyMissing, MfaSecretKeyPlaceholder,
MfaSecretInvalidKey. Aucun message ne contient la valeur de la clé
tentée : pour éviter de fuir un secret dans un log applicatif. Le
message inclut toujours la commande de génération d'une clé valide :
MFA reste opt-in : Forge ne force pas cette validation au niveau du
core. C'est l'application qui décide de l'appeler.
Secrets TOTP¶
Le secret TOTP est une clé partagée utilisée pour calculer les codes TOTP (RFC 6238).
Pourquoi on ne peut pas simplement hasher le secret TOTP :
Un hash est à sens unique. Pour vérifier un code TOTP, le serveur doit pouvoir recalculer TOTP(secret, timestamp). Si le secret est hashé, cette opération est impossible.
Le stockage production-ready d'un secret TOTP nécessite :
- un chiffrement applicatif réversible (AES-256-GCM ou équivalent avec clé de chiffrement séparée), ou
- un HSM (Hardware Security Module), ou
- un gestionnaire de secrets (Vault, AWS Secrets Manager, ou équivalent).
Depuis SEC-MFA-SECRET-ENCRYPTION-001, forge-mvc-mfa implémente le chiffrement Fernet
(cryptography.fernet.Fernet, AES-128-CBC + HMAC-SHA256) via la clé FORGE_MFA_SECRET_KEY.
Les valeurs stockées en base sont préfixées enc: pour distinguer les secrets chiffrés
d'éventuelles valeurs legacy.
Pour renforcer davantage, coupler FORGE_MFA_SECRET_KEY à un gestionnaire de secrets externe.
Codes de récupération¶
Les codes de récupération sont correctement protégés dans forge-mvc-mfa (série 1.0.0-beta.x) :
- générés via
secrets.choice()sur un alphabet sans ambiguïté ; - hashés avant stockage via
hash_recovery_code()(SHA-256) ; - vérifiés via
secrets.compare_digest()(résistant aux timing attacks) ; - stockés en base uniquement sous forme de hash : le code brut n'est jamais persisté.
Cette conception est conforme pour la production, à condition que la base elle-même soit protégée. Un hash de code de récupération exposé ne permet pas de retrouver le code brut.
Exigences avant production-ready¶
forge-mvc-mfa est en Beta (publié sur PyPI depuis 1.0.0-beta.9). Avant un usage en production critique, l'application doit couvrir les exigences suivantes :
Chiffrement applicatif des secrets TOTP✓ livré (SEC-MFA-SECRET-ENCRYPTION-001) : Fernet +FORGE_MFA_SECRET_KEY.- Politique de rotation documentée : rotation ou invalidation maîtrisée des secrets compromis.
- Documentation de sauvegarde/restauration : procédure en cas de perte de la clé de chiffrement.
Tests dédiés au stockage chiffré✓ livré (SEC-MFA-SECRET-ENCRYPTION-001) :tests/test_mfa_secret_crypto.py.- Revue sécurité explicite : validation que le stockage chiffré est correct.
Décision explicite de changement de statut✓ livré (MFA-PYPI-READY-001).Publication PyPI✓ livré en1.0.0-beta.9.Passage en Beta✓ acté (tous les opt-ins en Beta).
Tickets liés¶
| Ticket | Description | État |
|---|---|---|
MFA-SECRET-STORAGE-POLICY-001 |
Documenter la politique de stockage | livré |
SEC-MFA-SECRET-ENCRYPTION-001 |
Chiffrement applicatif du secret TOTP (Fernet) | livré |
MFA-PYPI-READY-001 |
Requalification Alpha (Pre-Alpha → Alpha) | livré |