Rempli à partir du gabarit Template de prompt de delegation, selon la convention de classement décrite dans README - Convention de classement. Fichiers vault à joindre en pièce jointe avec ce prompt : voir tout en bas de ce document.

Contexte projet — Nifram

Tu travailles sur Nifram, une marketplace multi-acteurs pour le Bénin et la diaspora.

Stack :

  • Backend : Laravel 13.x (PHP 8.4+) — API REST versionnée /api/v1/, monolithe modulaire (app/Domains/<X>/, un domaine = une responsabilité)
  • Base de données : MySQL 8.x / MariaDB 10.6+ LTS
  • Frontend web : Next.js 16.2 (React 19, Server Components par défaut), shadcn/ui, Tailwind 4
  • Mobile : Expo (React Native)
  • Paiement : Moneroo (API HTTP directe, pas de SDK), idempotence obligatoire sur les webhooks
  • Auth : Sanctum (cookie httpOnly web, Bearer SecureStore mobile)

Acteurs : Client · Vendeur · Livreur · Agence de livraison · Ambassadeur · Admin · Super Admin

Règles non négociables :

  • Spécification avant code : ne jamais implémenter un module/écran sans sa spec écrite dans le vault (Modules/ ou Design/Specifications/)
  • Multidevise (XOF par défaut, USD/EUR en affichage) — jamais une devise codée en dur ; montants toujours en entier (centimes), jamais en flottant
  • Multilingue FR/EN — jamais un texte codé en dur côté frontend
  • Couche d’abstraction paiement déjà en place (Moneroo) — ne jamais coupler directement à un autre PSP sans passer par l’interface existante
  • Idempotence obligatoire sur les endpoints de paiement et les webhooks
  • API REST versionnée /api/v1/ — un breaking change devient /api/v2/, jamais une modification d’un endpoint existant
  • Documents KYC chiffrés au repos

Module/écran concerné : Module 01 — Authentification et comptes (volet mot de passe oublié)

Objectif

Permettre à un utilisateur de récupérer l’accès à son compte s’il oublie son mot de passe, par email ou téléphone selon le canal enregistré, vérifié et disponible — sans jamais recréer la faille d’usurpation qu’une vérification de canal est censée fermer.

Périmètre

Inclus dans ta tâche : réinitialisation du mot de passe par email/téléphone avec choix du canal, et la bascule vers la récupération assistée par le support quand aucun canal n’est utilisable. Exclus (traités dans d’autres tickets) : inscription/connexion de base (ticket #1), 2FA (ticket #2), intégration frontend des écrans.

Acteurs concernés

Tout utilisateur ayant un compte (Client, Vendeur, Livreur, Agence, Ambassadeur, Admin).

Règles de gestion clés

  • RFC-028 : la demande de réinitialisation se fait par email ou téléphone, selon le(s) canal(aux) enregistré(s), vérifié(s) et disponible(s) chez l’utilisateur — un canal non vérifié ou un SMS désactivé n’est jamais proposé comme option (recréerait la faille d’usurpation fermée par RFC-027).
  • RFC-028 : un seul canal utilisable → code envoyé directement, aucun choix proposé. Les deux utilisables → l’utilisateur doit choisir : pas d’envoi automatique, réponse choice_required d’abord.
  • RFC-028 — une seule route, pas deux : le frontend rappelle le même endpoint POST /auth/password/forgot avec le paramètre channel renseigné pour finaliser son choix — ne jamais créer un endpoint séparé pour “lister les canaux disponibles” (même patron que Auth0/Cognito).
  • RFC-028 — sécurité : si l’identifiant fourni ne correspond à aucun compte, la réponse doit imiter la confirmation d’envoi normale — ne jamais révéler l’existence ou l’absence d’un compte via ce endpoint, dans aucun cas de figure.
  • RFC-028 — bascule support : si aucun canal vérifié et disponible n’existe (ex. téléphone seul non vérifié, ou SMS désactivé, et aucun email vérifié), le compte sort du libre-service et entre dans le processus de récupération assistée par le support (Module 09). Le mécanisme opérationnel précis de cette prise en charge est volontairement hors périmètre de ce ticket (question ouverte pour une future RFC/spec Support) — ton rôle ici s’arrête à détecter ce cas et à répondre en conséquence (indiquer la bascule), pas à construire le parcours support lui-même.
  • Réutilise la table verification_codes existante (même mécanisme que la vérification email/téléphone à l’inscription) — aucun nouveau champ de données pour ce ticket.
  • Code/lien de réinitialisation à durée limitée, à usage unique.

Dépendances

  • Module 01 lui-même (ticket #1 “Socle authentification” — doit être mergé en premier).
  • Module 09 (Chat et support) pour la bascule vers la récupération assistée (juste la détection du cas et la réponse API — pas le parcours support lui-même).
  • Module 12 (Notifications multicanal) pour l’envoi effectif du code par email/SMS.

Ta tâche

Implémenter le backend de la réinitialisation de mot de passe par email/téléphone, avec choix de canal et bascule support.

Ce qu’on attend comme livrable

Suivre l’ordre standard (Conception/Conventions Laravel Nifram.md §4) :

  1. Migration(s) — a priori aucune nécessaire (réutilisation de verification_codes existante), à confirmer en vérifiant la structure actuelle de cette table.
  2. Modèle Eloquent (si une méthode utilitaire doit être ajoutée, ex. déterminer les canaux utilisables d’un utilisateur).
  3. Service de domaine (logique de détermination du/des canal/canaux utilisables, génération/envoi du code, détection du cas “aucun canal utilisable”).
  4. Form Request (validation du payload — identifiant + channel optionnel).
  5. Contrôleur — endpoints POST /auth/password/forgot et POST /auth/password/reset (payload enrichi, pas de nouvel endpoint — voir contraintes ci-dessous).
  6. Tests Pest (Feature en priorité) couvrant les 4 cas : un seul canal utilisable, deux canaux utilisables (choix requis puis confirmé), aucun canal utilisable (bascule support), identifiant inexistant (réponse générique identique au cas normal).

Contraintes spécifiques à cette tâche

  • Vérifier le nom exact du paramètre channel et la forme exacte de la réponse choice_required dans Conception/05 - Contrats API (REST v1).md (section D1) avant de coder — ne pas deviner un nom de champ.
  • La détermination “canal utilisable” doit être une méthode/fonction unique réutilisable, pas dupliquée entre forgot et un éventuel autre endroit qui en aurait besoin plus tard.
  • Le test de la réponse générique (identifiant inexistant) est un test de sécurité à part entière, pas un cas secondaire — s’assurer qu’aucune différence de timing ou de contenu ne permette de distinguer “compte inexistant” de “compte existant, canal envoyé”.

Ce que tu NE dois PAS faire

  • Ne pas créer d’endpoint séparé pour lister ou choisir un canal — un seul endpoint, POST /auth/password/forgot, rappelé avec channel.
  • Ne pas construire le parcours de récupération assistée par le support lui-même (hors périmètre, question ouverte) — seulement détecter le cas et répondre en conséquence.
  • Ne pas implémenter la 2FA (ticket séparé, RFC-026/029).
  • Ne pas intégrer les écrans frontend (ticket séparé).

Format de réponse attendu

Plan d’implémentation d’abord (liste des fichiers à créer/modifier), puis le code complet de chaque fichier, dans l’ordre du cycle de réalisation ci-dessus.


Fichiers vault à joindre tels quels (pièce jointe, en plus de ce prompt)

  • Modules/Module 01 - Authentification et comptes.md
  • RFC/RFC-028 - Reinitialisation du mot de passe par telephone et cas SMS desactive.md
  • Design/Specifications/Auth/Mot de passe oublie.md
  • Conception/05 - Contrats API (REST v1).md (au moins la section D1 — Identité & Accès)

Liens