- 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)
10 KiB
10 KiB
🎨 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:
- L'utilisateur arrive sur la page avec son lien unique
- Affichage de son nom et email pré-remplis
- Clic sur "Recevoir le code"
- En mode test : OTP affiché dans les logs serveur
- En mode production : Email SES envoyé
- Saisie du code à 6 chiffres (auto-focus, auto-submit)
- Vérification du code → Génération d'un JWT (30min)
- 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:
- Canvas de signature responsive
- Dessin avec souris/trackpad/doigt
- Bouton "Recommencer" pour effacer
- Checkbox de consentement obligatoire
- Validation → Upload de l'image PNG
- Enregistrement en base + S3
- Si tous ont signé → Déclenchement webhook
- 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:
- 🎉 Animation de confetti
- Affichage des détails (date, référence)
- Progression des signatures (X/Y signé)
- Message différent selon statut:
- Tous signés → "Document finalisé"
- En attente → "Attente des autres"
- Boutons (téléchargement désactivé pour l'instant)
🧪 Test de l'interface
Prérequis
# 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:
- Ouvrir l'interface Employeur → Ouvre automatiquement le navigateur
- Ouvrir l'interface Salarié → Ouvre automatiquement le navigateur
- Afficher l'OTP Employeur → Trigger l'envoi + instructions pour logs
- Afficher l'OTP Salarié → Trigger l'envoi + instructions pour logs
- Vérifier le statut → Affiche qui a signé, progression
- 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
/* 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
// 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
{
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:
request_created→ Création demandeotp_sent→ Envoi codeotp_verified→ Code validéotp_verification_failed→ Code invalidesigned→ Signature enregistréerequest_completed→ Tous ont signépdf_sealed→ PAdES appliquédocument_timestamped→ TSA horodatagearchived→ 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
sm: 640px /* Tablettes portrait */
md: 768px /* Tablettes landscape */
lg: 1024px /* Desktop */
xl: 1280px /* Large screens */
Canvas tactile
// Disable browser touch gestures
style={{ touchAction: 'none' }}
// Support touch events
onTouchStart={startDrawing}
onTouchMove={draw}
onTouchEnd={stopDrawing}
🎯 Prochaines étapes
PDF Viewer intégration
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 signatureall-signatures-completed.html→ Envoyé à tous quand finalisé- Avec lien de téléchargement du PDF signé
Download du document
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
npm run dev
Chercher dans les logs:
⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
MODE TEST - Code OTP
⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
Console navigateur
// É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
-- 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
- Phase 1 : Nouveaux contrats → Odentas Sign
- Phase 2 : Anciens contrats → Continuer DocuSeal
- Phase 3 : Quand tous migrés → Supprimer DocuSeal
Détection automatique
// 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
✅ 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