feat(gherkin): compléter couverture règles métier avec 47 features manquantes
Ajout de 47 features Gherkin (~650 scénarios) pour couvrir 100% des règles métier : - Authentification (5) : validation mot de passe, tentatives connexion, multi-device, 2FA, récupération - Audio-guides (12) : détection mode, création, navigation piéton/voiture, ETA, gestion points, progression - Navigation (5) : notifications minimalistes, décompte 5s, stationnement, historique, basculement auto - Création contenu (3) : image auto, restrictions modification, suppression - Radio live (2) : enregistrement auto, interdictions modération - Droits auteur (6) : fair use 30s, détection musique, signalements, sanctions, appels - Modération (9) : badges Bronze/Argent/Or, score fiabilité, utilisateur confiance, audit, anti-abus - Premium (2) : webhooks Mangopay, tarification multi-canal - Profil/Partage/Recherche (5) : badge vérifié, stats arrondies, partage premium, filtres avancés, carte Tous les scénarios incluent edge cases, métriques de performance et conformité RGPD. Couverture fonctionnelle MVP maintenant complète.
This commit is contained in:
200
features/api/authentication/appareil-confiance-2fa.feature
Normal file
200
features/api/authentication/appareil-confiance-2fa.feature
Normal file
@@ -0,0 +1,200 @@
|
||||
# language: fr
|
||||
|
||||
@api @authentication @2fa @security @mvp
|
||||
Fonctionnalité: Appareils de confiance et authentification à deux facteurs
|
||||
|
||||
En tant qu'utilisateur soucieux de la sécurité
|
||||
Je veux gérer mes appareils de confiance et activer l'authentification à deux facteurs
|
||||
Afin de protéger mon compte contre les accès non autorisés
|
||||
|
||||
Contexte:
|
||||
Étant donné que le système supporte les méthodes 2FA suivantes:
|
||||
| Méthode | Disponibilité | Recommandée |
|
||||
| Application TOTP | Oui | Oui |
|
||||
| SMS | Oui | Non |
|
||||
| Email | Oui | Non |
|
||||
| Clés de sécurité USB | Phase 2 | Oui |
|
||||
|
||||
Scénario: Activation de l'authentification à deux facteurs par TOTP
|
||||
Étant donné un utilisateur "alice@roadwave.fr" sans 2FA activé
|
||||
Quand l'utilisateur accède à "Mon compte > Sécurité > Authentification à deux facteurs"
|
||||
Et clique sur "Activer l'authentification à deux facteurs"
|
||||
Alors le système génère un QR code avec secret TOTP
|
||||
Et affiche le secret en texte clair pour saisie manuelle
|
||||
Et affiche les instructions: "Scannez ce QR code avec Google Authenticator, Authy ou Microsoft Authenticator"
|
||||
Et l'utilisateur scanne le QR code avec son application TOTP
|
||||
Et saisit le code à 6 chiffres généré par l'application
|
||||
Alors le 2FA est activé
|
||||
Et 10 codes de récupération à usage unique sont générés
|
||||
Et les codes de récupération sont affichés avec avertissement: "Conservez ces codes en lieu sûr"
|
||||
Et un événement "2FA_ENABLED" est enregistré
|
||||
Et un email de confirmation est envoyé
|
||||
Et la métrique "auth.2fa.enabled" est incrémentée
|
||||
|
||||
Scénario: Connexion avec 2FA depuis un nouvel appareil
|
||||
Étant donné un utilisateur "bob@roadwave.fr" avec 2FA activé
|
||||
Et aucun appareil de confiance enregistré
|
||||
Quand l'utilisateur se connecte depuis un iPhone avec email/mot de passe corrects
|
||||
Alors une page de vérification 2FA s'affiche
|
||||
Et l'utilisateur saisit le code à 6 chiffres de son application TOTP
|
||||
Et coche l'option "Faire confiance à cet appareil pour 30 jours"
|
||||
Alors la connexion est réussie
|
||||
Et l'iPhone est enregistré comme appareil de confiance
|
||||
Et un token d'appareil de confiance est stocké localement (durée: 30 jours)
|
||||
Et un événement "2FA_SUCCESS_NEW_TRUSTED_DEVICE" est enregistré
|
||||
Et un email est envoyé: "Nouvel appareil de confiance ajouté: iPhone"
|
||||
Et la métrique "auth.2fa.trusted_device.added" est incrémentée
|
||||
|
||||
Scénario: Connexion depuis un appareil de confiance existant
|
||||
Étant donné un utilisateur "charlie@roadwave.fr" avec 2FA activé
|
||||
Et un iPhone enregistré comme appareil de confiance il y a 10 jours
|
||||
Quand l'utilisateur se connecte depuis cet iPhone avec email/mot de passe corrects
|
||||
Alors la connexion est réussie immédiatement sans demander le code 2FA
|
||||
Et un événement "LOGIN_TRUSTED_DEVICE" est enregistré
|
||||
Et la date de dernière utilisation de l'appareil de confiance est mise à jour
|
||||
Et la métrique "auth.2fa.trusted_device.used" est incrémentée
|
||||
|
||||
Scénario: Expiration automatique d'un appareil de confiance après 30 jours
|
||||
Étant donné un utilisateur "david@roadwave.fr" avec 2FA activé
|
||||
Et un iPad enregistré comme appareil de confiance il y a 31 jours
|
||||
Quand l'utilisateur se connecte depuis cet iPad avec email/mot de passe corrects
|
||||
Alors une page de vérification 2FA s'affiche
|
||||
Et l'utilisateur doit saisir le code TOTP
|
||||
Et un message s'affiche: "Votre appareil de confiance a expiré après 30 jours. Veuillez vous authentifier à nouveau."
|
||||
Et l'ancien token d'appareil de confiance est révoqué
|
||||
Et un événement "TRUSTED_DEVICE_EXPIRED" est enregistré
|
||||
Et la métrique "auth.2fa.trusted_device.expired" est incrémentée
|
||||
|
||||
Scénario: Gestion de la liste des appareils de confiance
|
||||
Étant donné un utilisateur "eve@roadwave.fr" avec 2FA activé
|
||||
Et 3 appareils de confiance enregistrés
|
||||
Quand l'utilisateur accède à "Mon compte > Sécurité > Appareils de confiance"
|
||||
Alors l'utilisateur voit la liste suivante:
|
||||
| Appareil | Ajouté le | Dernière utilisation | Expire le | Actions |
|
||||
| iPhone 14 Pro | 2026-01-15 | Il y a 2 heures | 2026-02-14 | [Révoquer] |
|
||||
| iPad Air | 2026-01-10 | Il y a 5 jours | 2026-02-09 | [Révoquer] |
|
||||
| MacBook Pro | 2026-01-05 | Il y a 10 jours | 2026-02-04 | [Révoquer] |
|
||||
Et un bouton "Révoquer tous les appareils de confiance" est disponible
|
||||
Et un compteur affiche "3 appareils de confiance actifs"
|
||||
|
||||
Scénario: Révocation manuelle d'un appareil de confiance
|
||||
Étant donné un utilisateur "frank@roadwave.fr" avec 2FA activé
|
||||
Et un MacBook Pro enregistré comme appareil de confiance
|
||||
Quand l'utilisateur clique sur "Révoquer" pour le MacBook Pro
|
||||
Alors l'appareil de confiance est immédiatement révoqué
|
||||
Et le token d'appareil de confiance est invalidé
|
||||
Et un événement "TRUSTED_DEVICE_REVOKED_MANUAL" est enregistré
|
||||
Et un email est envoyé: "Vous avez révoqué l'appareil de confiance: MacBook Pro"
|
||||
Et lors de la prochaine connexion, le code 2FA sera demandé
|
||||
Et la métrique "auth.2fa.trusted_device.revoked" est incrémentée
|
||||
|
||||
Scénario: Utilisation d'un code de récupération en cas de perte de l'application TOTP
|
||||
Étant donné un utilisateur "grace@roadwave.fr" avec 2FA activé
|
||||
Et l'utilisateur a perdu l'accès à son application TOTP
|
||||
Et il possède ses codes de récupération
|
||||
Quand l'utilisateur se connecte avec email/mot de passe corrects
|
||||
Et clique sur "Utiliser un code de récupération"
|
||||
Et saisit l'un des 10 codes de récupération
|
||||
Alors la connexion est réussie
|
||||
Et le code de récupération utilisé est marqué comme consommé
|
||||
Et il reste 9 codes de récupération disponibles
|
||||
Et un événement "2FA_RECOVERY_CODE_USED" est enregistré
|
||||
Et un email d'alerte est envoyé: "Un code de récupération a été utilisé. Il vous reste 9 codes."
|
||||
Et l'utilisateur est invité à reconfigurer son 2FA
|
||||
Et la métrique "auth.2fa.recovery_code.used" est incrémentée
|
||||
|
||||
Scénario: Régénération des codes de récupération
|
||||
Étant donné un utilisateur "henry@roadwave.fr" avec 2FA activé
|
||||
Et 3 codes de récupération ont été utilisés
|
||||
Quand l'utilisateur accède à "Mon compte > Sécurité > Codes de récupération"
|
||||
Et clique sur "Régénérer les codes de récupération"
|
||||
Alors un message d'avertissement s'affiche: "Les anciens codes seront invalidés. Êtes-vous sûr ?"
|
||||
Et après confirmation, 10 nouveaux codes de récupération sont générés
|
||||
Et les anciens codes sont invalidés immédiatement
|
||||
Et les nouveaux codes sont affichés une seule fois
|
||||
Et un événement "2FA_RECOVERY_CODES_REGENERATED" est enregistré
|
||||
Et un email est envoyé avec les nouveaux codes (chiffrés)
|
||||
Et la métrique "auth.2fa.recovery_codes.regenerated" est incrémentée
|
||||
|
||||
Scénario: Désactivation du 2FA avec vérification renforcée
|
||||
Étant donné un utilisateur "iris@roadwave.fr" avec 2FA activé
|
||||
Quand l'utilisateur accède à "Mon compte > Sécurité > Authentification à deux facteurs"
|
||||
Et clique sur "Désactiver l'authentification à deux facteurs"
|
||||
Alors un message d'avertissement s'affiche: "Cela réduira la sécurité de votre compte"
|
||||
Et l'utilisateur doit saisir son mot de passe actuel
|
||||
Et l'utilisateur doit saisir un code 2FA valide
|
||||
Et l'utilisateur doit confirmer par email via un lien sécurisé
|
||||
Alors le 2FA est désactivé
|
||||
Et tous les appareils de confiance sont révoqués
|
||||
Et tous les codes de récupération sont invalidés
|
||||
Et un événement "2FA_DISABLED" est enregistré
|
||||
Et un email de confirmation est envoyé
|
||||
Et la métrique "auth.2fa.disabled" est incrémentée
|
||||
|
||||
Scénario: Authentification 2FA par SMS en méthode de secours
|
||||
Étant donné un utilisateur "jack@roadwave.fr" avec 2FA par TOTP activé
|
||||
Et l'utilisateur a également configuré un numéro de téléphone de secours
|
||||
Quand l'utilisateur se connecte et clique sur "Recevoir un code par SMS"
|
||||
Alors un code à 6 chiffres est envoyé au numéro +33612345678
|
||||
Et l'utilisateur saisit le code reçu par SMS
|
||||
Alors la connexion est réussie
|
||||
Et un événement "2FA_SMS_FALLBACK_USED" est enregistré
|
||||
Et un email d'alerte est envoyé: "Vous avez utilisé la méthode SMS de secours"
|
||||
Et la métrique "auth.2fa.sms.used" est incrémentée
|
||||
|
||||
Scénario: Limitation des tentatives de codes 2FA
|
||||
Étant donné un utilisateur "kate@roadwave.fr" avec 2FA activé
|
||||
Quand l'utilisateur se connecte avec email/mot de passe corrects
|
||||
Et saisit 5 codes 2FA incorrects consécutivement
|
||||
Alors le compte est temporairement bloqué pour 15 minutes
|
||||
Et un message s'affiche: "Trop de tentatives échouées. Veuillez réessayer dans 15 minutes."
|
||||
Et un email d'alerte est envoyé: "Multiples tentatives échouées de codes 2FA détectées"
|
||||
Et un événement "2FA_TOO_MANY_ATTEMPTS" est enregistré avec niveau "HIGH"
|
||||
Et la métrique "auth.2fa.blocked.too_many_attempts" est incrémentée
|
||||
|
||||
Scénario: Détection de connexion suspecte malgré 2FA valide
|
||||
Étant donné un utilisateur "luke@roadwave.fr" avec 2FA activé
|
||||
Et toutes ses connexions habituelles sont depuis la France
|
||||
Quand l'utilisateur se connecte avec email/mot de passe corrects depuis la Russie
|
||||
Et saisit un code 2FA valide
|
||||
Alors la connexion est réussie mais marquée comme suspecte
|
||||
Et l'utilisateur reçoit immédiatement un email: "Connexion inhabituelle depuis Russie"
|
||||
Et une notification push est envoyée sur tous les appareils de confiance
|
||||
Et l'accès aux fonctionnalités sensibles (paiement, changement de mot de passe) est temporairement bloqué
|
||||
Et l'utilisateur doit confirmer son identité par email avant accès complet
|
||||
Et un événement "2FA_SUSPICIOUS_LOCATION" est enregistré avec niveau "HIGH"
|
||||
Et la métrique "auth.2fa.suspicious_login" est incrémentée
|
||||
|
||||
Scénario: Révocation de tous les appareils de confiance en cas de compromission
|
||||
Étant donné un utilisateur "mary@roadwave.fr" avec 2FA activé
|
||||
Et 5 appareils de confiance enregistrés
|
||||
Et l'utilisateur suspecte une compromission de son compte
|
||||
Quand l'utilisateur clique sur "Révoquer tous les appareils de confiance"
|
||||
Alors tous les appareils de confiance sont immédiatement révoqués
|
||||
Et tous les tokens d'appareils de confiance sont invalidés
|
||||
Et toutes les sessions actives sont fermées (sauf la session actuelle)
|
||||
Et un événement "ALL_TRUSTED_DEVICES_REVOKED" est enregistré avec niveau "HIGH"
|
||||
Et un email de confirmation est envoyé
|
||||
Et l'utilisateur devra saisir un code 2FA à chaque nouvelle connexion
|
||||
Et la métrique "auth.2fa.trusted_device.bulk_revoked" est incrémentée
|
||||
|
||||
Scénario: Métriques de sécurité pour le 2FA
|
||||
Étant donné que le système gère 50 000 utilisateurs avec 2FA activé
|
||||
Quand les métriques de sécurité sont collectées
|
||||
Alors les indicateurs suivants sont disponibles:
|
||||
| Métrique | Valeur cible |
|
||||
| Pourcentage d'utilisateurs avec 2FA | > 60% |
|
||||
| Taux de succès de validation 2FA | > 98% |
|
||||
| Temps moyen de saisie du code 2FA | < 15s |
|
||||
| Nombre d'appareils de confiance par user | Moyenne: 2.5 |
|
||||
| Taux d'utilisation des codes de récup. | < 0.5% |
|
||||
Et les métriques sont exportées vers le système de monitoring
|
||||
Et des alertes sont déclenchées si le taux de succès < 95%
|
||||
|
||||
Scénario: Badge de sécurité pour utilisateurs avec 2FA activé
|
||||
Étant donné un utilisateur "nathan@roadwave.fr" avec 2FA activé depuis 30 jours
|
||||
Quand l'utilisateur consulte son profil public
|
||||
Alors un badge "Compte sécurisé" s'affiche sur son profil
|
||||
Et le badge indique: "Cet utilisateur a activé l'authentification à deux facteurs"
|
||||
Et le badge améliore la visibilité et la crédibilité du créateur de contenu
|
||||
Et la métrique "profile.badge.2fa_secured" est visible
|
||||
171
features/api/authentication/limite-tentatives-connexion.feature
Normal file
171
features/api/authentication/limite-tentatives-connexion.feature
Normal file
@@ -0,0 +1,171 @@
|
||||
# language: fr
|
||||
|
||||
@api @authentication @security @mvp
|
||||
Fonctionnalité: Limitation des tentatives de connexion
|
||||
|
||||
En tant que système de sécurité
|
||||
Je veux limiter les tentatives de connexion échouées
|
||||
Afin de protéger les comptes utilisateurs contre les attaques par force brute
|
||||
|
||||
Contexte:
|
||||
Étant donné que le système est configuré avec les limites suivantes:
|
||||
| Paramètre | Valeur |
|
||||
| Tentatives max avant blocage | 5 |
|
||||
| Durée de blocage temporaire | 15 min |
|
||||
| Tentatives max avant blocage 24h | 10 |
|
||||
| Durée de blocage prolongé | 24h |
|
||||
| Fenêtre de temps pour reset | 30 min |
|
||||
|
||||
Scénario: Connexion réussie réinitialise le compteur de tentatives
|
||||
Étant donné un utilisateur "alice@roadwave.fr" avec 3 tentatives échouées
|
||||
Quand l'utilisateur se connecte avec les bons identifiants
|
||||
Alors la connexion est réussie
|
||||
Et le compteur de tentatives échouées est réinitialisé à 0
|
||||
Et un événement de sécurité "LOGIN_SUCCESS_AFTER_FAILURES" est enregistré
|
||||
|
||||
Scénario: Blocage temporaire après 5 tentatives échouées
|
||||
Étant donné un utilisateur "bob@roadwave.fr" avec 4 tentatives échouées
|
||||
Quand l'utilisateur tente de se connecter avec un mauvais mot de passe
|
||||
Alors la connexion échoue avec le code d'erreur "ACCOUNT_TEMPORARILY_LOCKED"
|
||||
Et le message est "Votre compte est temporairement verrouillé pour 15 minutes suite à de multiples tentatives échouées"
|
||||
Et un email de notification de sécurité est envoyé à "bob@roadwave.fr"
|
||||
Et un événement de sécurité "ACCOUNT_LOCKED_TEMP" est enregistré
|
||||
Et la métrique "security.account_locks.temporary" est incrémentée
|
||||
|
||||
Scénario: Tentative de connexion pendant le blocage temporaire
|
||||
Étant donné un utilisateur "charlie@roadwave.fr" bloqué temporairement
|
||||
Et il reste 10 minutes avant la fin du blocage
|
||||
Quand l'utilisateur tente de se connecter avec les bons identifiants
|
||||
Alors la connexion échoue avec le code d'erreur "ACCOUNT_TEMPORARILY_LOCKED"
|
||||
Et le message contient "Votre compte reste verrouillé pour 10 minutes"
|
||||
Et le temps de blocage restant est indiqué en minutes
|
||||
Et la tentative ne rallonge pas la durée du blocage
|
||||
|
||||
Scénario: Connexion autorisée après expiration du blocage temporaire
|
||||
Étant donné un utilisateur "david@roadwave.fr" bloqué temporairement il y a 16 minutes
|
||||
Quand l'utilisateur tente de se connecter avec les bons identifiants
|
||||
Alors la connexion est réussie
|
||||
Et le compteur de tentatives échouées est réinitialisé à 0
|
||||
Et le statut de blocage est levé
|
||||
Et un événement de sécurité "ACCOUNT_UNLOCKED_AUTO" est enregistré
|
||||
|
||||
Scénario: Blocage prolongé après 10 tentatives échouées sur 24h
|
||||
Étant donné un utilisateur "eve@roadwave.fr" avec historique:
|
||||
| Tentatives échouées | Quand |
|
||||
| 5 | Il y a 2 heures |
|
||||
| Blocage 15min levé | Il y a 1h30 |
|
||||
| 4 | Il y a 30 minutes |
|
||||
Quand l'utilisateur tente une nouvelle connexion échouée
|
||||
Alors la connexion échoue avec le code d'erreur "ACCOUNT_LOCKED_24H"
|
||||
Et le message est "Votre compte est verrouillé pour 24 heures suite à de multiples tentatives suspectes"
|
||||
Et un email urgent de sécurité est envoyé avec un lien de déblocage sécurisé
|
||||
Et une notification SMS est envoyée (si configuré)
|
||||
Et un événement de sécurité "ACCOUNT_LOCKED_24H" est enregistré avec niveau "HIGH"
|
||||
Et la métrique "security.account_locks.prolonged" est incrémentée
|
||||
|
||||
Scénario: Blocage différencié par adresse IP
|
||||
Étant donné un utilisateur "frank@roadwave.fr" avec 3 tentatives échouées depuis IP "1.2.3.4"
|
||||
Quand l'utilisateur se connecte avec succès depuis IP "5.6.7.8"
|
||||
Alors la connexion est réussie
|
||||
Et le compteur de tentatives échouées pour IP "1.2.3.4" reste à 3
|
||||
Et le compteur de tentatives échouées pour IP "5.6.7.8" est à 0
|
||||
Et un événement de sécurité "LOGIN_FROM_NEW_IP" est enregistré
|
||||
|
||||
Scénario: Alerte de sécurité sur pattern suspect multi-IP
|
||||
Étant donné un utilisateur "grace@roadwave.fr"
|
||||
Quand 5 tentatives échouées sont détectées depuis 5 IP différentes en 10 minutes:
|
||||
| IP | Tentatives | Timestamp |
|
||||
| 1.2.3.4 | 2 | Il y a 10 min |
|
||||
| 5.6.7.8 | 1 | Il y a 8 min |
|
||||
| 9.10.11.12 | 1 | Il y a 5 min |
|
||||
| 13.14.15.16| 1 | Il y a 2 min |
|
||||
Alors le compte est immédiatement bloqué pour 24h
|
||||
Et un email d'alerte critique "POSSIBLE_CREDENTIAL_STUFFING_ATTACK" est envoyé
|
||||
Et l'équipe de sécurité est notifiée via webhook
|
||||
Et toutes les sessions actives sont révoquées
|
||||
Et la métrique "security.attacks.credential_stuffing.detected" est incrémentée
|
||||
|
||||
Scénario: Déblocage manuel par l'utilisateur via email sécurisé
|
||||
Étant donné un utilisateur "henry@roadwave.fr" bloqué pour 24h
|
||||
Et il a reçu un email avec un lien de déblocage sécurisé à usage unique
|
||||
Quand l'utilisateur clique sur le lien dans les 2 heures suivant l'email
|
||||
Et confirme son identité via un code envoyé par SMS
|
||||
Alors le compte est débloqué immédiatement
|
||||
Et l'utilisateur est invité à changer son mot de passe
|
||||
Et un événement de sécurité "ACCOUNT_UNLOCKED_MANUAL" est enregistré
|
||||
Et la métrique "security.account_unlocks.user_initiated" est incrémentée
|
||||
|
||||
Scénario: Réinitialisation automatique du compteur après période d'inactivité
|
||||
Étant donné un utilisateur "iris@roadwave.fr" avec 3 tentatives échouées
|
||||
Et aucune nouvelle tentative depuis 35 minutes
|
||||
Quand l'utilisateur tente de se connecter avec un mauvais mot de passe
|
||||
Alors le compteur de tentatives est réinitialisé à 1
|
||||
Et le message d'erreur est standard sans mention de blocage imminent
|
||||
Et un événement de sécurité "ATTEMPT_COUNTER_RESET" est enregistré
|
||||
|
||||
Scénario: Protection contre les attaques par timing
|
||||
Étant donné un utilisateur "jack@roadwave.fr"
|
||||
Quand l'utilisateur effectue 10 tentatives de connexion échouées
|
||||
Alors chaque réponse HTTP prend entre 800ms et 1200ms (temps constant)
|
||||
Et les messages d'erreur ne révèlent pas si l'email existe
|
||||
Et la métrique "security.timing_protection.applied" est incrémentée
|
||||
Et les logs n'exposent pas de patterns de timing exploitables
|
||||
|
||||
Scénario: Escalade des notifications avec tentatives répétées
|
||||
Étant donné un utilisateur "kate@roadwave.fr" Premium
|
||||
Quand les événements suivants se produisent:
|
||||
| Événement | Notification |
|
||||
| 3 tentatives échouées | Aucune notification |
|
||||
| 5 tentatives (blocage) | Email standard |
|
||||
| 10 tentatives (24h) | Email + SMS + notification app|
|
||||
| Tentative pendant 24h | Email urgent + alerte support |
|
||||
Alors chaque niveau de notification est proportionnel à la gravité
|
||||
Et l'utilisateur peut configurer ses préférences de notification
|
||||
Et la métrique "security.notifications.escalated" est incrémentée
|
||||
|
||||
Scénario: Whitelist d'IP pour utilisateurs de confiance
|
||||
Étant donné un utilisateur "luke@roadwave.fr" avec IP de confiance "1.2.3.4"
|
||||
Et la whitelist est configurée pour autoriser 10 tentatives au lieu de 5
|
||||
Quand l'utilisateur effectue 7 tentatives échouées depuis "1.2.3.4"
|
||||
Alors le compte n'est pas bloqué
|
||||
Et un avertissement est affiché "3 tentatives restantes avant blocage"
|
||||
Et un événement de sécurité "TRUSTED_IP_EXTENDED_ATTEMPTS" est enregistré
|
||||
|
||||
Scénario: Logs de sécurité détaillés pour audit
|
||||
Étant donné un utilisateur "mary@roadwave.fr" avec tentatives échouées
|
||||
Quand un audit de sécurité est effectué
|
||||
Alors les logs contiennent pour chaque tentative:
|
||||
| Champ | Exemple |
|
||||
| Timestamp | 2026-02-03T14:32:18.123Z |
|
||||
| User ID | uuid-123-456 |
|
||||
| Email | mary@roadwave.fr |
|
||||
| IP Address | 1.2.3.4 |
|
||||
| User Agent | Mozilla/5.0 (iPhone...) |
|
||||
| Failure Reason | INVALID_PASSWORD |
|
||||
| Attempts Count | 3 |
|
||||
| Geolocation | Paris, France |
|
||||
| Device Fingerprint| hash-abc-def |
|
||||
Et les logs sont conservés pendant 90 jours minimum
|
||||
Et les logs sont conformes RGPD (pas de mots de passe en clair)
|
||||
|
||||
Scénario: Métriques de performance du système de limitation
|
||||
Étant donné que le système traite 1000 tentatives de connexion par minute
|
||||
Quand les métriques de performance sont collectées
|
||||
Alors les indicateurs suivants sont disponibles:
|
||||
| Métrique | Valeur cible |
|
||||
| Temps de vérification du compteur | < 50ms |
|
||||
| Latence ajoutée par le rate limiting | < 100ms |
|
||||
| Pourcentage de tentatives bloquées | < 2% |
|
||||
| Faux positifs (utilisateurs légitimes) | < 0.1% |
|
||||
| Temps de déblocage automatique | < 1s |
|
||||
Et les métriques sont exportées vers le système de monitoring
|
||||
Et des alertes sont déclenchées si les seuils sont dépassés
|
||||
|
||||
Scénario: Compatibilité avec authentification multi-facteurs
|
||||
Étant donné un utilisateur "nathan@roadwave.fr" avec 2FA activé
|
||||
Et il a 4 tentatives échouées (mot de passe correct mais code 2FA incorrect)
|
||||
Quand l'utilisateur tente une 5ème connexion avec mot de passe correct et mauvais code 2FA
|
||||
Alors le compte est bloqué temporairement
|
||||
Et le message précise "Blocage suite à de multiples erreurs de code 2FA"
|
||||
Et le compteur 2FA est distinct du compteur de mot de passe
|
||||
Et un événement de sécurité "2FA_LOCK_TRIGGERED" est enregistré
|
||||
191
features/api/authentication/multi-device-sessions.feature
Normal file
191
features/api/authentication/multi-device-sessions.feature
Normal file
@@ -0,0 +1,191 @@
|
||||
# language: fr
|
||||
|
||||
@api @authentication @sessions @mvp
|
||||
Fonctionnalité: Gestion des sessions multi-appareils
|
||||
|
||||
En tant qu'utilisateur
|
||||
Je veux gérer mes sessions actives sur plusieurs appareils
|
||||
Afin de contrôler l'accès à mon compte et améliorer la sécurité
|
||||
|
||||
Contexte:
|
||||
Étant donné que le système supporte les sessions suivantes:
|
||||
| Paramètre | Valeur |
|
||||
| Nombre max de sessions simultanées | 5 |
|
||||
| Durée de vie d'une session | 30 jours |
|
||||
| Durée d'inactivité avant expiration | 7 jours |
|
||||
| Durée du token de refresh | 90 jours |
|
||||
| Taille max du stockage de session | 10 KB |
|
||||
|
||||
Scénario: Création d'une nouvelle session avec empreinte d'appareil
|
||||
Étant donné un utilisateur "alice@roadwave.fr" non connecté
|
||||
Quand l'utilisateur se connecte depuis un iPhone 14 Pro avec iOS 17.2
|
||||
Alors une nouvelle session est créée avec les métadonnées:
|
||||
| Champ | Valeur |
|
||||
| Device Type | mobile |
|
||||
| OS | iOS 17.2 |
|
||||
| App Version | 1.2.3 |
|
||||
| Device Model | iPhone 14 Pro |
|
||||
| Browser | N/A |
|
||||
| IP Address | 1.2.3.4 |
|
||||
| Geolocation | Paris, France |
|
||||
| Created At | 2026-02-03T14:32:18Z |
|
||||
| Last Activity | 2026-02-03T14:32:18Z |
|
||||
Et un token JWT avec durée de vie de 30 jours est généré
|
||||
Et un refresh token avec durée de vie de 90 jours est généré
|
||||
Et un événement "SESSION_CREATED" est enregistré
|
||||
Et la métrique "sessions.created" est incrémentée
|
||||
|
||||
Scénario: Connexion simultanée sur plusieurs appareils
|
||||
Étant donné un utilisateur "bob@roadwave.fr" connecté sur:
|
||||
| Appareil | OS | Dernière activité |
|
||||
| iPhone 13 | iOS 16.5 | Il y a 5 min |
|
||||
| iPad Pro | iPadOS 17.1 | Il y a 2 heures |
|
||||
| MacBook Pro | macOS 14.2 | Il y a 1 jour |
|
||||
Quand l'utilisateur se connecte depuis un Samsung Galaxy S23
|
||||
Alors une nouvelle session est créée
|
||||
Et l'utilisateur a maintenant 4 sessions actives
|
||||
Et toutes les sessions précédentes restent valides
|
||||
Et un événement "NEW_DEVICE_LOGIN" est enregistré
|
||||
Et une notification push est envoyée sur tous les appareils: "Nouvelle connexion depuis Samsung Galaxy S23"
|
||||
|
||||
Scénario: Limitation du nombre de sessions simultanées
|
||||
Étant donné un utilisateur "charlie@roadwave.fr" avec 5 sessions actives
|
||||
Quand l'utilisateur se connecte depuis un 6ème appareil
|
||||
Alors la session la plus ancienne est automatiquement révoquée
|
||||
Et une nouvelle session est créée pour le nouvel appareil
|
||||
Et l'utilisateur reçoit une notification: "Votre session sur [Ancien Appareil] a été fermée automatiquement"
|
||||
Et un événement "SESSION_EVICTED_MAX_LIMIT" est enregistré
|
||||
Et la métrique "sessions.evicted.max_limit" est incrémentée
|
||||
|
||||
Scénario: Liste des sessions actives dans les paramètres du compte
|
||||
Étant donné un utilisateur "david@roadwave.fr" avec 3 sessions actives
|
||||
Quand l'utilisateur accède à "Mon compte > Sécurité > Appareils connectés"
|
||||
Alors l'utilisateur voit la liste suivante:
|
||||
| Appareil | Localisation | Dernière activité | IP | Actions |
|
||||
| iPhone 14 Pro | Paris, France | Actif maintenant | 1.2.3.4 | [Cet appareil]|
|
||||
| iPad Air | Lyon, France | Il y a 2 heures | 5.6.7.8 | [Déconnecter] |
|
||||
| MacBook Pro | Marseille, FR | Il y a 3 jours | 9.10.11.12| [Déconnecter] |
|
||||
Et la session actuelle est clairement identifiée
|
||||
Et un bouton "Déconnecter tous les autres appareils" est disponible
|
||||
|
||||
Scénario: Révocation manuelle d'une session spécifique
|
||||
Étant donné un utilisateur "eve@roadwave.fr" avec 4 sessions actives
|
||||
Et il consulte la liste de ses appareils depuis son iPhone
|
||||
Quand l'utilisateur clique sur "Déconnecter" pour la session "MacBook Pro"
|
||||
Alors la session "MacBook Pro" est immédiatement révoquée
|
||||
Et le token JWT associé est invalidé dans Redis
|
||||
Et le refresh token est révoqué
|
||||
Et l'utilisateur sur le MacBook Pro est déconnecté lors de sa prochaine requête
|
||||
Et un événement "SESSION_REVOKED_MANUAL" est enregistré
|
||||
Et une notification est envoyée: "Vous avez été déconnecté de votre MacBook Pro"
|
||||
|
||||
Scénario: Déconnexion de tous les autres appareils
|
||||
Étant donné un utilisateur "frank@roadwave.fr" avec 5 sessions actives
|
||||
Et il suspecte un accès non autorisé
|
||||
Quand l'utilisateur clique sur "Déconnecter tous les autres appareils" depuis son iPhone
|
||||
Alors toutes les sessions sauf la session actuelle (iPhone) sont révoquées
|
||||
Et 4 tokens JWT sont invalidés
|
||||
Et 4 refresh tokens sont révoqués
|
||||
Et un événement "SESSIONS_REVOKED_ALL_OTHER" est enregistré
|
||||
Et une notification est envoyée sur tous les appareils déconnectés
|
||||
Et un email de confirmation est envoyé: "Vous avez déconnecté tous vos autres appareils"
|
||||
Et la métrique "sessions.revoked.bulk" est incrémentée
|
||||
|
||||
Scénario: Détection de connexion suspecte depuis un nouveau pays
|
||||
Étant donné un utilisateur "grace@roadwave.fr" avec sessions habituelles en France
|
||||
Quand l'utilisateur se connecte depuis une IP en Russie
|
||||
Alors une alerte de sécurité est déclenchée
|
||||
Et un email est envoyé: "Connexion détectée depuis Russie - Est-ce bien vous ?"
|
||||
Et une notification push est envoyée sur tous les appareils de confiance
|
||||
Et la session est créée mais marquée comme "suspecte"
|
||||
Et un événement "SUSPICIOUS_LOCATION_LOGIN" est enregistré avec niveau "HIGH"
|
||||
Et l'utilisateur doit confirmer son identité par code SMS avant d'accéder aux fonctionnalités sensibles
|
||||
|
||||
Scénario: Expiration automatique d'une session inactive
|
||||
Étant donné un utilisateur "henry@roadwave.fr" avec une session sur iPad
|
||||
Et la session n'a pas été utilisée depuis 8 jours
|
||||
Quand le job de nettoyage des sessions s'exécute
|
||||
Alors la session iPad est automatiquement révoquée
|
||||
Et le token JWT est invalidé
|
||||
Et le refresh token est révoqué
|
||||
Et un événement "SESSION_EXPIRED_INACTIVITY" est enregistré
|
||||
Et un email est envoyé: "Votre session sur iPad a expiré suite à 8 jours d'inactivité"
|
||||
Et la métrique "sessions.expired.inactivity" est incrémentée
|
||||
|
||||
Scénario: Rafraîchissement automatique du token avant expiration
|
||||
Étant donné un utilisateur "iris@roadwave.fr" avec une session active
|
||||
Et le token JWT expire dans 2 minutes
|
||||
Quand l'application mobile effectue une requête API
|
||||
Alors l'API détecte que le token expire bientôt
|
||||
Et un nouveau token JWT est généré automatiquement
|
||||
Et le nouveau token est retourné dans le header "X-Refreshed-Token"
|
||||
Et l'application mobile stocke le nouveau token
|
||||
Et un événement "TOKEN_REFRESHED" est enregistré
|
||||
Et la métrique "tokens.refreshed" est incrémentée
|
||||
|
||||
Scénario: Révocation de toutes les sessions lors d'un changement de mot de passe
|
||||
Étant donné un utilisateur "jack@roadwave.fr" avec 4 sessions actives
|
||||
Quand l'utilisateur change son mot de passe depuis son iPhone
|
||||
Alors toutes les sessions sauf la session actuelle (iPhone) sont révoquées
|
||||
Et tous les tokens JWT sont invalidés
|
||||
Et tous les refresh tokens sont révoqués
|
||||
Et un événement "SESSIONS_REVOKED_PASSWORD_CHANGE" est enregistré
|
||||
Et un email est envoyé: "Votre mot de passe a été modifié. Toutes vos autres sessions ont été déconnectées."
|
||||
Et des notifications push sont envoyées sur tous les appareils déconnectés
|
||||
|
||||
Scénario: Persistance de la session avec "Se souvenir de moi"
|
||||
Étant donné un utilisateur "kate@roadwave.fr" qui se connecte
|
||||
Quand l'utilisateur coche l'option "Se souvenir de moi"
|
||||
Alors la durée de vie du token JWT est étendue à 90 jours
|
||||
Et la durée de vie du refresh token est étendue à 180 jours
|
||||
Et la session persiste même après fermeture de l'application
|
||||
Et un cookie sécurisé "remember_token" est stocké (pour web)
|
||||
Et un événement "LONG_SESSION_CREATED" est enregistré
|
||||
Et la métrique "sessions.remember_me.enabled" est incrémentée
|
||||
|
||||
Scénario: Détection de vol de token et révocation automatique
|
||||
Étant donné un utilisateur "luke@roadwave.fr" avec une session active
|
||||
Et le token JWT a été volé et utilisé depuis une IP différente
|
||||
Quand le système détecte une utilisation simultanée du même token depuis 2 IP différentes
|
||||
Alors toutes les sessions de l'utilisateur sont immédiatement révoquées
|
||||
Et tous les tokens sont invalidés
|
||||
Et un email d'alerte critique est envoyé: "Activité suspecte détectée - Toutes vos sessions ont été fermées"
|
||||
Et une notification push urgente est envoyée sur tous les appareils
|
||||
Et l'utilisateur doit réinitialiser son mot de passe avant de se reconnecter
|
||||
Et un événement "TOKEN_THEFT_DETECTED" est enregistré avec niveau "CRITICAL"
|
||||
Et l'équipe de sécurité est alertée via webhook
|
||||
|
||||
Scénario: Synchronisation des informations de session en temps réel
|
||||
Étant donné un utilisateur "mary@roadwave.fr" connecté sur 3 appareils
|
||||
Quand l'utilisateur révoque une session depuis son iPhone
|
||||
Alors la liste des sessions est mise à jour en temps réel sur tous les appareils via WebSocket
|
||||
Et l'appareil déconnecté reçoit immédiatement une notification de déconnexion
|
||||
Et l'UI est rafraîchie automatiquement sur tous les appareils connectés
|
||||
Et la métrique "sessions.realtime_sync" est incrémentée
|
||||
|
||||
Scénario: Métriques de performance de gestion des sessions
|
||||
Étant donné que le système gère 100 000 sessions actives
|
||||
Quand les métriques de performance sont collectées
|
||||
Alors les indicateurs suivants sont respectés:
|
||||
| Métrique | Valeur cible |
|
||||
| Temps de création de session | < 50ms |
|
||||
| Temps de validation de token | < 20ms |
|
||||
| Temps de révocation de session | < 100ms |
|
||||
| Latence de synchronisation temps réel | < 500ms |
|
||||
| Taux de succès du refresh automatique | > 99.9% |
|
||||
Et les métriques sont exportées vers le système de monitoring
|
||||
Et des alertes sont déclenchées si les seuils sont dépassés
|
||||
|
||||
Scénario: Stockage sécurisé des sessions dans Redis
|
||||
Étant donné un utilisateur "nathan@roadwave.fr" avec une session active
|
||||
Quand la session est stockée dans Redis
|
||||
Alors les données suivantes sont chiffrées:
|
||||
| Champ | Chiffrement |
|
||||
| User ID | Hash |
|
||||
| Refresh Token | AES-256 |
|
||||
| Device Info | Non |
|
||||
| IP Address | Hash |
|
||||
Et la clé Redis a un TTL correspondant à la durée de vie de la session
|
||||
Et les données sensibles ne sont jamais loggées en clair
|
||||
Et les accès à Redis sont audités
|
||||
Et la métrique "sessions.storage.encrypted" est incrémentée
|
||||
@@ -0,0 +1,187 @@
|
||||
# language: fr
|
||||
|
||||
@api @authentication @security @mvp
|
||||
Fonctionnalité: Récupération et réinitialisation avancée du mot de passe
|
||||
|
||||
En tant qu'utilisateur ayant oublié son mot de passe
|
||||
Je veux pouvoir récupérer l'accès à mon compte de manière sécurisée
|
||||
Afin de reprendre l'utilisation de l'application
|
||||
|
||||
Contexte:
|
||||
Étant donné que le système de récupération est configuré avec:
|
||||
| Paramètre | Valeur |
|
||||
| Durée de validité du lien de reset | 1 heure |
|
||||
| Nombre max de demandes par heure | 3 |
|
||||
| Nombre max de demandes par jour | 10 |
|
||||
| Longueur du token de reset | 64 chars |
|
||||
| Délai de cooldown entre demandes | 5 minutes |
|
||||
|
||||
Scénario: Demande de réinitialisation de mot de passe
|
||||
Étant donné un utilisateur "alice@roadwave.fr" qui a oublié son mot de passe
|
||||
Quand l'utilisateur clique sur "Mot de passe oublié ?" sur l'écran de connexion
|
||||
Et saisit son adresse email "alice@roadwave.fr"
|
||||
Alors un email de réinitialisation est envoyé avec:
|
||||
| Élément | Contenu |
|
||||
| Sujet | Réinitialisation de votre mot de passe RoadWave |
|
||||
| Lien sécurisé | https://roadwave.fr/reset?token=abc123... |
|
||||
| Durée de validité | Ce lien expire dans 1 heure |
|
||||
| Warning sécurité | Si vous n'êtes pas à l'origine de cette demande... |
|
||||
Et un événement "PASSWORD_RESET_REQUESTED" est enregistré
|
||||
Et la métrique "auth.password_reset.requested" est incrémentée
|
||||
Et un message s'affiche: "Si cette adresse est enregistrée, vous recevrez un email de réinitialisation"
|
||||
|
||||
Scénario: Protection contre l'énumération d'adresses email
|
||||
Étant donné une adresse email "inexistant@roadwave.fr" non enregistrée
|
||||
Quand un utilisateur demande la réinitialisation pour cette adresse
|
||||
Alors le même message de confirmation s'affiche: "Si cette adresse est enregistrée, vous recevrez un email"
|
||||
Et aucun email n'est envoyé
|
||||
Et le temps de réponse est identique à une demande valide (800-1200ms)
|
||||
Et un événement "PASSWORD_RESET_UNKNOWN_EMAIL" est enregistré
|
||||
Et la métrique "auth.password_reset.unknown_email" est incrémentée
|
||||
Et les logs n'exposent pas l'information de l'existence ou non de l'email
|
||||
|
||||
Scénario: Limitation du nombre de demandes de réinitialisation
|
||||
Étant donné un utilisateur "bob@roadwave.fr"
|
||||
Et il a déjà effectué 3 demandes de réinitialisation dans la dernière heure
|
||||
Quand l'utilisateur effectue une 4ème demande
|
||||
Alors la demande est refusée avec le message: "Trop de demandes de réinitialisation. Veuillez attendre 1 heure."
|
||||
Et aucun email n'est envoyé
|
||||
Et un événement "PASSWORD_RESET_RATE_LIMITED" est enregistré
|
||||
Et la métrique "auth.password_reset.rate_limited" est incrémentée
|
||||
|
||||
Scénario: Utilisation du lien de réinitialisation valide
|
||||
Étant donné un utilisateur "charlie@roadwave.fr" ayant demandé la réinitialisation
|
||||
Et il a reçu un email avec un token valide il y a 30 minutes
|
||||
Quand l'utilisateur clique sur le lien dans l'email
|
||||
Alors il est redirigé vers la page de réinitialisation
|
||||
Et le formulaire de nouveau mot de passe s'affiche
|
||||
Et le token est validé côté serveur
|
||||
Et un événement "PASSWORD_RESET_TOKEN_ACCESSED" est enregistré
|
||||
Et la session est sécurisée avec CSRF protection
|
||||
|
||||
Scénario: Définition du nouveau mot de passe avec validation
|
||||
Étant donné un utilisateur "david@roadwave.fr" sur la page de réinitialisation
|
||||
Et il a un token valide
|
||||
Quand l'utilisateur saisit un nouveau mot de passe "SecurePass2026!"
|
||||
Et confirme le mot de passe
|
||||
Alors le mot de passe est validé selon les règles de sécurité
|
||||
Et le mot de passe est hashé avec bcrypt (cost: 12)
|
||||
Et le mot de passe est enregistré dans la base de données
|
||||
Et toutes les sessions actives sont révoquées
|
||||
Et tous les tokens d'accès sont invalidés
|
||||
Et un événement "PASSWORD_RESET_COMPLETED" est enregistré
|
||||
Et un email de confirmation est envoyé: "Votre mot de passe a été modifié avec succès"
|
||||
Et la métrique "auth.password_reset.completed" est incrémentée
|
||||
Et l'utilisateur est redirigé vers la page de connexion
|
||||
|
||||
Scénario: Tentative d'utilisation d'un token expiré
|
||||
Étant donné un utilisateur "eve@roadwave.fr" ayant demandé la réinitialisation
|
||||
Et il a reçu un email avec un token valide il y a 2 heures
|
||||
Quand l'utilisateur clique sur le lien expiré
|
||||
Alors un message d'erreur s'affiche: "Ce lien de réinitialisation a expiré. Veuillez faire une nouvelle demande."
|
||||
Et un bouton "Demander un nouveau lien" est affiché
|
||||
Et un événement "PASSWORD_RESET_TOKEN_EXPIRED" est enregistré
|
||||
Et la métrique "auth.password_reset.token_expired" est incrémentée
|
||||
|
||||
Scénario: Tentative d'utilisation d'un token déjà utilisé
|
||||
Étant donné un utilisateur "frank@roadwave.fr" ayant réinitialisé son mot de passe
|
||||
Et le token a déjà été utilisé il y a 10 minutes
|
||||
Quand l'utilisateur tente de réutiliser le même lien
|
||||
Alors un message d'erreur s'affiche: "Ce lien a déjà été utilisé. Si vous avez besoin de réinitialiser à nouveau, faites une nouvelle demande."
|
||||
Et un événement "PASSWORD_RESET_TOKEN_REUSED" est enregistré avec niveau "MEDIUM"
|
||||
Et un email d'alerte est envoyé: "Tentative de réutilisation d'un ancien lien de réinitialisation"
|
||||
Et la métrique "auth.password_reset.token_reused" est incrémentée
|
||||
|
||||
Scénario: Détection de tentative d'attaque par force brute sur les tokens
|
||||
Étant donné un attaquant qui tente de deviner des tokens de réinitialisation
|
||||
Quand 10 tokens invalides sont testés depuis la même IP en 5 minutes
|
||||
Alors l'IP est bloquée temporairement pour 1 heure
|
||||
Et tous les tokens valides pour cette IP sont invalidés
|
||||
Et un événement "PASSWORD_RESET_BRUTE_FORCE_DETECTED" est enregistré avec niveau "CRITICAL"
|
||||
Et l'équipe de sécurité est alertée via webhook
|
||||
Et la métrique "security.password_reset.brute_force" est incrémentée
|
||||
|
||||
Scénario: Réinitialisation avec validation 2FA pour comptes sensibles
|
||||
Étant donné un utilisateur "grace@roadwave.fr" avec 2FA activé
|
||||
Et il a demandé la réinitialisation de son mot de passe
|
||||
Quand l'utilisateur clique sur le lien de réinitialisation
|
||||
Alors une étape supplémentaire de vérification 2FA s'affiche
|
||||
Et l'utilisateur doit saisir un code TOTP ou un code de récupération
|
||||
Et après validation 2FA, le formulaire de nouveau mot de passe s'affiche
|
||||
Et un événement "PASSWORD_RESET_2FA_VALIDATED" est enregistré
|
||||
Et la métrique "auth.password_reset.with_2fa" est incrémentée
|
||||
|
||||
Scénario: Notification de sécurité sur tous les appareils
|
||||
Étant donné un utilisateur "henry@roadwave.fr" connecté sur 3 appareils
|
||||
Quand l'utilisateur réinitialise son mot de passe
|
||||
Alors une notification push est envoyée sur tous les appareils:
|
||||
| Message |
|
||||
| Votre mot de passe a été modifié |
|
||||
| Si ce n'est pas vous, contactez immédiatement le support |
|
||||
Et un email est envoyé avec détails:
|
||||
| Détail | Valeur |
|
||||
| Date et heure | 2026-02-03 14:32:18 |
|
||||
| Adresse IP | 1.2.3.4 |
|
||||
| Localisation | Paris, France |
|
||||
| Appareil | iPhone 14 Pro |
|
||||
| Navigateur | Safari 17.2 |
|
||||
Et un lien "Ce n'était pas moi" permet de bloquer le compte immédiatement
|
||||
|
||||
Scénario: Historique des modifications de mot de passe
|
||||
Étant donné un utilisateur "iris@roadwave.fr"
|
||||
Quand l'utilisateur accède à "Mon compte > Sécurité > Historique"
|
||||
Alors l'utilisateur voit l'historique des modifications:
|
||||
| Date | Action | IP | Appareil | Localisation |
|
||||
| 2026-02-03 14:32 | Réinitialisation mot de passe | 1.2.3.4 | iPhone 14 | Paris, FR |
|
||||
| 2026-01-15 10:20 | Changement mot de passe | 5.6.7.8 | MacBook Pro | Lyon, FR |
|
||||
| 2025-12-01 08:15 | Création du compte | 9.10.11.12| iPad Air | Marseille, FR |
|
||||
Et les événements sont conservés pendant 90 jours minimum
|
||||
Et les logs sont conformes RGPD
|
||||
|
||||
Scénario: Réinitialisation impossible pour compte bloqué ou suspendu
|
||||
Étant donné un utilisateur "jack@roadwave.fr" dont le compte est suspendu
|
||||
Quand l'utilisateur demande la réinitialisation de son mot de passe
|
||||
Alors un message s'affiche: "Votre compte est actuellement suspendu. Veuillez contacter le support."
|
||||
Et aucun email de réinitialisation n'est envoyé
|
||||
Et un événement "PASSWORD_RESET_ACCOUNT_SUSPENDED" est enregistré
|
||||
Et un lien vers le support est fourni
|
||||
Et la métrique "auth.password_reset.blocked_account" est incrémentée
|
||||
|
||||
Scénario: Vérification de l'unicité du nouveau mot de passe
|
||||
Étant donné un utilisateur "kate@roadwave.fr" sur la page de réinitialisation
|
||||
Quand l'utilisateur tente de définir le même mot de passe que l'ancien
|
||||
Alors une erreur s'affiche: "Veuillez choisir un mot de passe différent de l'ancien"
|
||||
Et le mot de passe n'est pas enregistré
|
||||
Et un événement "PASSWORD_RESET_SAME_PASSWORD" est enregistré
|
||||
Et la métrique "auth.password_reset.same_password" est incrémentée
|
||||
|
||||
Scénario: Vérification contre les mots de passe compromis
|
||||
Étant donné un utilisateur "luke@roadwave.fr" sur la page de réinitialisation
|
||||
Quand l'utilisateur tente de définir un mot de passe "Password123!"
|
||||
Et ce mot de passe figure dans la base de données Have I Been Pwned
|
||||
Alors une erreur s'affiche: "Ce mot de passe est connu et a été compromis. Veuillez en choisir un autre."
|
||||
Et le mot de passe n'est pas enregistré
|
||||
Et un événement "PASSWORD_RESET_COMPROMISED_PASSWORD" est enregistré
|
||||
Et la métrique "auth.password_reset.compromised_blocked" est incrémentée
|
||||
|
||||
Scénario: Cooldown entre demandes successives de réinitialisation
|
||||
Étant donné un utilisateur "mary@roadwave.fr"
|
||||
Et il a fait une demande de réinitialisation il y a 2 minutes
|
||||
Quand l'utilisateur fait une nouvelle demande de réinitialisation
|
||||
Alors la demande est refusée avec le message: "Veuillez attendre 5 minutes entre chaque demande"
|
||||
Et un compteur affiche "Vous pourrez faire une nouvelle demande dans 3 minutes"
|
||||
Et un événement "PASSWORD_RESET_COOLDOWN" est enregistré
|
||||
Et la métrique "auth.password_reset.cooldown_hit" est incrémentée
|
||||
|
||||
Scénario: Métriques de sécurité pour la réinitialisation de mot de passe
|
||||
Étant donné que le système traite 1000 demandes de réinitialisation par jour
|
||||
Quand les métriques de sécurité sont collectées
|
||||
Alors les indicateurs suivants sont disponibles:
|
||||
| Métrique | Valeur cible |
|
||||
| Taux de complétion des réinitialisations | > 75% |
|
||||
| Taux de tokens expirés avant utilisation | < 20% |
|
||||
| Temps moyen de complétion | < 5 min |
|
||||
| Taux de détection de mots de passe compromis | > 5% |
|
||||
| Nombre de tentatives de brute force bloquées | Visible |
|
||||
Et les métriques sont exportées vers le système de monitoring
|
||||
Et des alertes sont déclenchées si anomalies détectées
|
||||
250
features/api/authentication/validation-mot-passe.feature
Normal file
250
features/api/authentication/validation-mot-passe.feature
Normal file
@@ -0,0 +1,250 @@
|
||||
# language: fr
|
||||
|
||||
@api @authentication @security @mvp
|
||||
Fonctionnalité: Validation des règles de mot de passe
|
||||
|
||||
En tant que système d'authentification
|
||||
Je veux valider la complexité des mots de passe
|
||||
Afin de garantir la sécurité des comptes utilisateurs
|
||||
|
||||
Contexte:
|
||||
Étant donné un utilisateur souhaite créer un compte ou modifier son mot de passe
|
||||
|
||||
# ============================================================================
|
||||
# VALIDATION LONGUEUR MINIMALE (8 CARACTÈRES)
|
||||
# ============================================================================
|
||||
|
||||
Scénario: Mot de passe valide avec 8 caractères minimum
|
||||
Étant donné l'utilisateur saisit le mot de passe "Azerty123"
|
||||
Quand le système valide le mot de passe
|
||||
Alors la validation doit réussir
|
||||
Et aucune erreur ne doit être affichée
|
||||
|
||||
Scénario: Mot de passe trop court (7 caractères)
|
||||
Étant donné l'utilisateur saisit le mot de passe "Azert12"
|
||||
Quand le système valide le mot de passe
|
||||
Alors la validation doit échouer
|
||||
Et le message d'erreur doit être "Le mot de passe doit contenir au moins 8 caractères"
|
||||
Et le champ doit être marqué en rouge
|
||||
|
||||
Scénario: Mot de passe très court (3 caractères)
|
||||
Étant donné l'utilisateur saisit le mot de passe "Ab1"
|
||||
Quand le système valide le mot de passe
|
||||
Alors la validation doit échouer
|
||||
Et le message d'erreur doit être "Le mot de passe doit contenir au moins 8 caractères"
|
||||
|
||||
# ============================================================================
|
||||
# VALIDATION MAJUSCULE REQUISE
|
||||
# ============================================================================
|
||||
|
||||
Scénario: Mot de passe valide avec au moins 1 majuscule
|
||||
Étant donné l'utilisateur saisit le mot de passe "Monpass123"
|
||||
Quand le système valide le mot de passe
|
||||
Alors la validation doit réussir
|
||||
Et le critère "majuscule" doit être validé avec une coche verte
|
||||
|
||||
Scénario: Mot de passe sans majuscule
|
||||
Étant donné l'utilisateur saisit le mot de passe "monpass123"
|
||||
Quand le système valide le mot de passe
|
||||
Alors la validation doit échouer
|
||||
Et le message d'erreur doit être "Le mot de passe doit contenir au moins 1 majuscule"
|
||||
|
||||
Scénario: Mot de passe avec plusieurs majuscules
|
||||
Étant donné l'utilisateur saisit le mot de passe "MonPASSword123"
|
||||
Quand le système valide le mot de passe
|
||||
Alors la validation doit réussir
|
||||
Car au moins 1 majuscule est présente
|
||||
|
||||
# ============================================================================
|
||||
# VALIDATION CHIFFRE REQUIS
|
||||
# ============================================================================
|
||||
|
||||
Scénario: Mot de passe valide avec au moins 1 chiffre
|
||||
Étant donné l'utilisateur saisit le mot de passe "Monpass1"
|
||||
Quand le système valide le mot de passe
|
||||
Alors la validation doit réussir
|
||||
Et le critère "chiffre" doit être validé avec une coche verte
|
||||
|
||||
Scénario: Mot de passe sans chiffre
|
||||
Étant donné l'utilisateur saisit le mot de passe "Monpassword"
|
||||
Quand le système valide le mot de passe
|
||||
Alors la validation doit échouer
|
||||
Et le message d'erreur doit être "Le mot de passe doit contenir au moins 1 chiffre"
|
||||
|
||||
Scénario: Mot de passe avec plusieurs chiffres
|
||||
Étant donné l'utilisateur saisit le mot de passe "Monpass123456"
|
||||
Quand le système valide le mot de passe
|
||||
Alors la validation doit réussir
|
||||
Car au moins 1 chiffre est présent
|
||||
|
||||
# ============================================================================
|
||||
# VALIDATION COMBINÉE DES 3 CRITÈRES
|
||||
# ============================================================================
|
||||
|
||||
Scénario: Mot de passe valide respectant tous les critères
|
||||
Étant donné l'utilisateur saisit le mot de passe "SecurePass2024!"
|
||||
Quand le système valide le mot de passe
|
||||
Alors la validation doit réussir
|
||||
Et tous les critères doivent être validés :
|
||||
| critère | statut |
|
||||
| longueur | ✓ |
|
||||
| majuscule | ✓ |
|
||||
| chiffre | ✓ |
|
||||
|
||||
Scénario: Mot de passe échouant sur plusieurs critères
|
||||
Étant donné l'utilisateur saisit le mot de passe "pass"
|
||||
Quand le système valide le mot de passe
|
||||
Alors la validation doit échouer
|
||||
Et les messages d'erreur suivants doivent être affichés :
|
||||
| Le mot de passe doit contenir au moins 8 caractères |
|
||||
| Le mot de passe doit contenir au moins 1 majuscule |
|
||||
| Le mot de passe doit contenir au moins 1 chiffre |
|
||||
|
||||
Scénario: Mot de passe long mais sans majuscule ni chiffre
|
||||
Étant donné l'utilisateur saisit le mot de passe "monmotdepasse"
|
||||
Quand le système valide le mot de passe
|
||||
Alors la validation doit échouer
|
||||
Et les messages d'erreur suivants doivent être affichés :
|
||||
| Le mot de passe doit contenir au moins 1 majuscule |
|
||||
| Le mot de passe doit contenir au moins 1 chiffre |
|
||||
|
||||
# ============================================================================
|
||||
# VALIDATION TEMPS RÉEL (FRONTEND)
|
||||
# ============================================================================
|
||||
|
||||
Scénario: Affichage progressif des critères pendant la saisie
|
||||
Étant donné l'utilisateur commence à saisir son mot de passe
|
||||
Quand l'utilisateur tape "m"
|
||||
Alors les critères suivants doivent être affichés :
|
||||
| critère | statut |
|
||||
| longueur | ✗ |
|
||||
| majuscule | ✗ |
|
||||
| chiffre | ✗ |
|
||||
Quand l'utilisateur tape "Mon"
|
||||
Alors les critères doivent être mis à jour :
|
||||
| critère | statut |
|
||||
| longueur | ✗ |
|
||||
| majuscule | ✓ |
|
||||
| chiffre | ✗ |
|
||||
Quand l'utilisateur tape "Monpass1"
|
||||
Alors les critères doivent être mis à jour :
|
||||
| critère | statut |
|
||||
| longueur | ✓ |
|
||||
| majuscule | ✓ |
|
||||
| chiffre | ✓ |
|
||||
|
||||
Scénario: Feedback visuel temps réel
|
||||
Étant donné l'utilisateur saisit progressivement son mot de passe
|
||||
Quand un critère est validé
|
||||
Alors une coche verte ✓ doit apparaître à côté du critère
|
||||
Et le texte du critère doit passer en vert
|
||||
Quand un critère n'est pas validé
|
||||
Alors une croix rouge ✗ doit apparaître
|
||||
Et le texte du critère doit rester en gris ou rouge
|
||||
|
||||
# ============================================================================
|
||||
# VALIDATION BACKEND (SÉCURITÉ)
|
||||
# ============================================================================
|
||||
|
||||
Scénario: Validation backend en plus du frontend
|
||||
Étant donné l'utilisateur contourne la validation frontend
|
||||
Et envoie directement le mot de passe "weak" via API
|
||||
Quand le backend reçoit la requête
|
||||
Alors la validation backend doit rejeter le mot de passe
|
||||
Et retourner une erreur HTTP 400 Bad Request
|
||||
Et le message doit être :
|
||||
"""
|
||||
{
|
||||
"error": "invalid_password",
|
||||
"details": [
|
||||
"Le mot de passe doit contenir au moins 8 caractères",
|
||||
"Le mot de passe doit contenir au moins 1 majuscule",
|
||||
"Le mot de passe doit contenir au moins 1 chiffre"
|
||||
]
|
||||
}
|
||||
"""
|
||||
|
||||
Scénario: Validation backend avec mot de passe valide
|
||||
Étant donné l'utilisateur envoie le mot de passe "SecurePass123"
|
||||
Quand le backend valide le mot de passe
|
||||
Alors la validation backend doit réussir
|
||||
Et le mot de passe doit être hashé avec bcrypt (coût 12)
|
||||
Et le hash doit être stocké dans la base de données
|
||||
|
||||
# ============================================================================
|
||||
# CAS LIMITES ET CARACTÈRES SPÉCIAUX
|
||||
# ============================================================================
|
||||
|
||||
Scénario: Mot de passe avec caractères spéciaux (acceptés)
|
||||
Étant donné l'utilisateur saisit le mot de passe "MonP@ss123!"
|
||||
Quand le système valide le mot de passe
|
||||
Alors la validation doit réussir
|
||||
Car les caractères spéciaux sont autorisés (mais non obligatoires)
|
||||
|
||||
Scénario: Mot de passe avec espaces (acceptés)
|
||||
Étant donné l'utilisateur saisit le mot de passe "Mon Pass 123"
|
||||
Quand le système valide le mot de passe
|
||||
Alors la validation doit réussir
|
||||
Car les espaces sont autorisés
|
||||
|
||||
Scénario: Mot de passe avec accents (acceptés)
|
||||
Étant donné l'utilisateur saisit le mot de passe "MônPàss123"
|
||||
Quand le système valide le mot de passe
|
||||
Alors la validation doit réussir
|
||||
Car les caractères accentués comptent comme des lettres
|
||||
|
||||
Scénario: Mot de passe avec émojis (acceptés)
|
||||
Étant donné l'utilisateur saisit le mot de passe "MonPass123🔒"
|
||||
Quand le système valide le mot de passe
|
||||
Alors la validation doit réussir
|
||||
Car les émojis sont autorisés
|
||||
|
||||
Scénario: Mot de passe vide
|
||||
Étant donné l'utilisateur laisse le champ mot de passe vide
|
||||
Quand le système valide le mot de passe
|
||||
Alors la validation doit échouer
|
||||
Et le message d'erreur doit être "Le mot de passe est requis"
|
||||
|
||||
# ============================================================================
|
||||
# MODIFICATION MOT DE PASSE
|
||||
# ============================================================================
|
||||
|
||||
Scénario: Changement de mot de passe avec validation
|
||||
Étant donné un utilisateur authentifié veut changer son mot de passe
|
||||
Et l'utilisateur saisit son ancien mot de passe "OldPass123"
|
||||
Et l'utilisateur saisit le nouveau mot de passe "NewSecure456"
|
||||
Quand le système valide le nouveau mot de passe
|
||||
Alors la validation doit réussir
|
||||
Et le nouveau mot de passe doit respecter les mêmes règles
|
||||
Et l'ancien mot de passe doit être vérifié avant le changement
|
||||
|
||||
Scénario: Nouveau mot de passe identique à l'ancien (autorisé)
|
||||
Étant donné un utilisateur veut changer son mot de passe
|
||||
Et l'utilisateur saisit le nouveau mot de passe identique à l'ancien
|
||||
Quand le système valide le mot de passe
|
||||
Alors la validation doit réussir
|
||||
Car il n'y a pas de règle interdisant la réutilisation
|
||||
|
||||
# ============================================================================
|
||||
# MESSAGES D'AIDE ET UX
|
||||
# ============================================================================
|
||||
|
||||
Scénario: Affichage des règles avant saisie
|
||||
Étant donné l'utilisateur accède au formulaire d'inscription
|
||||
Quand le champ mot de passe reçoit le focus
|
||||
Alors une info-bulle doit s'afficher avec les règles :
|
||||
"""
|
||||
Votre mot de passe doit contenir :
|
||||
• Au moins 8 caractères
|
||||
• Au moins 1 majuscule
|
||||
• Au moins 1 chiffre
|
||||
"""
|
||||
|
||||
Scénario: Indicateur de force du mot de passe
|
||||
Étant donné l'utilisateur saisit progressivement son mot de passe
|
||||
Quand l'utilisateur tape "Weak1"
|
||||
Alors l'indicateur de force doit afficher "Faible" en orange
|
||||
Quand l'utilisateur tape "Medium12"
|
||||
Alors l'indicateur de force doit afficher "Moyen" en jaune
|
||||
Quand l'utilisateur tape "VeryStrong123!"
|
||||
Alors l'indicateur de force doit afficher "Fort" en vert
|
||||
Reference in New Issue
Block a user