Aller au contenu

Activer le MFA (TOTP) et la politique MFA admin

Repod prend en charge l'authentification multifacteur basée sur TOTP (Google Authenticator, Authy, 1Password, toute application compatible RFC 6238) pour tout compte utilisateur, ainsi qu'une politique opt-in exigeant que les comptes admin aient le MFA activé, avec une période de grâce avant que l'application ne prenne effet.

Cette page couvre deux scénarios indépendants :

  1. N'importe quel utilisateur activant le MFA pour son propre compte (self-service, aucune permission spéciale requise).
  2. Un administrateur activant le MFA obligatoire pour les comptes admin, et ce qui arrive à un admin qui ne l'a pas encore configuré.

Partie A — Activer le MFA pour votre propre compte

1. Prérequis

  • Une session Repod connectée (n'importe quel rôle).
  • Une application d'authentification TOTP sur votre téléphone ou gestionnaire de mots de passe.

2. Étape 1 — Demander un secret TOTP

curl -X POST https://repod.example.com/api/v1/auth/mfa/setup \
  -H "Authorization: Bearer $JWT"
{
  "secret": "JBSWY3DPEHPK3PXP",
  "uri": "otpauth://totp/repod:alice?secret=JBSWY3DPEHPK3PXP&issuer=repod",
  "qr_code_base64": "iVBORw0KGgoAAAANSUhEUgA..."
}

Le secret est stocké en attente — il n'est pas encore actif. Dans l'interface, cette étape affiche le PNG qr_code_base64 sous forme de code QR scannable. Scannez-le avec votre application d'authentification, ou saisissez le secret manuellement si le scan n'est pas possible.

3. Étape 2 — Confirmer avec un code TOTP

Saisissez le code à 6 chiffres que votre application d'authentification affiche désormais :

curl -X POST https://repod.example.com/api/v1/auth/mfa/confirm \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{"totp_code": "482913"}'
{
  "message": "MFA activé avec succès. Conservez vos codes de récupération en lieu sûr.",
  "recovery_codes": ["3F7K-9QRT", "8P2M-XW4L", "…"]
}

Le MFA est désormais actif. Conservez les 8 codes de récupération en lieu sûr — chacun est à usage unique et vous permet de vous connecter si vous perdez l'accès à votre application d'authentification. Ils ne sont affichés qu'une seule fois, au moment de la confirmation ; ils ne peuvent plus jamais être récupérés, sauf en régénérant un nouveau jeu (ce qui invalide les anciens).

Les codes de récupération ne sont affichés qu'une seule fois

Si vous les perdez, la seule façon d'en obtenir un nouveau jeu est POST /api/v1/auth/mfa/recovery-codes (nécessite votre mot de passe) — ce qui invalide chaque code précédemment émis.

4. Se connecter avec le MFA activé

Une fois le MFA actif, POST /api/v1/auth/token ne renvoie plus directement un access_token — il renvoie à la place un challenge MFA de courte durée :

{
  "mfa_required": true,
  "mfa_token": "eyJhbGciOi...",
  "token_type": "bearer"
}

Soumettez le code TOTP (ou un code de récupération) contre ce jeton pour finaliser la connexion :

curl -X POST https://repod.example.com/api/v1/auth/mfa/authenticate \
  -H "Content-Type: application/json" \
  -d '{"mfa_token": "eyJhbGciOi...", "totp_code": "482913"}'
{"access_token": "eyJhbGciOi...", "token_type": "bearer"}

Pour utiliser un code de récupération au lieu d'un code TOTP, envoyez "recovery_code": "3F7K-9QRT" au lieu de totp_code. Le mfa_token lui-même expire après 5 minutes.

5. Désactiver le MFA

curl -X POST https://repod.example.com/api/v1/auth/mfa/disable \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{"password": "your-current-password"}'

Nécessite votre mot de passe actuel comme confirmation.

Vérifier que ça a fonctionné (Partie A)

  1. GET /api/v1/auth/me doit afficher "mfa_enabled": true.
  2. GET /api/v1/auth/mfa/recovery-codes/count doit afficher {"remaining": 8} juste après la configuration, en diminuant de un à chaque utilisation d'un code de récupération.
  3. Déconnectez-vous et reconnectez-vous — vous devriez être invité à saisir un code TOTP avant de recevoir un jeton d'accès.

Partie B — MFA admin obligatoire avec période de grâce

Cette politique force chaque compte admin à activer le MFA, sans jamais verrouiller totalement quiconque. Elle est opt-in et désactivée par défaut — l'activer ne casse jamais un déploiement existant où les admins n'ont pas encore configuré le MFA, grâce à la période de grâce décrite ci-dessous.

1. Prérequis

  • Un accès admin à Paramètres.

2. Étape 1 — Activer la politique

curl -X PATCH https://repod.example.com/api/v1/settings/ \
  -H "Authorization: Bearer $ADMIN_JWT" \
  -H "Content-Type: application/json" \
  -d '{"mfa_policy": {"enforce_admin_mfa": true, "grace_period_days": 7}}'
Paramètre Défaut Signification
enforce_admin_mfa false Interrupteur principal. false = la politique n'a aucun effet, quelle que soit la valeur de grace_period_days.
grace_period_days 7 Nombre de jours pendant lesquels un admin sans MFA peut continuer à se connecter normalement après l'activation de la politique, avant d'être bloqué pour une connexion normale.

3. Ce qui arrive à un compte admin sans MFA

L'horloge de la période de grâce démarre à la première connexion de l'admin après l'activation de la politique — jamais à la création du compte. Un admin créé il y a deux ans n'est pas pénalisé rétroactivement par l'ancienneté de son compte le jour où la politique est activée ; un tout nouvel admin obtient exactement la même fenêtre, à compter de sa propre première connexion. Un seul mécanisme couvre les deux cas.

Chronologie pour un compte admin sans MFA, une fois enforce_admin_mfa: true :

  1. Première connexion après l'activation — la connexion réussit normalement, et l'horloge de grâce démarre (users.mfa_grace_started_at est défini une fois, côté serveur).
  2. Pendant la fenêtre de grâce — la connexion continue de réussir normalement. GET /api/v1/auth/me inclut un champ mfa_grace afin que l'interface puisse afficher une bannière d'avertissement :
    {"username": "bob", "role": "admin", "mfa_grace": {"days_remaining": 4}}
    
  3. Après l'expiration de la fenêtre de grâcePOST /api/v1/auth/token n'émet plus de jeton d'accès normal. Il renvoie à la place :
    {"mfa_setup_required": true, "mfa_setup_token": "eyJhbGciOi...", "token_type": "bearer"}
    

Jamais un verrouillage total

Le mfa_setup_token (scope mfa_setup_required, expiration à 30 minutes) est rejeté par tout endpoint normal — il n'autorise que POST /api/v1/auth/mfa/setup et POST /api/v1/auth/mfa/confirm. Un admin bloqué peut toujours terminer d'activer le MFA lui-même ; il n'existe aucun état où un admin est verrouillé sans aucune voie de sortie.

4. Terminer la configuration avec un jeton restreint

Un admin qui a reçu mfa_setup_required suit exactement les deux mêmes appels que la Partie A, en utilisant simplement mfa_setup_token comme jeton bearer :

curl -X POST https://repod.example.com/api/v1/auth/mfa/setup \
  -H "Authorization: Bearer $MFA_SETUP_TOKEN"
curl -X POST https://repod.example.com/api/v1/auth/mfa/confirm \
  -H "Authorization: Bearer $MFA_SETUP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"totp_code": "482913"}'

Comme l'appelant n'avait aucune autre session valide (le jeton de configuration n'accorde rien d'autre), mfa_confirm détecte ce cas et renvoie directement un véritable access_token dans la même réponse — aucune seconde connexion n'est nécessaire :

{
  "message": "MFA activé avec succès. Conservez vos codes de récupération en lieu sûr.",
  "recovery_codes": ["…", "…"],
  "access_token": "eyJhbGciOi...",
  "token_type": "bearer"
}

Vérifier que ça a fonctionné (Partie B)

  1. Confirmez que la politique est enregistrée : GET /api/v1/settings/mfa_policy.enforce_admin_mfa vaut true.
  2. Connectez-vous en tant qu'admin sans MFA — la réponse doit être un access_token normal (période de grâce active), et GET /api/v1/auth/me doit afficher mfa_grace.days_remaining en compte à rebours.
  3. Pour tester le chemin de grâce expirée sans attendre une semaine, définissez temporairement grace_period_days: 0 et reconnectez-vous en tant que ce même admin — vous devriez obtenir mfa_setup_required au lieu de access_token.
  4. Terminez la configuration en utilisant mfa_setup_token comme montré ci-dessus, et confirmez que la réponse inclut un access_token fonctionnel.
  5. Restaurez ensuite grace_period_days à la valeur prévue.

Les admins qui ont déjà le MFA ne sont pas affectés

Le flux de connexion MFA préexistant (mfa_requiredPOST /api/v1/auth/mfa/authenticate) s'exécute toujours en premier et aboutit avant que cette politique ne soit évaluée — un admin qui a déjà le MFA activé ne voit jamais mfa_setup_required.