TESTING.md
Rama master · réplica de lectura del repositorio original.
# TESTING — Authentification avancée
Plan de tests manuels pour les passkeys, la vérification email, la 2FA et
l'approbation de connexion multi-appareils. À dérouler après déploiement (ou en
local avec une base de test).
## ⚙️ Configuration requise (Vercel + local)
Les variables suivantes doivent exister (cf. [`.env.example`](.env.example)) :
| Variable | Requis | Notes |
|---|---|---|
| `AUTH_RESEND_KEY` | ✅ | Clé API Resend (déjà présente). Sans elle, aucun email n'est envoyé. |
| `EMAIL_FROM` | ✅ | Expéditeur, domaine vérifié sur Resend. |
| `RP_ID` | ⬜ | `drivecord.vercel.app` en prod, `localhost` en dev. **Fallback auto** sur l'hôte de la requête. |
| `RP_NAME` | ⬜ | `Drivecord` (défaut). |
| `WEBAUTHN_ORIGIN` | ⬜ | `https://drivecord.vercel.app` en prod. **Fallback auto** sur l'origine. |
| `TOTP_ISSUER` | ⬜ | `Drivecord` (défaut), affiché dans l'app d'authentification. |
> ⚠️ Pour les **passkeys sur l'app iOS** (WebView chargeant `drivecord.vercel.app`),
> définis explicitement `RP_ID=drivecord.vercel.app` et
> `WEBAUTHN_ORIGIN=https://drivecord.vercel.app` sur Vercel — le fallback host
> peut différer dans certaines WebViews.
La migration `20260626131000_add_advanced_auth` est **déjà appliquée** sur la base
(additive, sans perte). Aucune action DB supplémentaire.
## 1. Vérification email
- [ ] **Inscription email + mot de passe** : créer un compte → redirigé vers
`/auth/challenge` → un code à 6 chiffres arrive par email → saisir → accès au drive.
- [ ] **Inscription Google / Discord (1re fois)** : la 1re connexion déclenche un
code email (règle « 1re connexion / 24h ») → vérifier → accès.
- [ ] **Renvoi** : bouton « Renvoyer le code » grisé 60 s (cooldown).
- [ ] **Erreurs** : code faux → message ; 5 essais → blocage ; code expiré (10 min) → message.
- [ ] **Règle 24h** : se reconnecter dans les 24 h → pas de code. Forcer `lastLoginAt`
à > 24 h en base → un code est de nouveau exigé.
## 2. Passkeys (WebAuthn)
- [ ] **Ajouter** : Réglages → Sécurité → Passkeys → « Ajouter » → Face/Touch ID →
le passkey apparaît dans la liste.
- [ ] **Renommer** (inline) et **supprimer** (avec confirmation).
- [ ] **Garde du dernier facteur** : sur un compte sans mot de passe ni OAuth, la
suppression du dernier passkey est refusée.
- [ ] **Connexion** : `/login` → « Se connecter avec un passkey » → sélection →
session ouverte.
- [ ] **Anti-rejeu** : le `counter` est mis à jour (pas d'erreur à la 2e connexion).
## 3. 2FA (TOTP + email + recovery)
- [ ] **Activer TOTP** : Réglages → Sécurité → « Configurer » → scanner le QR →
saisir le code → 2FA activée + **10 codes de récupération affichés une fois**.
- [ ] **Login TOTP** : déconnexion/reconnexion → écran 2FA → code de l'app → accès.
Tolérance ±1 fenêtre (30 s).
- [ ] **Login recovery** : « Utiliser un code de récupération » → un code → accès →
le code est consommé (réutilisation refusée).
- [ ] **2FA email** : activer la méthode email → au login, un code arrive par email.
- [ ] **Régénérer** les codes (invalide les anciens).
- [ ] **Désactiver** : exige un code 2FA ou de récupération.
## 4. Approbation multi-appareils
- [ ] **Appareil A** (déjà connecté, full) reste sur l'app. **Appareil B** (ou autre
navigateur) se connecte → écran challenge → « Approuver depuis un autre appareil ».
- [ ] B affiche un **code à 4 chiffres** et « En attente… ». A reçoit une **fenêtre**
d'approbation avec le **même code**, l'appareil et la localisation (best-effort).
- [ ] **Approuver** sur A → B ouvre la session (≤ ~3 s de polling).
- [ ] **Refuser** sur A → B affiche « Connexion refusée ».
- [ ] **Expiration** : ne rien faire 2 min → B affiche « expiré » ; fallback code email.
- [ ] **Sans appareil de confiance** : l'option n'apparaît pas (seul le code email).
- [ ] **Révocation** : Réglages → Appareils de confiance → révoquer un appareil.
## 5. E2EE non cassé (critique)
Après **chaque** nouveau moyen de login (passkey, OAuth, cross-device, 2FA) :
- [ ] Ouvrir un drive, **uploader** un fichier, le **télécharger** → contenu déchiffré OK.
- [ ] Le **coffre-fort** (PIN) se déverrouille et déchiffre normalement.
> La clé de chiffrement par drive est récupérée du serveur après authentification
> (modèle inchangé), donc indépendante du facteur de login : aucun moyen de
> connexion ne casse le déchiffrement.
## 6. Compte Cord (OIDC)
Variables : `AUTH_CORD_ISSUER`, `AUTH_CORD_ID`, `AUTH_CORD_SECRET`,
`NEXT_PUBLIC_CORD_ACCOUNT_URL` (sans elles, le bouton et la carte Réglages sont masqués).
En local, lancer `cordlauncher/account-service` (`CORD_ISSUER=http://localhost:4319`,
`CORD_CLIENTS` avec le retour `http://localhost:3000/api/auth/callback/cord`).
- [ ] **1re connexion Cord** : `/login` → « Continuer avec Cord » → consentement →
compte créé (nom + photo Cord, email déjà vérifié) → code de connexion (règle 24 h) → drive.
- [ ] **Déjà consenti** : reconnexion sans écran de consentement, même compte (pas de doublon).
- [ ] **Refus sur Cord** → `/login` : « Connexion avec Cord annulée ».
- [ ] **Email déjà utilisé par un compte Drivecord** → `/login` : « Cet email a déjà un
compte Drivecord… associe Cord dans Réglages ». Aucune association silencieuse.
- [ ] **Association** : connecté (session complète) → Réglages › Compte Cord › Associer →
retour `/settings` « Compte Cord associé ». Nom manuel conservé (proposition « Utiliser ce
nom »), avatar rempli seulement s'il était vide.
- [ ] **Compte Cord déjà associé à un autre compte** → message dans la carte Cord.
- [ ] **Dissocier** : possible seulement s'il reste mot de passe / passkey / Google / Discord.
- [ ] **App iOS** : « Continuer avec Cord » ouvre Safari → retour `drivecord://auth` → connecté.
- [ ] **Drivecord Desktop** : login Cord dans la fenêtre → bascule dans l'app (jeton desktop).
- [ ] **Raccourcis** Sécurité / Appareils et Passcord / Apps connectées → bonnes ancres du portail.
## Limites connues / TODO
- **Push iOS native** pour l'approbation cross-device : non implémentée ; le
polling web (3–5 s) couvre le cas. Hook à ajouter si une infra push existe.
- **2FA** : une seule méthode active à la fois (TOTP **ou** email).
- **Modale post-login « ajouter un passkey »** : non implémentée (l'ajout se fait
dans Réglages → Sécurité + bouton de login).
- L'E2EE des fichiers normaux n'est **pas** zero-knowledge (le serveur détient la
clé du drive) — choix produit assumé, inchangé par cette feature.
## Portes de qualité
```bash
npx tsc --noEmit # 0 erreur
npm run lint # 0 erreur (warnings préexistants tolérés)
npm test # tests unitaires (node --test)
npm run build # build de production OK
```