- Remplacement de DocuSeal par solution souveraine Odentas Sign - Système d'authentification OTP pour signataires (bcryptjs + JWT) - 8 routes API: send-otp, verify-otp, sign, pdf-url, positions, status, webhook, signers - Interface moderne avec canvas de signature et animations (framer-motion, confetti) - Système de templates pour auto-détection des positions de signature (CDDU, RG, avenants) - PDF viewer avec @react-pdf-viewer (compatible Next.js) - Stockage S3: source/, signatures/, evidence/, signed/, certs/ - Tables Supabase: sign_requests, signers, sign_positions, sign_events, sign_assets - Evidence bundle automatique (JSON metadata + timestamps) - Templates emails: OTP et completion - Scripts Lambda prêts: pades-sign (KMS seal) et tsaStamp (RFC3161) - Mode test détecté automatiquement (emails whitelist) - Tests complets avec PDF CDDU réel (2 signataires)
407 lines
10 KiB
Markdown
407 lines
10 KiB
Markdown
# 🎨 Odentas Sign - Interface de Signature (Phase 2)
|
||
|
||
## 📋 Vue d'ensemble
|
||
|
||
L'interface de signature Odentas Sign offre une expérience moderne et fluide pour la signature électronique de documents. Elle remplace complètement DocuSeal avec une solution souveraine et conforme eIDAS.
|
||
|
||
## 🎯 Fonctionnalités
|
||
|
||
### ✅ Implémenté
|
||
|
||
- ✨ Design moderne avec Tailwind CSS et Framer Motion
|
||
- 🔐 Authentification OTP à 6 chiffres
|
||
- ✍️ Canvas de signature HTML5 (souris, trackpad, tactile)
|
||
- 📊 Barre de progression en temps réel
|
||
- 🎉 Animation de célébration (confetti) après signature
|
||
- 📱 Responsive mobile-first
|
||
- 🔒 Vérification de consentement obligatoire
|
||
- ⏱️ Countdown timer pour l'OTP (15 minutes)
|
||
- 🚫 Limite de tentatives (3 maximum)
|
||
- 📈 Affichage de la progression des signatures
|
||
|
||
### 🔄 À venir
|
||
|
||
- 📄 Visualiseur PDF avec zones de signature surlignées
|
||
- 📥 Téléchargement du document signé
|
||
- 📧 Notifications email améliorées
|
||
|
||
## 🗂️ Structure des fichiers
|
||
|
||
```
|
||
app/signer/[requestId]/[signerId]/
|
||
├── page.tsx # Page principale avec routing
|
||
└── components/
|
||
├── ProgressBar.tsx # Barre de progression des étapes
|
||
├── OTPVerification.tsx # Écran de vérification OTP
|
||
├── SignatureCapture.tsx # Canvas de signature
|
||
└── CompletionScreen.tsx # Écran de confirmation
|
||
```
|
||
|
||
## 🎨 Flow utilisateur
|
||
|
||
### 1️⃣ Étape 1 : Vérification OTP
|
||
|
||
**URL:** `/signer/[requestId]/[signerId]`
|
||
|
||
**Processus:**
|
||
1. L'utilisateur arrive sur la page avec son lien unique
|
||
2. Affichage de son nom et email pré-remplis
|
||
3. Clic sur "Recevoir le code"
|
||
4. En **mode test** : OTP affiché dans les logs serveur
|
||
5. En **mode production** : Email SES envoyé
|
||
6. Saisie du code à 6 chiffres (auto-focus, auto-submit)
|
||
7. Vérification du code → Génération d'un JWT (30min)
|
||
8. Transition automatique vers l'étape signature
|
||
|
||
**Sécurité:**
|
||
- Code valide 15 minutes
|
||
- Maximum 3 tentatives
|
||
- Rate limiting 60 secondes entre envois
|
||
- JWT avec expiration
|
||
|
||
### 2️⃣ Étape 2 : Signature
|
||
|
||
**Processus:**
|
||
1. Canvas de signature responsive
|
||
2. Dessin avec souris/trackpad/doigt
|
||
3. Bouton "Recommencer" pour effacer
|
||
4. Checkbox de consentement obligatoire
|
||
5. Validation → Upload de l'image PNG
|
||
6. Enregistrement en base + S3
|
||
7. Si tous ont signé → Déclenchement webhook
|
||
8. Transition vers écran de confirmation
|
||
|
||
**Canvas:**
|
||
- Taille adaptative (devicePixelRatio)
|
||
- Ligne fluide (lineCap: round)
|
||
- Couleur noire (#1e293b)
|
||
- Export PNG avec transparence
|
||
- Support touch events
|
||
|
||
### 3️⃣ Étape 3 : Confirmation
|
||
|
||
**Processus:**
|
||
1. 🎉 Animation de confetti
|
||
2. Affichage des détails (date, référence)
|
||
3. Progression des signatures (X/Y signé)
|
||
4. Message différent selon statut:
|
||
- Tous signés → "Document finalisé"
|
||
- En attente → "Attente des autres"
|
||
5. Boutons (téléchargement désactivé pour l'instant)
|
||
|
||
## 🧪 Test de l'interface
|
||
|
||
### Prérequis
|
||
|
||
```bash
|
||
# 1. Créer une demande de test
|
||
node test-odentas-sign.js
|
||
|
||
# 2. Lancer le serveur Next.js en dev
|
||
npm run dev
|
||
|
||
# 3. Utiliser le script de test interactif
|
||
./test-interface-signature.sh
|
||
```
|
||
|
||
### Script de test
|
||
|
||
Le script `test-interface-signature.sh` offre:
|
||
|
||
1. **Ouvrir l'interface Employeur** → Ouvre automatiquement le navigateur
|
||
2. **Ouvrir l'interface Salarié** → Ouvre automatiquement le navigateur
|
||
3. **Afficher l'OTP Employeur** → Trigger l'envoi + instructions pour logs
|
||
4. **Afficher l'OTP Salarié** → Trigger l'envoi + instructions pour logs
|
||
5. **Vérifier le statut** → Affiche qui a signé, progression
|
||
6. **Quitter**
|
||
|
||
### Mode test vs Production
|
||
|
||
| Aspect | Mode Test | Mode Production |
|
||
|--------|-----------|----------------|
|
||
| **Détection** | Ref commence par `TEST-` | Ref normale |
|
||
| **OTP** | Logs serveur | Email SES |
|
||
| **Scellement** | Sauté | PAdES + TSA |
|
||
| **Archive** | Sautée | S3 Object Lock 10 ans |
|
||
|
||
## 🎨 Design System
|
||
|
||
### Couleurs
|
||
|
||
```css
|
||
/* Gradients principaux */
|
||
from-indigo-600 to-purple-600 /* Header vérification */
|
||
from-green-500 to-teal-500 /* Confirmation success */
|
||
from-slate-50 via-blue-50 /* Background page */
|
||
|
||
/* Boutons */
|
||
bg-indigo-600 hover:bg-indigo-700 /* Primary action */
|
||
bg-slate-100 text-slate-400 /* Disabled */
|
||
border-slate-200 /* Secondary */
|
||
```
|
||
|
||
### Animations
|
||
|
||
```typescript
|
||
// Transitions de page
|
||
initial={{ opacity: 0, y: 20 }}
|
||
animate={{ opacity: 1, y: 0 }}
|
||
exit={{ opacity: 0, y: -20 }}
|
||
|
||
// Progress bar fill
|
||
initial={{ width: 0 }}
|
||
animate={{ width: `${percentage}%` }}
|
||
transition={{ duration: 1, ease: 'easeOut' }}
|
||
|
||
// Confetti celebration
|
||
confetti({
|
||
particleCount: 50,
|
||
startVelocity: 30,
|
||
spread: 360,
|
||
colors: ['#6366f1', '#8b5cf6', '#ec4899']
|
||
})
|
||
```
|
||
|
||
### Composants réutilisables
|
||
|
||
- `ProgressBar` : Étapes avec cercles et connecteurs
|
||
- Icons de Lucide : `Shield`, `Check`, `Clock`, `Users`, `PenTool`
|
||
- Modales animées avec `AnimatePresence`
|
||
|
||
## 🔐 Sécurité
|
||
|
||
### JWT Session Token
|
||
|
||
```typescript
|
||
{
|
||
signerId: string,
|
||
requestId: string,
|
||
role: string,
|
||
iat: number, // Issued at
|
||
exp: number, // Expiration (30 minutes)
|
||
iss: 'odentas-sign'
|
||
}
|
||
```
|
||
|
||
**Utilisation:**
|
||
- Généré après validation OTP
|
||
- Stocké dans state React (pas de localStorage)
|
||
- Envoyé dans header `Authorization: Bearer <token>`
|
||
- Vérifié côté serveur pour `/sign`
|
||
|
||
### Protection CSRF
|
||
|
||
- Origin checking dans les API routes
|
||
- Rate limiting sur `/send-otp`
|
||
- Validation des inputs (OTP digits only)
|
||
|
||
### Données personnelles
|
||
|
||
- Email affiché mais non modifiable
|
||
- IP et User-Agent enregistrés dans `sign_events`
|
||
- Consentement explicite requis
|
||
- Archivage 10 ans conforme RGPD (base légale: contrat)
|
||
|
||
## 📊 Tracking des événements
|
||
|
||
Tous les événements sont loggés dans `sign_events`:
|
||
|
||
1. `request_created` → Création demande
|
||
2. `otp_sent` → Envoi code
|
||
3. `otp_verified` → Code validé
|
||
4. `otp_verification_failed` → Code invalide
|
||
5. `signed` → Signature enregistrée
|
||
6. `request_completed` → Tous ont signé
|
||
7. `pdf_sealed` → PAdES appliqué
|
||
8. `document_timestamped` → TSA horodatage
|
||
9. `archived` → Archive S3
|
||
|
||
## 🌐 URLs
|
||
|
||
### Signature
|
||
|
||
```
|
||
/signer/[requestId]/[signerId]
|
||
```
|
||
|
||
**Exemple:**
|
||
```
|
||
/signer/75b4408d-1bbd-464f-a9ea-2b4e5075a817/95c4ccdc-1a26-4426-a56f-653758159b54
|
||
```
|
||
|
||
### API Endpoints utilisés
|
||
|
||
```
|
||
POST /api/odentas-sign/signers/[id]/send-otp
|
||
POST /api/odentas-sign/signers/[id]/verify-otp
|
||
POST /api/odentas-sign/signers/[id]/sign
|
||
GET /api/odentas-sign/signers/[id]/status
|
||
```
|
||
|
||
## 📱 Responsive Design
|
||
|
||
### Breakpoints
|
||
|
||
```css
|
||
sm: 640px /* Tablettes portrait */
|
||
md: 768px /* Tablettes landscape */
|
||
lg: 1024px /* Desktop */
|
||
xl: 1280px /* Large screens */
|
||
```
|
||
|
||
### Canvas tactile
|
||
|
||
```typescript
|
||
// Disable browser touch gestures
|
||
style={{ touchAction: 'none' }}
|
||
|
||
// Support touch events
|
||
onTouchStart={startDrawing}
|
||
onTouchMove={draw}
|
||
onTouchEnd={stopDrawing}
|
||
```
|
||
|
||
## 🎯 Prochaines étapes
|
||
|
||
### PDF Viewer intégration
|
||
|
||
```bash
|
||
npm install react-pdf pdfjs-dist
|
||
```
|
||
|
||
**Fonctionnalités:**
|
||
- Afficher le PDF dans `SignatureCapture`
|
||
- Overlay semi-transparent sur zones de signature
|
||
- Scroll automatique vers la zone du signataire
|
||
- Zoom responsive
|
||
|
||
### Email notifications
|
||
|
||
Templates à créer:
|
||
- `signature-completed.html` → Envoyé au signataire après sa signature
|
||
- `all-signatures-completed.html` → Envoyé à tous quand finalisé
|
||
- Avec lien de téléchargement du PDF signé
|
||
|
||
### Download du document
|
||
|
||
```typescript
|
||
async function downloadSignedDocument() {
|
||
const response = await fetch(`/api/odentas-sign/requests/${requestId}/download`);
|
||
const blob = await response.blob();
|
||
const url = URL.createObjectURL(blob);
|
||
const a = document.createElement('a');
|
||
a.href = url;
|
||
a.download = `${documentRef}-signed.pdf`;
|
||
a.click();
|
||
}
|
||
```
|
||
|
||
## 🐛 Debugging
|
||
|
||
### Logs serveur
|
||
|
||
```bash
|
||
npm run dev
|
||
```
|
||
|
||
Chercher dans les logs:
|
||
```
|
||
⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
|
||
MODE TEST - Code OTP
|
||
⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
|
||
```
|
||
|
||
### Console navigateur
|
||
|
||
```javascript
|
||
// État du composant
|
||
React DevTools → SignerPage → state
|
||
|
||
// Erreurs réseau
|
||
Network tab → Filter: "odentas-sign"
|
||
|
||
// JWT decode
|
||
JSON.parse(atob(token.split('.')[1]))
|
||
```
|
||
|
||
### Base de données
|
||
|
||
```sql
|
||
-- Derniers événements
|
||
SELECT * FROM sign_events
|
||
ORDER BY created_at DESC
|
||
LIMIT 20;
|
||
|
||
-- Statut des signataires
|
||
SELECT
|
||
sr.ref,
|
||
s.name,
|
||
s.role,
|
||
s.has_signed,
|
||
s.signed_at
|
||
FROM signers s
|
||
JOIN sign_requests sr ON s.request_id = sr.id
|
||
ORDER BY sr.created_at DESC;
|
||
```
|
||
|
||
## 🎉 Migration depuis DocuSeal
|
||
|
||
### Coexistence
|
||
|
||
Les anciennes pages DocuSeal restent actives:
|
||
- `/signatures-electroniques` → DocuSeal (employeur)
|
||
- `/signature-salarie` → DocuSeal (salarié)
|
||
|
||
Nouvelles pages Odentas Sign:
|
||
- `/signer/[requestId]/[signerId]` → Odentas Sign (tous rôles)
|
||
|
||
### Migration progressive
|
||
|
||
1. **Phase 1** : Nouveaux contrats → Odentas Sign
|
||
2. **Phase 2** : Anciens contrats → Continuer DocuSeal
|
||
3. **Phase 3** : Quand tous migrés → Supprimer DocuSeal
|
||
|
||
### Détection automatique
|
||
|
||
```typescript
|
||
// Dans le code de création de contrat
|
||
const useOdentasSign = process.env.NEXT_PUBLIC_USE_ODENTAS_SIGN === 'true';
|
||
|
||
if (useOdentasSign) {
|
||
// Créer via /api/odentas-sign/requests/create
|
||
// Envoyer liens /signer/[requestId]/[signerId]
|
||
} else {
|
||
// Créer via DocuSeal
|
||
// Envoyer liens DocuSeal
|
||
}
|
||
```
|
||
|
||
## 📚 Ressources
|
||
|
||
- [Framer Motion Docs](https://www.framer.com/motion/)
|
||
- [Tailwind CSS](https://tailwindcss.com)
|
||
- [Lucide Icons](https://lucide.dev)
|
||
- [Canvas Confetti](https://www.npmjs.com/package/canvas-confetti)
|
||
- [eIDAS Regulation](https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=uriserv:OJ.L_.2014.257.01.0073.01.ENG)
|
||
|
||
## ✅ Checklist de production
|
||
|
||
Avant mise en production:
|
||
|
||
- [ ] Tester sur mobile (iOS Safari, Android Chrome)
|
||
- [ ] Vérifier accessibilité (contraste, keyboard navigation)
|
||
- [ ] Intégrer PDF viewer
|
||
- [ ] Ajouter templates email de notification
|
||
- [ ] Activer vraie signature PAdES (retirer test mode bypass)
|
||
- [ ] Configurer monitoring (Sentry, logs CloudWatch)
|
||
- [ ] Load testing (Artillery, k6)
|
||
- [ ] Documentation utilisateur finale
|
||
- [ ] Formation équipe support
|
||
- [ ] Plan de rollback DocuSeal
|
||
|
||
---
|
||
|
||
**Créé le:** $(date +%Y-%m-%d)
|
||
**Version:** 2.0.0
|
||
**Auteur:** GitHub Copilot
|
||
**Statut:** ✅ Phase 2 Complète
|