refactor(docs): réorganiser la documentation selon principes DDD
Réorganise la documentation du projet selon les principes du Domain-Driven Design (DDD) pour améliorer la cohésion, la maintenabilité et l'alignement avec l'architecture modulaire du backend. **Structure cible:** ``` docs/domains/ ├── README.md (Context Map) ├── _shared/ (Core Domain) ├── recommendation/ (Supporting Subdomain) ├── content/ (Supporting Subdomain) ├── moderation/ (Supporting Subdomain) ├── advertising/ (Generic Subdomain) ├── premium/ (Generic Subdomain) └── monetization/ (Generic Subdomain) ``` **Changements effectués:** Phase 1: Création de l'arborescence des 7 bounded contexts Phase 2: Déplacement des règles métier (01-19) vers domains/*/rules/ Phase 3: Déplacement des diagrammes d'entités vers domains/*/entities/ Phase 4: Déplacement des diagrammes flux/états/séquences vers domains/*/ Phase 5: Création des README.md pour chaque domaine Phase 6: Déplacement des features Gherkin vers domains/*/features/ Phase 7: Création du Context Map (domains/README.md) Phase 8: Mise à jour de mkdocs.yml pour la nouvelle navigation Phase 9: Correction automatique des liens internes (script fix-markdown-links.sh) Phase 10: Nettoyage de l'ancienne structure (regles-metier/, diagrammes/, features/) **Configuration des tests:** - Makefile: godog run docs/domains/*/features/ - scripts/generate-bdd-docs.py: features_dir → docs/domains **Avantages:** ✅ Cohésion forte: toute la doc d'un domaine au même endroit ✅ Couplage faible: domaines indépendants, dépendances explicites ✅ Navigabilité améliorée: README par domaine = entrée claire ✅ Alignement code/docs: miroir de backend/internal/ ✅ Onboarding facilité: exploration domaine par domaine ✅ Tests BDD intégrés: features au plus près des règles métier Voir docs/REFACTOR-DDD.md pour le plan complet.
This commit is contained in:
@@ -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
|
||||
@@ -0,0 +1,120 @@
|
||||
# language: fr
|
||||
Fonctionnalité: Classification des contenus par âge
|
||||
En tant que plateforme responsable
|
||||
Je veux classifier les contenus par tranche d'âge
|
||||
Afin de protéger les mineurs et respecter les obligations légales
|
||||
|
||||
Contexte:
|
||||
Étant donné que l'API RoadWave est disponible
|
||||
|
||||
Scénario: Créateur doit classifier son contenu à la publication
|
||||
Étant donné que je suis un créateur connecté
|
||||
Quand je crée un nouveau contenu audio
|
||||
Alors je dois obligatoirement choisir une classification d'âge parmi:
|
||||
| classification | description |
|
||||
| Tout public | Contenu adapté à tous les âges |
|
||||
| 13+ | Contenu mature léger |
|
||||
| 16+ | Contenu mature |
|
||||
| 18+ | Contenu adulte |
|
||||
|
||||
Scénario: Publication impossible sans classification
|
||||
Étant donné que je crée un contenu audio
|
||||
Quand j'essaie de publier sans sélectionner de classification
|
||||
Alors la publication échoue
|
||||
Et je vois le message "Vous devez sélectionner une classification d'âge"
|
||||
|
||||
Scénario: Utilisateur 13-15 ans voit "Tout public" et "13+"
|
||||
Étant donné que je suis un utilisateur de 14 ans
|
||||
Et qu'il existe des contenus avec les classifications suivantes:
|
||||
| classification | nombre |
|
||||
| Tout public | 20 |
|
||||
| 13+ | 15 |
|
||||
| 16+ | 10 |
|
||||
| 18+ | 5 |
|
||||
Quand je demande des recommandations
|
||||
Alors je vois 35 contenus (Tout public + 13+)
|
||||
Et les contenus 16+ et 18+ ne sont jamais proposés
|
||||
|
||||
Scénario: Utilisateur 16-17 ans voit "Tout public", "13+" et "16+"
|
||||
Étant donné que je suis un utilisateur de 17 ans
|
||||
Et qu'il existe des contenus avec les classifications suivantes:
|
||||
| classification | nombre |
|
||||
| Tout public | 20 |
|
||||
| 13+ | 15 |
|
||||
| 16+ | 10 |
|
||||
| 18+ | 5 |
|
||||
Quand je demande des recommandations
|
||||
Alors je vois 45 contenus (Tout public + 13+ + 16+)
|
||||
Et les contenus 18+ ne sont pas proposés
|
||||
|
||||
Scénario: Utilisateur 18+ voit tous les contenus
|
||||
Étant donné que je suis un utilisateur de 25 ans
|
||||
Et qu'il existe des contenus avec toutes les classifications
|
||||
Quand je demande des recommandations
|
||||
Alors je vois tous les contenus sans restriction
|
||||
Et aucun filtre d'âge n'est appliqué
|
||||
|
||||
Scénario: Inscription réussie à 13 ans pile - accès limité à "Tout public" et "13+"
|
||||
Étant donné que je m'inscris avec une date de naissance "2013-01-21"
|
||||
Alors mon compte est créé avec succès
|
||||
Et je peux voir les contenus "Tout public" et "13+"
|
||||
Et les contenus 16+ et 18+ ne sont pas accessibles
|
||||
|
||||
Scénario: Modérateur reclassifie un contenu mal catégorisé
|
||||
Étant donné qu'un contenu est publié avec la classification "Tout public"
|
||||
Et que ce contenu contient du langage inapproprié détecté en modération
|
||||
Quand le modérateur reclassifie ce contenu en "16+"
|
||||
Alors la nouvelle classification est appliquée immédiatement
|
||||
Et le contenu n'est plus visible pour les utilisateurs de moins de 16 ans
|
||||
Et le créateur reçoit une notification de reclassification
|
||||
|
||||
Scénario: Strike si classification volontairement incorrecte
|
||||
Étant donné qu'un créateur a publié un contenu "18+" classifié comme "Tout public"
|
||||
Et que ce contenu a été signalé
|
||||
Quand le modérateur confirme la mauvaise classification volontaire
|
||||
Alors le créateur reçoit 1 strike
|
||||
Et le contenu est reclassifié en "18+"
|
||||
Et le créateur reçoit une notification explicative
|
||||
|
||||
Scénario: Créateur peut voir la distribution d'âge de son audience
|
||||
Étant donné que je suis un créateur
|
||||
Et que j'ai publié des contenus avec différentes classifications
|
||||
Quand je consulte mes statistiques
|
||||
Alors je vois la répartition des âges de mes auditeurs:
|
||||
| tranche_age | pourcentage |
|
||||
| 13-15 ans | 15% |
|
||||
| 16-17 ans | 20% |
|
||||
| 18+ ans | 65% |
|
||||
|
||||
Scénario: Recherche filtrée par classification d'âge
|
||||
Étant donné que je suis un utilisateur de 16 ans
|
||||
Quand je recherche des contenus
|
||||
Alors les résultats incluent uniquement:
|
||||
| classification |
|
||||
| Tout public |
|
||||
| 13+ |
|
||||
| 16+ |
|
||||
Et je ne vois pas les contenus 18+ dans les résultats
|
||||
|
||||
Scénario: Notification si tentative d'accès à contenu non autorisé
|
||||
Étant donné que je suis un utilisateur de 15 ans
|
||||
Et qu'un contenu "16+" est partagé avec moi via un lien direct
|
||||
Quand j'essaie d'accéder au contenu
|
||||
Alors l'accès est refusé
|
||||
Et je vois le message "Ce contenu est réservé aux utilisateurs de 16 ans et plus"
|
||||
|
||||
Scénario: Validation obligatoire des 3 premiers contenus inclut la classification
|
||||
Étant donné que je suis un nouveau créateur
|
||||
Et que je publie mon premier contenu classifié "18+"
|
||||
Quand le modérateur valide mon contenu
|
||||
Alors il vérifie que la classification "18+" est appropriée
|
||||
Et peut la modifier si nécessaire avant validation
|
||||
|
||||
Scénario: Statistiques de classification dans l'interface créateur
|
||||
Étant donné que je suis un créateur
|
||||
Quand je consulte mes contenus publiés
|
||||
Alors je vois pour chaque contenu:
|
||||
| information | exemple |
|
||||
| Classification actuelle | 13+ |
|
||||
| Nombre de signalements | 2 |
|
||||
| Reclassifications | Aucune / 1× par modérateur |
|
||||
@@ -0,0 +1,84 @@
|
||||
# language: fr
|
||||
Fonctionnalité: Connexion utilisateur
|
||||
En tant qu'utilisateur existant
|
||||
Je veux me connecter à mon compte
|
||||
Afin d'accéder à mes contenus et paramètres
|
||||
|
||||
Contexte:
|
||||
Étant donné que l'API RoadWave est disponible
|
||||
Et qu'un utilisateur existe avec:
|
||||
| email | mot_de_passe |
|
||||
| user@test.fr | Password123 |
|
||||
|
||||
Scénario: Connexion réussie avec identifiants valides
|
||||
Quand je me connecte avec:
|
||||
| email | mot_de_passe |
|
||||
| user@test.fr | Password123 |
|
||||
Alors je suis connecté avec succès
|
||||
Et je reçois un access token valide pour 15 minutes
|
||||
Et je reçois un refresh token valide pour 30 jours
|
||||
|
||||
Scénario: Connexion échouée avec email inexistant
|
||||
Quand je me connecte avec l'email "inexistant@test.fr"
|
||||
Alors la connexion échoue
|
||||
Et je vois le message "Email ou mot de passe incorrect"
|
||||
|
||||
Scénario: Connexion échouée avec mot de passe incorrect
|
||||
Quand je me connecte avec:
|
||||
| email | mot_de_passe |
|
||||
| user@test.fr | MauvaisPass1 |
|
||||
Alors la connexion échoue
|
||||
Et je vois le message "Email ou mot de passe incorrect"
|
||||
|
||||
Scénario: Blocage après 5 tentatives échouées
|
||||
Étant donné que j'ai échoué 4 tentatives de connexion
|
||||
Quand j'échoue une 5ème tentative de connexion
|
||||
Alors mon compte est temporairement bloqué
|
||||
Et je vois le message "Compte bloqué pour 15 minutes après 5 tentatives échouées"
|
||||
Et je reçois un email de notification de blocage
|
||||
|
||||
Scénario: Tentative de connexion pendant le blocage
|
||||
Étant donné que mon compte est bloqué suite à 5 tentatives échouées
|
||||
Et que seulement 5 minutes se sont écoulées
|
||||
Quand j'essaie de me connecter avec les bons identifiants
|
||||
Alors la connexion échoue
|
||||
Et je vois le message "Compte bloqué. Réessayez dans 10 minutes"
|
||||
|
||||
Scénario: Déblocage automatique après 15 minutes
|
||||
Étant donné que mon compte est bloqué suite à 5 tentatives échouées
|
||||
Et que 15 minutes se sont écoulées
|
||||
Quand je me connecte avec les bons identifiants
|
||||
Alors je suis connecté avec succès
|
||||
Et le compteur de tentatives est réinitialisé
|
||||
|
||||
Scénario: Reset du compteur après connexion réussie
|
||||
Étant donné que j'ai échoué 3 tentatives de connexion
|
||||
Quand je me connecte avec les bons identifiants
|
||||
Alors je suis connecté avec succès
|
||||
Et le compteur de tentatives est remis à 0
|
||||
|
||||
Scénario: Reset automatique du compteur après 15 minutes sans blocage
|
||||
Étant donné que j'ai échoué 3 tentatives de connexion
|
||||
Et que 15 minutes se sont écoulées sans nouvelle tentative
|
||||
Quand je consulte mon compteur de tentatives
|
||||
Alors le compteur est réinitialisé à 0
|
||||
|
||||
Scénario: Déblocage via lien "Mot de passe oublié"
|
||||
Étant donné que mon compte est bloqué suite à 5 tentatives échouées
|
||||
Quand j'utilise la fonction "Mot de passe oublié"
|
||||
Et que je réinitialise mon mot de passe
|
||||
Alors le blocage est levé immédiatement
|
||||
Et je peux me connecter avec le nouveau mot de passe
|
||||
|
||||
Scénario: Email de notification lors d'un blocage
|
||||
Étant donné que j'ai échoué 5 tentatives de connexion
|
||||
Alors je reçois un email avec:
|
||||
| sujet | Tentatives de connexion suspectes détectées |
|
||||
| contenu_contient | Votre compte a été temporairement bloqué |
|
||||
| lien_mot_de_passe | présent |
|
||||
|
||||
Scénario: Connexion multi-device simultanée autorisée
|
||||
Étant donné que je suis connecté sur un appareil iOS
|
||||
Quand je me connecte également sur un appareil Android
|
||||
Alors les deux sessions sont actives simultanément
|
||||
Et je peux utiliser l'application sur les deux appareils
|
||||
@@ -0,0 +1,199 @@
|
||||
# language: fr
|
||||
Fonctionnalité: Gestion de compte utilisateur
|
||||
En tant qu'utilisateur connecté
|
||||
Je veux gérer les paramètres de mon compte
|
||||
Afin de maintenir la sécurité et l'exactitude de mes informations
|
||||
|
||||
Contexte:
|
||||
Étant donné que l'API RoadWave est disponible
|
||||
Et que je suis connecté avec:
|
||||
| email | user@test.fr |
|
||||
| mot_de_passe | Password123 |
|
||||
|
||||
# ==========================================
|
||||
# Déconnexion
|
||||
# ==========================================
|
||||
|
||||
Scénario: Déconnexion volontaire de l'appareil actuel
|
||||
Quand je clique sur "Se déconnecter"
|
||||
Alors ma session est invalidée immédiatement
|
||||
Et mon refresh token est révoqué
|
||||
Et je suis redirigé vers l'écran de connexion
|
||||
Et je dois me reconnecter pour accéder à l'application
|
||||
|
||||
Scénario: Déconnexion ne révoque pas les autres appareils
|
||||
Étant donné que je suis connecté sur mon iPhone et mon iPad
|
||||
Quand je me déconnecte depuis mon iPhone
|
||||
Alors la session iPhone est invalidée
|
||||
Et ma session iPad reste active
|
||||
Et je peux continuer à utiliser l'application sur iPad
|
||||
|
||||
# ==========================================
|
||||
# Changement de mot de passe
|
||||
# ==========================================
|
||||
|
||||
Scénario: Changement de mot de passe avec ancien mot de passe correct
|
||||
Quand je change mon mot de passe depuis les paramètres avec:
|
||||
| ancien_mot_de_passe | Password123 |
|
||||
| nouveau_mot_de_passe | NewPass456 |
|
||||
| confirmation | NewPass456 |
|
||||
Alors mon mot de passe est modifié avec succès
|
||||
Et je reste connecté sur cet appareil
|
||||
Et tous les autres appareils sont déconnectés
|
||||
Et je reçois un email de confirmation de changement
|
||||
Et je vois le message "Mot de passe modifié avec succès"
|
||||
|
||||
Scénario: Changement de mot de passe avec ancien mot de passe incorrect
|
||||
Quand je change mon mot de passe avec un ancien mot de passe incorrect "WrongPass123"
|
||||
Alors le changement échoue
|
||||
Et je vois le message "Ancien mot de passe incorrect"
|
||||
Et mon mot de passe actuel reste inchangé
|
||||
|
||||
Scénario: Changement de mot de passe avec nouveau mot de passe invalide
|
||||
Quand je change mon mot de passe avec un nouveau mot de passe "faible"
|
||||
Alors le changement échoue
|
||||
Et je vois le message "Le mot de passe doit contenir au moins 8 caractères, 1 majuscule et 1 chiffre"
|
||||
|
||||
Scénario: Changement de mot de passe avec confirmation non correspondante
|
||||
Quand je change mon mot de passe avec:
|
||||
| ancien_mot_de_passe | Password123 |
|
||||
| nouveau_mot_de_passe | NewPass456 |
|
||||
| confirmation | DiffPass789 |
|
||||
Alors le changement échoue
|
||||
Et je vois le message "Les mots de passe ne correspondent pas"
|
||||
|
||||
Scénario: Nouveau mot de passe identique à l'ancien
|
||||
Quand je change mon mot de passe avec:
|
||||
| ancien_mot_de_passe | Password123 |
|
||||
| nouveau_mot_de_passe | Password123 |
|
||||
| confirmation | Password123 |
|
||||
Alors le changement échoue
|
||||
Et je vois le message "Le nouveau mot de passe doit être différent de l'ancien"
|
||||
|
||||
Scénario: Notification sur tous les appareils après changement de mot de passe
|
||||
Étant donné que je suis connecté sur 3 appareils différents
|
||||
Quand je change mon mot de passe depuis mon iPhone
|
||||
Alors je reçois une notification push sur mes 2 autres appareils
|
||||
Et je reçois un email de confirmation avec:
|
||||
| sujet | Votre mot de passe a été modifié |
|
||||
| appareil | iPhone 13 - Safari |
|
||||
| localisation | Paris, France |
|
||||
| action_urgence | Lien pour sécuriser le compte |
|
||||
|
||||
# ==========================================
|
||||
# Changement d'email
|
||||
# ==========================================
|
||||
|
||||
Scénario: Changement d'email avec vérification
|
||||
Quand je change mon email pour "nouveau@test.fr"
|
||||
Alors un email de vérification est envoyé à "nouveau@test.fr"
|
||||
Et mon ancien email "user@test.fr" reste actif pour la connexion
|
||||
Et je vois le message "Email de vérification envoyé à nouveau@test.fr"
|
||||
Et le lien de vérification expire dans 7 jours
|
||||
|
||||
Scénario: Validation du changement d'email
|
||||
Étant donné que j'ai demandé un changement d'email pour "nouveau@test.fr"
|
||||
Et que j'ai reçu le lien de vérification
|
||||
Quand je clique sur le lien de vérification dans l'email
|
||||
Alors mon email est changé pour "nouveau@test.fr"
|
||||
Et je reçois une notification sur l'ancien email "user@test.fr"
|
||||
Et je vois le message "Email modifié avec succès"
|
||||
Et je dois utiliser "nouveau@test.fr" pour me connecter désormais
|
||||
|
||||
Scénario: Changement d'email vers un email déjà utilisé
|
||||
Étant donné qu'un utilisateur existe avec l'email "existant@test.fr"
|
||||
Quand j'essaie de changer mon email pour "existant@test.fr"
|
||||
Alors le changement échoue
|
||||
Et je vois le message "Cet email est déjà utilisé par un autre compte"
|
||||
|
||||
Scénario: Changement d'email avec format invalide
|
||||
Quand j'essaie de changer mon email pour "email.invalide"
|
||||
Alors le changement échoue
|
||||
Et je vois le message "Format d'email invalide"
|
||||
|
||||
Scénario: Expiration du lien de vérification de changement d'email
|
||||
Étant donné que j'ai demandé un changement d'email il y a 8 jours
|
||||
Quand j'essaie d'utiliser le lien de vérification
|
||||
Alors la vérification échoue
|
||||
Et je vois le message "Ce lien a expiré"
|
||||
Et mon email reste inchangé à "user@test.fr"
|
||||
Et je peux demander un nouveau changement d'email
|
||||
|
||||
Scénario: Annulation du changement d'email avant vérification
|
||||
Étant donné que j'ai demandé un changement d'email pour "nouveau@test.fr"
|
||||
Et que je n'ai pas encore vérifié le nouveau email
|
||||
Quand je demande à annuler le changement d'email
|
||||
Alors la demande de changement est annulée
|
||||
Et le lien de vérification est invalidé
|
||||
Et mon email reste "user@test.fr"
|
||||
|
||||
Scénario: Limite de changements d'email
|
||||
Étant donné que j'ai déjà changé mon email 2 fois dans les 30 derniers jours
|
||||
Quand j'essaie de changer mon email une 3ème fois
|
||||
Alors le changement échoue
|
||||
Et je vois le message "Maximum 2 changements d'email par mois"
|
||||
|
||||
Scénario: Notification de sécurité sur l'ancien email
|
||||
Étant donné que j'ai changé mon email de "ancien@test.fr" à "nouveau@test.fr"
|
||||
Alors je reçois un email sur "ancien@test.fr" avec:
|
||||
| sujet | Votre adresse email a été modifiée |
|
||||
| contenu | Votre email de connexion est maintenant nouveau@test.fr |
|
||||
| date_heure | présente |
|
||||
| appareil | présent |
|
||||
| action_urgence | Lien pour annuler si ce n'était pas vous |
|
||||
|
||||
# ==========================================
|
||||
# Changement de pseudo
|
||||
# ==========================================
|
||||
|
||||
Scénario: Changement de pseudo valide
|
||||
Quand je change mon pseudo pour "nouveau_pseudo"
|
||||
Alors mon pseudo est modifié avec succès
|
||||
Et je vois le message "Pseudo modifié avec succès"
|
||||
Et le nouveau pseudo apparaît sur mon profil
|
||||
|
||||
Scénario: Changement de pseudo invalide - trop court
|
||||
Quand j'essaie de changer mon pseudo pour "ab"
|
||||
Alors le changement échoue
|
||||
Et je vois le message "Le pseudo doit contenir entre 3 et 30 caractères"
|
||||
|
||||
Scénario: Changement de pseudo invalide - caractères spéciaux
|
||||
Quand j'essaie de changer mon pseudo pour "user@123"
|
||||
Alors le changement échoue
|
||||
Et je vois le message "Le pseudo ne peut contenir que des lettres, chiffres et underscores"
|
||||
|
||||
Scénario: Changement de pseudo déjà utilisé
|
||||
Étant donné qu'un utilisateur existe avec le pseudo "pseudo_existant"
|
||||
Quand j'essaie de changer mon pseudo pour "pseudo_existant"
|
||||
Alors le changement échoue
|
||||
Et je vois le message "Ce pseudo est déjà utilisé"
|
||||
|
||||
Scénario: Limite de changements de pseudo
|
||||
Étant donné que j'ai changé mon pseudo il y a 15 jours
|
||||
Quand j'essaie de changer mon pseudo à nouveau
|
||||
Alors le changement échoue
|
||||
Et je vois le message "Vous ne pouvez changer votre pseudo qu'une fois par mois"
|
||||
|
||||
# ==========================================
|
||||
# Consultation des informations de compte
|
||||
# ==========================================
|
||||
|
||||
Scénario: Consulter les informations de mon compte
|
||||
Quand je consulte les paramètres de mon compte
|
||||
Alors je vois les informations suivantes:
|
||||
| champ | valeur |
|
||||
| Email | user@test.fr |
|
||||
| Pseudo | user_test |
|
||||
| Date création | 15/01/2026 |
|
||||
| Email vérifié | Oui |
|
||||
| 2FA activée | Non |
|
||||
| Abonnement | Gratuit |
|
||||
|
||||
Scénario: Historique des changements de sécurité
|
||||
Quand je consulte l'historique de sécurité de mon compte
|
||||
Alors je vois la liste des événements suivants:
|
||||
| événement | date | appareil |
|
||||
| Changement mot de passe | 01/02/2026 | iPhone 13 |
|
||||
| Activation 2FA | 25/01/2026 | iPad Pro |
|
||||
| Changement email | 20/01/2026 | PC Windows |
|
||||
| Création compte | 15/01/2026 | iPhone 13 |
|
||||
112
docs/domains/_shared/features/authentication/inscription.feature
Normal file
112
docs/domains/_shared/features/authentication/inscription.feature
Normal file
@@ -0,0 +1,112 @@
|
||||
# language: fr
|
||||
Fonctionnalité: Inscription utilisateur
|
||||
En tant que nouvel utilisateur
|
||||
Je veux créer un compte avec email et mot de passe
|
||||
Afin d'accéder à l'application RoadWave
|
||||
|
||||
Contexte:
|
||||
Étant donné que l'API RoadWave est disponible
|
||||
Et que Zitadel est configuré
|
||||
|
||||
Scénario: Inscription réussie avec données valides
|
||||
Étant donné que l'email "nouveau@example.com" n'existe pas
|
||||
Quand je m'inscris avec les données suivantes:
|
||||
| champ | valeur |
|
||||
| email | nouveau@example.com |
|
||||
| mot_de_passe | Password123 |
|
||||
| pseudo | nouveau_user |
|
||||
| date_naissance | 1995-06-15 |
|
||||
Alors mon compte est créé avec succès
|
||||
Et je reçois un email de vérification
|
||||
Et le lien de vérification expire dans 7 jours
|
||||
Et je suis redirigé vers l'application
|
||||
|
||||
Scénario: Inscription avec email déjà existant
|
||||
Étant donné qu'un utilisateur existe avec l'email "existant@example.com"
|
||||
Quand je m'inscris avec l'email "existant@example.com"
|
||||
Alors l'inscription échoue
|
||||
Et je vois le message "Cet email est déjà utilisé"
|
||||
|
||||
Scénario: Inscription avec mot de passe invalide - trop court
|
||||
Quand je m'inscris avec un mot de passe de moins de 8 caractères "Pass1"
|
||||
Alors l'inscription échoue
|
||||
Et je vois le message "Le mot de passe doit contenir au moins 8 caractères"
|
||||
|
||||
Scénario: Inscription avec mot de passe invalide - sans majuscule
|
||||
Quand je m'inscris avec un mot de passe sans majuscule "password123"
|
||||
Alors l'inscription échoue
|
||||
Et je vois le message "Le mot de passe doit contenir au moins une majuscule"
|
||||
|
||||
Scénario: Inscription avec mot de passe invalide - sans chiffre
|
||||
Quand je m'inscris avec un mot de passe sans chiffre "Password"
|
||||
Alors l'inscription échoue
|
||||
Et je vois le message "Le mot de passe doit contenir au moins un chiffre"
|
||||
|
||||
Scénario: Inscription avec pseudo invalide - trop court
|
||||
Quand je m'inscris avec un pseudo de 2 caractères "ab"
|
||||
Alors l'inscription échoue
|
||||
Et je vois le message "Le pseudo doit contenir entre 3 et 30 caractères"
|
||||
|
||||
Scénario: Inscription avec pseudo invalide - caractères spéciaux
|
||||
Quand je m'inscris avec un pseudo contenant des caractères spéciaux "user@123"
|
||||
Alors l'inscription échoue
|
||||
Et je vois le message "Le pseudo ne peut contenir que des lettres, chiffres et underscores"
|
||||
|
||||
Scénario: Inscription avec email invalide
|
||||
Quand je m'inscris avec un email invalide "email.invalide"
|
||||
Alors l'inscription échoue
|
||||
Et je vois le message "Format d'email invalide"
|
||||
|
||||
Plan du Scénario: Inscription avec âge minimum non respecté
|
||||
Étant donné la date du jour est "2026-01-21"
|
||||
Quand je m'inscris avec une date de naissance "<date_naissance>"
|
||||
Alors l'inscription échoue
|
||||
Et je vois le message "Vous devez avoir au moins 13 ans pour créer un compte"
|
||||
|
||||
Exemples:
|
||||
| date_naissance | age |
|
||||
| 2013-01-22 | 12 |
|
||||
| 2015-06-15 | 10 |
|
||||
| 2020-01-01 | 6 |
|
||||
|
||||
Scénario: Inscription avec âge limite acceptable (13 ans)
|
||||
Étant donné la date du jour est "2026-01-21"
|
||||
Quand je m'inscris avec une date de naissance "2013-01-21"
|
||||
Alors mon compte est créé avec succès
|
||||
Et je peux accéder aux contenus "Tout public" et "13+"
|
||||
|
||||
Scénario: Inscription avec âge supérieur à 18 ans
|
||||
Étant donné la date du jour est "2026-01-21"
|
||||
Quand je m'inscris avec une date de naissance "1990-06-15"
|
||||
Alors mon compte est créé avec succès
|
||||
Et j'ai accès à tous les contenus sans restriction d'âge
|
||||
|
||||
Scénario: Données minimales requises à l'inscription
|
||||
Quand je m'inscris sans fournir de nom complet
|
||||
Et sans fournir de photo de profil
|
||||
Et sans fournir de bio
|
||||
Alors mon compte est créé avec succès
|
||||
Et un avatar par défaut est généré
|
||||
Et les champs optionnels sont vides
|
||||
|
||||
Scénario: Renvoyer l'email de vérification
|
||||
Étant donné que je me suis inscrit avec l'email "nouveau@example.com"
|
||||
Et que je n'ai pas vérifié mon email
|
||||
Quand je demande à renvoyer l'email de vérification
|
||||
Alors un nouvel email de vérification est envoyé
|
||||
Et le précédent lien est invalidé
|
||||
|
||||
Scénario: Limite de renvoi d'email de vérification
|
||||
Étant donné que je me suis inscrit avec l'email "nouveau@example.com"
|
||||
Et que j'ai déjà renvoyé l'email de vérification 3 fois aujourd'hui
|
||||
Quand je demande à renvoyer l'email de vérification une 4ème fois
|
||||
Alors la demande échoue
|
||||
Et je vois le message "Vous avez atteint la limite de 3 renvois par jour"
|
||||
|
||||
Scénario: Expiration du lien de vérification
|
||||
Étant donné que je me suis inscrit il y a 8 jours
|
||||
Et que je n'ai pas vérifié mon email
|
||||
Quand j'essaie d'utiliser le lien de vérification
|
||||
Alors la vérification échoue
|
||||
Et je vois le message "Ce lien a expiré"
|
||||
Et je peux demander un nouveau lien
|
||||
@@ -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é
|
||||
@@ -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,107 @@
|
||||
# language: fr
|
||||
Fonctionnalité: Récupération de compte
|
||||
En tant qu'utilisateur ayant oublié son mot de passe
|
||||
Je veux pouvoir réinitialiser mon mot de passe via email
|
||||
Afin de récupérer l'accès à mon compte
|
||||
|
||||
Contexte:
|
||||
Étant donné que l'API RoadWave est disponible
|
||||
Et qu'un utilisateur existe avec l'email "user@test.fr"
|
||||
|
||||
Scénario: Demander la réinitialisation du mot de passe
|
||||
Quand je clique sur "Mot de passe oublié"
|
||||
Et que je saisis mon email "user@test.fr"
|
||||
Alors je reçois un email avec un lien de réinitialisation
|
||||
Et le lien expire dans 1 heure
|
||||
Et je vois le message "Email de réinitialisation envoyé"
|
||||
|
||||
Scénario: Email inexistant lors de la demande de réinitialisation
|
||||
Quand je demande une réinitialisation pour l'email "inexistant@test.fr"
|
||||
Alors je vois le même message "Email de réinitialisation envoyé"
|
||||
Mais aucun email n'est envoyé (sécurité - pas d'énumération d'emails)
|
||||
|
||||
Scénario: Réinitialiser le mot de passe avec un lien valide
|
||||
Étant donné que j'ai demandé une réinitialisation de mot de passe
|
||||
Et que j'ai reçu le lien de réinitialisation
|
||||
Quand je clique sur le lien
|
||||
Et que je saisis un nouveau mot de passe "NouveauPass123"
|
||||
Et que je confirme le nouveau mot de passe "NouveauPass123"
|
||||
Alors mon mot de passe est modifié avec succès
|
||||
Et je suis déconnecté de tous mes appareils sauf celui en cours
|
||||
Et je reçois un email de confirmation de changement
|
||||
|
||||
Scénario: Lien de réinitialisation expiré
|
||||
Étant donné que j'ai demandé une réinitialisation il y a 2 heures
|
||||
Quand j'essaie d'utiliser le lien
|
||||
Alors je vois le message "Ce lien a expiré"
|
||||
Et je peux demander un nouveau lien
|
||||
|
||||
Scénario: Nouveau mot de passe ne respecte pas les règles
|
||||
Étant donné que j'ai un lien de réinitialisation valide
|
||||
Quand je saisis un nouveau mot de passe "faible"
|
||||
Alors la réinitialisation échoue
|
||||
Et je vois le message "Le mot de passe doit contenir au moins 8 caractères, 1 majuscule et 1 chiffre"
|
||||
|
||||
Scénario: Confirmation du mot de passe ne correspond pas
|
||||
Étant donné que j'ai un lien de réinitialisation valide
|
||||
Quand je saisis un nouveau mot de passe "NouveauPass123"
|
||||
Et que je confirme avec un mot de passe différent "AutrePass123"
|
||||
Alors la réinitialisation échoue
|
||||
Et je vois le message "Les mots de passe ne correspondent pas"
|
||||
|
||||
Scénario: Limite de demandes de réinitialisation
|
||||
Étant donné que j'ai déjà demandé 3 réinitialisations dans la dernière heure
|
||||
Quand je demande une 4ème réinitialisation
|
||||
Alors la demande échoue
|
||||
Et je vois le message "Maximum 3 demandes par heure. Réessayez plus tard."
|
||||
|
||||
Scénario: Compteur de demandes se réinitialise après 1 heure
|
||||
Étant donné que j'ai demandé 3 réinitialisations
|
||||
Et que 1 heure s'est écoulée
|
||||
Quand je demande une nouvelle réinitialisation
|
||||
Alors la demande réussit
|
||||
Et je reçois un email avec un nouveau lien
|
||||
|
||||
Scénario: Email de notification de changement de mot de passe
|
||||
Étant donné que je viens de réinitialiser mon mot de passe
|
||||
Alors je reçois un email de confirmation avec:
|
||||
| sujet | Votre mot de passe a été modifié |
|
||||
| contenu_contient | Votre mot de passe a été modifié |
|
||||
| date_heure | présente |
|
||||
| appareil | présent |
|
||||
| localisation | présente |
|
||||
| action_urgence | Lien si ce n'était pas vous |
|
||||
|
||||
Scénario: Notification push si changement depuis appareil non reconnu
|
||||
Étant donné que je me suis toujours connecté depuis mon iPhone
|
||||
Et que je réinitialise mon mot de passe depuis un PC Windows
|
||||
Alors je reçois une notification push sur mon iPhone avec:
|
||||
| titre | Mot de passe modifié |
|
||||
| message | Depuis Windows - Paris, France |
|
||||
| action | Sécuriser le compte si ce n'est pas vous |
|
||||
|
||||
Scénario: Déconnexion de tous les appareils après réinitialisation
|
||||
Étant donné que je suis connecté sur 4 appareils différents
|
||||
Et que je réinitialise mon mot de passe depuis un navigateur web
|
||||
Alors les 3 autres appareils sont déconnectés immédiatement
|
||||
Et seule la session du navigateur web reste active
|
||||
Et je vois le message "Vous avez été déconnecté des autres appareils par sécurité"
|
||||
|
||||
Scénario: Lien de réinitialisation invalide si déjà utilisé
|
||||
Étant donné que j'ai réinitialisé mon mot de passe avec un lien
|
||||
Quand j'essaie de réutiliser le même lien
|
||||
Alors je vois le message "Ce lien a déjà été utilisé"
|
||||
Et je peux demander un nouveau lien si nécessaire
|
||||
|
||||
Scénario: Nouveau lien invalide l'ancien
|
||||
Étant donné que j'ai demandé une réinitialisation et reçu un lien
|
||||
Quand je demande une nouvelle réinitialisation
|
||||
Alors l'ancien lien est invalidé
|
||||
Et seul le nouveau lien fonctionne
|
||||
|
||||
Scénario: Réinitialisation débloque un compte bloqué
|
||||
Étant donné que mon compte est bloqué après 5 tentatives de connexion
|
||||
Quand je réinitialise mon mot de passe via email
|
||||
Alors le blocage est levé immédiatement
|
||||
Et je peux me connecter avec le nouveau mot de passe
|
||||
Et le compteur de tentatives est remis à 0
|
||||
@@ -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
|
||||
@@ -0,0 +1,114 @@
|
||||
# language: fr
|
||||
Fonctionnalité: Gestion des sessions et tokens
|
||||
En tant qu'utilisateur connecté
|
||||
Je veux que mes sessions soient sécurisées et gérées automatiquement
|
||||
Afin de maintenir l'accès à l'application sans friction
|
||||
|
||||
Contexte:
|
||||
Étant donné que l'API RoadWave est disponible
|
||||
Et que je suis connecté avec succès
|
||||
|
||||
Scénario: Access token expire après 15 minutes
|
||||
Étant donné que j'ai reçu un access token
|
||||
Et que 15 minutes se sont écoulées
|
||||
Quand je fais une requête API avec cet access token
|
||||
Alors la requête échoue avec le code 401
|
||||
Et je vois le message "Token expiré"
|
||||
|
||||
Scénario: Refresh automatique du token avec refresh token
|
||||
Étant donné que mon access token a expiré
|
||||
Et que mon refresh token est valide
|
||||
Quand l'application demande un nouveau access token
|
||||
Alors je reçois un nouvel access token valide pour 15 minutes
|
||||
Et je reçois un nouveau refresh token (rotation)
|
||||
Et l'ancien refresh token est invalidé
|
||||
|
||||
Scénario: Refresh token expire après 30 jours d'inactivité
|
||||
Étant donné que je me suis connecté il y a 30 jours
|
||||
Et que je n'ai pas utilisé l'application depuis
|
||||
Quand j'essaie d'utiliser mon refresh token
|
||||
Alors la requête échoue
|
||||
Et je dois me reconnecter avec email/password
|
||||
|
||||
Scénario: Prolongation automatique de la session si l'app est utilisée
|
||||
Étant donné que je me suis connecté il y a 25 jours
|
||||
Et que j'utilise l'application régulièrement
|
||||
Quand je fais une requête API
|
||||
Alors ma session est automatiquement prolongée
|
||||
Et mon refresh token reste valide
|
||||
|
||||
Scénario: Détection de token replay attack
|
||||
Étant donné que j'ai rafraîchi mon token
|
||||
Et que j'ai reçu un nouveau refresh token
|
||||
Quand j'essaie de réutiliser l'ancien refresh token
|
||||
Alors la requête échoue
|
||||
Et je vois le message "Token invalide ou révoqué"
|
||||
Et toutes mes sessions sont révoquées par sécurité
|
||||
|
||||
Scénario: Voir la liste des appareils connectés
|
||||
Étant donné que je suis connecté sur 3 appareils différents
|
||||
Quand je consulte la liste de mes appareils connectés
|
||||
Alors je vois 3 appareils avec les informations suivantes:
|
||||
| information | exemple |
|
||||
| OS | iOS 17.1 |
|
||||
| Navigateur | Safari |
|
||||
| Dernière connexion | Il y a 2 heures |
|
||||
| Localisation | Paris, France (IP visible) |
|
||||
|
||||
Scénario: Révoquer un appareil spécifique
|
||||
Étant donné que je suis connecté sur mon iPhone et mon iPad
|
||||
Quand je révoque la session de mon iPad depuis les paramètres
|
||||
Alors la session iPad est immédiatement déconnectée
|
||||
Et ma session iPhone reste active
|
||||
|
||||
Scénario: Déconnecter tous les appareils sauf celui en cours
|
||||
Étant donné que je suis connecté sur 4 appareils
|
||||
Quand je clique sur "Déconnecter tous les appareils"
|
||||
Alors les 3 autres appareils sont déconnectés
|
||||
Et seul l'appareil actuel reste connecté
|
||||
|
||||
Scénario: Alerte de connexion depuis nouveau device
|
||||
Étant donné que je me suis toujours connecté depuis Paris
|
||||
Quand je me connecte depuis un nouvel appareil à Lyon
|
||||
Alors je reçois une notification push sur mes autres appareils
|
||||
Et je reçois un email avec:
|
||||
| sujet | Nouvelle connexion détectée |
|
||||
| localisation | Lyon, France |
|
||||
| appareil | Android 14 - Chrome |
|
||||
| action | Lien pour révoquer la session |
|
||||
|
||||
Scénario: Alerte de connexion suspecte depuis pays différent
|
||||
Étant donné que je me suis toujours connecté depuis la France
|
||||
Quand je me connecte depuis un appareil aux États-Unis
|
||||
Alors je reçois une notification push immédiate
|
||||
Et je reçois un email d'alerte de sécurité
|
||||
Et la nouvelle session nécessite une validation 2FA même si désactivée
|
||||
|
||||
Scénario: Déconnexion après 30 jours d'inactivité totale
|
||||
Étant donné que je ne me suis pas connecté depuis 30 jours
|
||||
Quand j'ouvre l'application
|
||||
Alors je suis automatiquement déconnecté
|
||||
Et je dois me reconnecter avec email/password
|
||||
Et je vois le message "Session expirée après 30 jours d'inactivité"
|
||||
|
||||
Scénario: Sessions multiples simultanées autorisées
|
||||
Étant donné que je suis connecté sur:
|
||||
| appareil |
|
||||
| iPhone |
|
||||
| iPad |
|
||||
| PC Windows (Web) |
|
||||
Quand je fais des actions sur les 3 appareils simultanément
|
||||
Alors toutes les sessions fonctionnent sans conflit
|
||||
Et chaque appareil maintient sa propre session
|
||||
|
||||
Scénario: Validation de JWT via Zitadel
|
||||
Étant donné que j'ai reçu un access token JWT
|
||||
Quand l'API RoadWave valide le token
|
||||
Alors la validation est faite localement avec la clé publique Zitadel
|
||||
Et aucune requête externe n'est effectuée (performance)
|
||||
Et le token contient les claims suivants:
|
||||
| claim | valeur_exemple |
|
||||
| sub | user-id-123 |
|
||||
| email | user@test.fr |
|
||||
| exp | timestamp + 15 minutes |
|
||||
| iss | zitadel.roadwave.com |
|
||||
@@ -0,0 +1,132 @@
|
||||
# language: fr
|
||||
Fonctionnalité: Authentification à deux facteurs (2FA)
|
||||
En tant qu'utilisateur soucieux de sécurité
|
||||
Je veux activer la 2FA sur mon compte
|
||||
Afin de protéger mon accès même si mon mot de passe est compromis
|
||||
|
||||
Contexte:
|
||||
Étant donné que l'API RoadWave est disponible
|
||||
Et que je suis connecté à mon compte
|
||||
|
||||
Scénario: Activer la 2FA TOTP (Time-based One-Time Password)
|
||||
Étant donné que la 2FA n'est pas activée sur mon compte
|
||||
Quand je choisis d'activer la 2FA TOTP
|
||||
Alors je vois un QR code à scanner
|
||||
Et je vois le secret partagé en texte clair (backup)
|
||||
Et je dois entrer un code de vérification depuis mon app authenticator
|
||||
Quand je saisis un code TOTP valide
|
||||
Alors la 2FA TOTP est activée avec succès
|
||||
Et je reçois des codes de backup (10 codes)
|
||||
|
||||
Scénario: Connexion avec 2FA TOTP activée
|
||||
Étant donné que la 2FA TOTP est activée sur mon compte
|
||||
Quand je me connecte avec email/password
|
||||
Alors je suis redirigé vers la page de saisie du code 2FA
|
||||
Quand je saisis un code TOTP valide de mon authenticator
|
||||
Alors je suis connecté avec succès
|
||||
|
||||
Scénario: Connexion échouée avec code TOTP invalide
|
||||
Étant donné que la 2FA TOTP est activée
|
||||
Quand je me connecte avec email/password
|
||||
Et que je saisis un code TOTP invalide "000000"
|
||||
Alors la connexion échoue
|
||||
Et je vois le message "Code d'authentification invalide"
|
||||
Et je peux réessayer
|
||||
|
||||
Scénario: Utiliser un code de backup pour 2FA
|
||||
Étant donné que la 2FA TOTP est activée
|
||||
Et que j'ai perdu l'accès à mon authenticator
|
||||
Quand je me connecte avec email/password
|
||||
Et que je clique sur "Utiliser un code de backup"
|
||||
Et que je saisis un code de backup valide
|
||||
Alors je suis connecté avec succès
|
||||
Et le code de backup utilisé est invalidé
|
||||
Et il me reste 9 codes de backup
|
||||
|
||||
Scénario: Activer la 2FA par email
|
||||
Étant donné que la 2FA n'est pas activée
|
||||
Quand je choisis d'activer la 2FA par email
|
||||
Alors la 2FA email est activée immédiatement
|
||||
Et je vois le message "2FA email activée. Vous recevrez un code à chaque connexion"
|
||||
|
||||
Scénario: Connexion avec 2FA email
|
||||
Étant donné que la 2FA email est activée
|
||||
Quand je me connecte avec email/password
|
||||
Alors je reçois un email avec un code à 6 chiffres
|
||||
Et le code expire dans 10 minutes
|
||||
Et je dois saisir ce code pour terminer la connexion
|
||||
|
||||
Scénario: Code 2FA email expiré
|
||||
Étant donné que la 2FA email est activée
|
||||
Et que je me suis connecté avec email/password
|
||||
Et que j'ai reçu un code 2FA par email il y a 11 minutes
|
||||
Quand je saisis ce code
|
||||
Alors la connexion échoue
|
||||
Et je vois le message "Code expiré. Demandez un nouveau code."
|
||||
|
||||
Scénario: Renvoyer le code 2FA email
|
||||
Étant donné que la 2FA email est activée
|
||||
Et que je suis sur la page de saisie du code 2FA
|
||||
Quand je clique sur "Renvoyer le code"
|
||||
Alors je reçois un nouveau code par email
|
||||
Et l'ancien code est invalidé
|
||||
|
||||
Scénario: Ajouter un appareil de confiance (skip 2FA pendant 30 jours)
|
||||
Étant donné que la 2FA TOTP est activée
|
||||
Quand je me connecte avec email/password et code TOTP
|
||||
Et que je coche "Ne plus demander sur cet appareil"
|
||||
Alors je suis connecté avec succès
|
||||
Et cet appareil est enregistré comme "appareil de confiance"
|
||||
Quand je me reconnecte dans les 30 jours suivants sur ce même appareil
|
||||
Alors je ne dois pas saisir de code 2FA
|
||||
|
||||
Scénario: Appareil de confiance expire après 30 jours
|
||||
Étant donné que j'ai enregistré un appareil de confiance il y a 31 jours
|
||||
Quand je me connecte depuis cet appareil
|
||||
Alors je dois saisir un code 2FA
|
||||
Et je vois le message "Appareil de confiance expiré. Veuillez vous authentifier"
|
||||
|
||||
Scénario: Voir la liste des appareils de confiance
|
||||
Étant donné que j'ai enregistré 3 appareils de confiance
|
||||
Quand je consulte mes paramètres de sécurité
|
||||
Alors je vois la liste de mes 3 appareils de confiance avec:
|
||||
| information | exemple |
|
||||
| Nom | iPhone 13 - Safari |
|
||||
| Date ajout | 15 janvier 2026 |
|
||||
| Dernière vue | Il y a 2 heures |
|
||||
| Expire le | 14 février 2026 |
|
||||
|
||||
Scénario: Révoquer un appareil de confiance
|
||||
Étant donné que j'ai un iPhone enregistré comme appareil de confiance
|
||||
Quand je révoque cet appareil depuis les paramètres
|
||||
Alors l'appareil est supprimé de la liste
|
||||
Quand je me reconnecte depuis cet iPhone
|
||||
Alors je dois saisir un code 2FA
|
||||
|
||||
Scénario: Révoquer tous les appareils de confiance
|
||||
Étant donné que j'ai 5 appareils de confiance enregistrés
|
||||
Quand je clique sur "Révoquer tous les appareils de confiance"
|
||||
Alors tous les appareils sont révoqués
|
||||
Et je vois le message "Tous les appareils de confiance ont été révoqués"
|
||||
|
||||
Scénario: 2FA forcée pour connexion suspecte malgré appareil de confiance
|
||||
Étant donné que j'ai un appareil de confiance enregistré en France
|
||||
Et que je me connecte depuis ce même appareil mais avec une IP américaine
|
||||
Quand je tente de me connecter
|
||||
Alors la 2FA est requise malgré l'appareil de confiance
|
||||
Et je vois le message "Connexion suspecte détectée. Authentification requise."
|
||||
|
||||
Scénario: Désactiver la 2FA
|
||||
Étant donné que la 2FA TOTP est activée
|
||||
Quand je désactive la 2FA depuis mes paramètres
|
||||
Et que je confirme avec mon mot de passe
|
||||
Alors la 2FA est désactivée
|
||||
Et tous les codes de backup sont invalidés
|
||||
Et tous les appareils de confiance sont révoqués
|
||||
|
||||
Scénario: Régénérer les codes de backup
|
||||
Étant donné que la 2FA est activée
|
||||
Et que j'ai utilisé 8 codes de backup sur 10
|
||||
Quand je demande à régénérer les codes de backup
|
||||
Alors je reçois 10 nouveaux codes
|
||||
Et tous les anciens codes (utilisés ou non) sont invalidés
|
||||
@@ -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
|
||||
@@ -0,0 +1,79 @@
|
||||
# language: fr
|
||||
Fonctionnalité: Vérification d'email
|
||||
En tant qu'utilisateur inscrit
|
||||
Je veux vérifier mon adresse email
|
||||
Afin d'accéder à toutes les fonctionnalités selon mon rôle
|
||||
|
||||
Contexte:
|
||||
Étant donné que l'API RoadWave est disponible
|
||||
|
||||
Scénario: Auditeur avec email non vérifié - lecture illimitée
|
||||
Étant donné que je suis un auditeur avec email non vérifié
|
||||
Quand j'essaie d'écouter du contenu
|
||||
Alors je peux écouter tous les contenus sans limite
|
||||
|
||||
Scénario: Auditeur avec email non vérifié - création limitée à 5 contenus
|
||||
Étant donné que je suis un auditeur avec email non vérifié
|
||||
Et que j'ai créé 4 contenus
|
||||
Quand je crée un 5ème contenu
|
||||
Alors le contenu est créé avec succès
|
||||
Mais quand j'essaie de créer un 6ème contenu
|
||||
Alors la création échoue
|
||||
Et je vois le message "Vérifiez votre email pour créer plus de contenus"
|
||||
|
||||
Scénario: Rappel de vérification après le 3ème contenu créé
|
||||
Étant donné que je suis un auditeur avec email non vérifié
|
||||
Et que j'ai créé 2 contenus
|
||||
Quand je crée mon 3ème contenu
|
||||
Alors le contenu est créé avec succès
|
||||
Et je vois une notification in-app "Vérifiez votre email pour débloquer la création illimitée"
|
||||
|
||||
Scénario: Auditeur vérifie son email
|
||||
Étant donné que je suis un auditeur avec email non vérifié
|
||||
Et que j'ai reçu un lien de vérification
|
||||
Quand je clique sur le lien de vérification dans l'email
|
||||
Alors mon email est marqué comme vérifié
|
||||
Et je vois le message "Email vérifié avec succès"
|
||||
Et toutes les fonctionnalités sont débloquées
|
||||
|
||||
Scénario: Créateur doit vérifier son email sous 7 jours pour monétisation
|
||||
Étant donné que je suis inscrit comme créateur
|
||||
Et que mon email n'est pas vérifié
|
||||
Et que je remplis les conditions de monétisation
|
||||
Quand j'essaie d'accéder au programme de monétisation
|
||||
Alors l'accès est refusé
|
||||
Et je vois le message "Vérifiez votre email pour accéder à la monétisation"
|
||||
|
||||
Scénario: Créateur ne peut pas publier de contenus illimités sans vérification
|
||||
Étant donné que je suis un créateur avec email non vérifié
|
||||
Et que j'ai créé 5 contenus
|
||||
Quand j'essaie de créer un 6ème contenu
|
||||
Alors la création échoue
|
||||
Et je vois le message "Vérifiez votre email pour publier des contenus illimités"
|
||||
|
||||
Scénario: Créateur vérifie son email et déboque tout
|
||||
Étant donné que je suis un créateur avec email non vérifié
|
||||
Et que j'ai reçu un lien de vérification
|
||||
Quand je clique sur le lien de vérification
|
||||
Alors mon email est marqué comme vérifié
|
||||
Et je peux publier des contenus illimités
|
||||
Et je peux accéder au programme de monétisation si j'en remplis les conditions
|
||||
|
||||
Scénario: KYC impossible sans email vérifié
|
||||
Étant donné que je suis un créateur avec email non vérifié
|
||||
Quand j'essaie de compléter le KYC via Mangopay
|
||||
Alors l'accès au KYC est refusé
|
||||
Et je vois le message "Vérifiez votre email avant de procéder au KYC"
|
||||
|
||||
Scénario: Tentative de vérification avec un lien déjà utilisé
|
||||
Étant donné que j'ai déjà vérifié mon email avec un lien
|
||||
Quand j'essaie de réutiliser le même lien de vérification
|
||||
Alors la vérification échoue
|
||||
Et je vois le message "Ce lien a déjà été utilisé"
|
||||
|
||||
Scénario: Auditeur vérifié peut créer plus de 5 contenus
|
||||
Étant donné que je suis un auditeur avec email vérifié
|
||||
Et que j'ai créé 10 contenus
|
||||
Quand je crée un 11ème contenu
|
||||
Alors le contenu est créé avec succès
|
||||
Et il n'y a pas de limite de création
|
||||
Reference in New Issue
Block a user