# Système de Vérification de Signature Électronique ## Vue d'ensemble Ce système permet aux signataires de **vérifier l'authenticité et l'intégrité** de leurs documents signés électroniquement via une page web publique accessible par **URL ou QR code**. ## Architecture ### 1. Page de Vérification Publique **Fichier**: `app/verify/[id]/page.tsx` - Route dynamique: `https://espace-paie.odentas.com/verify/{uuid}` - Accessible sans authentification (RLS public en lecture) - Affiche tous les détails techniques de la signature ### 2. Table Supabase **Fichier**: `supabase/migrations/20251028_signature_verifications.sql` ```sql signature_verifications ├── id (UUID) → Identifiant unique de vérification ├── document_name → Nom du document signé ├── pdf_url → URL du PDF signé ├── signed_at → Date/heure de signature ├── signer_name → Nom du signataire ├── signer_email → Email du signataire ├── signature_hash → Hash SHA-256 du contenu signé ├── signature_hex → Signature complète en hexadécimal ├── certificate_info (JSONB) → Infos du certificat ├── timestamp (JSONB) → Horodatage TSA ├── verification_status (JSONB) → Statuts de validation ├── contract_id → Lien vers le contrat └── organization_id → Lien vers l'organisation ``` ### 3. API de Création **Fichier**: `app/api/signatures/create-verification/route.ts` ```typescript POST /api/signatures/create-verification Body: { document_name: string, pdf_url: string, signer_name: string, signer_email: string, signature_hash: string, organization_id: string, // Optionnel: signature_hex?: string, certificate_info?: object, timestamp?: object, contract_id?: string } Returns: { verification_id: string, verification_url: string, qr_code_data_url: string (PNG en base64) } ``` ### 4. Générateur de PDF de Preuve **Fichier**: `lib/signature-proof-pdf.ts` Génère un PDF A4 professionnel contenant: - En-tête Odentas Sign - Informations du document signé - **QR code** (70x70mm, centré) - URL de vérification - Détails techniques (hash, algorithme, certificat) - Note explicative sur le certificat auto-signé ### 5. Hook React **Fichier**: `hooks/useSignatureProof.ts` ```typescript const { createSignatureProof, isCreating } = useSignatureProof(); const proof = await createSignatureProof({ document_name: "Contrat CDDU", pdf_url: "https://...", signer_name: "Jean Dupont", signer_email: "jean@example.com", signature_hash: "dafe9a5e...", organization_id: "uuid" }); // Retourne: // - verification_url // - qr_code_data_url // - proof_pdf_blob ``` ## Workflow d'Utilisation ### Lors de la Signature d'un Document 1. **Le document est signé** avec Odentas Sign (Lambda PAdES) 2. **Extraction des données** : - Hash SHA-256 du contenu signé - Certificat de signature - Horodatage TSA (si présent) 3. **Appel API** `/api/signatures/create-verification` 4. **Génération** : - Entrée dans `signature_verifications` - QR code pointant vers `/verify/{id}` - PDF de preuve avec QR code 5. **Téléchargement** : - Document signé (PDF avec signature PAdES) - Preuve de signature (PDF avec QR code) ### Vérification par le Signataire 1. **Scanner le QR code** ou visiter l'URL 2. **Page publique** affiche : - ✅ Statut global (Valide / Techniquement Valide) - 📄 Informations du document - 🛡️ **Odentas Seal** (sceau électronique) - Format PAdES-BASELINE-B - Intégrité vérifiée (hash SHA-256) - Algorithme RSASSA-PSS - Détails du certificat - 🕐 **Odentas TSA** (horodatage) - RFC 3161 conforme - Autorité de temps - Empreinte horodatée - 🔍 **Vérification technique** - Structure PAdES valide - ByteRange correct - signing-certificate-v2 présent - MessageDigest intact - Statut du certificat (auto-signé ou qualifié) ## Éléments Affichés ### Odentas Seal - **Format** : PAdES-BASELINE-B (ETSI TS 102 778) - **Intégrité** : Hash SHA-256 du document - **Algorithme** : RSASSA-PSS avec SHA-256 - **Certificat** : - Émetteur - Sujet - Période de validité - Numéro de série ### Odentas TSA (si présent) - **Standard** : RFC 3161 - **Autorité** : URL du TSA - **Timestamp** : Date/heure certifiée - **Empreinte** : Hash horodaté ### Vérification Technique - ✅ Structure PAdES valide - ✅ ByteRange correct et complet - ✅ Attribut signing-certificate-v2 présent - ✅ MessageDigest intact - ⚠️ Certificat auto-signé (ou ✅ si qualifié) ## Statuts de Validation ### "Signature Valide" ✅ - Certificat **qualifié** reconnu par les autorités européennes - Tous les contrôles techniques passent - **Valeur légale** : QES (Qualified Electronic Signature) ### "Signature Techniquement Valide" ⚠️ - Certificat **auto-signé** (Odentas Media SAS) - Tous les contrôles techniques passent - **Valeur légale** : SES (Simple Electronic Signature) - Note explicative affichée ## Sécurité ### Row Level Security (RLS) ```sql -- Lecture publique (vérification accessible à tous) CREATE POLICY "Vérifications publiques" ON signature_verifications FOR SELECT USING (true); -- Seul le système peut créer/modifier CREATE POLICY "Système peut gérer" ON signature_verifications FOR ALL USING (auth.uid() IS NOT NULL); ``` ### Données Publiques Les informations exposées sont **volontairement publiques** : - Nom du signataire - Email du signataire - Nom du document - Hash SHA-256 - Certificat de signature **Aucune donnée sensible** (contenu du document, IBAN, etc.) n'est stockée. ## Intégration dans l'Application ### Dans les Contrats CDDU/RG Après signature via Docuseal + Odentas Sign : ```typescript import { useSignatureProof } from "@/hooks/useSignatureProof"; const { createSignatureProof } = useSignatureProof(); // Après réception du document signé const proof = await createSignatureProof({ document_name: `Contrat ${employee.prenom} ${employee.nom}`, pdf_url: signedPdfUrl, signer_name: `${employee.prenom} ${employee.nom}`, signer_email: employee.email, signature_hash: extractedHash, contract_id: contract.id, organization_id: contract.organization_id, }); // Télécharger les deux fichiers: // 1. Document signé (PDF PAdES) // 2. Preuve de signature (PDF avec QR code) downloadFile(signedPdfUrl, "contrat-signe.pdf"); downloadFile(URL.createObjectURL(proof.proof_pdf_blob), "preuve-signature.pdf"); ``` ## Design ### Page de Vérification - **Layout** : Gradient slate 50→100 - **Cards** : Blanc avec shadow-xl et border-radius 16px - **Statuts** : - ✅ Vert (green-600) pour valide - ⚠️ Orange (orange-600) pour techniquement valide - ❌ Rouge (red-600) pour invalide - **Icônes** : Lucide React (CheckCircle2, Shield, Clock, FileText) ### PDF de Preuve - **Format** : A4 portrait - **En-tête** : Indigo (Odentas branding) - **QR Code** : 70x70mm, centré, haute qualité - **Sections** : - Document signé (fond slate-100) - QR code avec URL - Détails techniques - Note importante (fond orange-50) ## Variables d'Environnement ```env NEXT_PUBLIC_APP_URL=https://espace-paie.odentas.com ``` Utilisée pour générer les URLs de vérification. ## Migration Supabase ```bash # Appliquer la migration supabase db push # Ou manuellement psql -h db.xxx.supabase.co -U postgres -d postgres -f supabase/migrations/20251028_signature_verifications.sql ``` ## Exemples d'URLs ``` https://espace-paie.odentas.com/verify/550e8400-e29b-41d4-a716-446655440000 ``` ## TODO / Améliorations Futures - [ ] Support multi-signatures (plusieurs signataires) - [ ] Historique de vérifications (analytics) - [ ] Notifications email avec lien de vérification - [ ] API publique de vérification (pour intégrations tierces) - [ ] Support certificats qualifiés (badge "Qualifié eIDAS") - [ ] Archivage long terme (LTV avec DSS/LTA) - [ ] Export PDF/A pour archivage légal ## Conformité - ✅ **PAdES-BASELINE-B** : ETSI TS 102 778 - ✅ **RFC 3161** : Horodatage électronique - ✅ **RFC 5035** : signing-certificate-v2 - ✅ **RGPD** : Données publiques consenties - ⚠️ **eIDAS** : SES (auto-signé) ou QES (avec certificat qualifié) --- **Odentas Media SAS** - Signature électronique sécurisée