# Signatures Multiples avec /Name et /Reason - Odentas Sign ## 📋 Concept Quand plusieurs personnes signent un document via Odentas Sign (employeur puis salarié), **chaque signature est techniquement signée par Odentas** (avec le certificat KMS), mais avec des métadonnées personnalisées indiquant **pour qui** la signature a été faite. C'est exactement ce que fait DocuSeal : quand vous ouvrez un PDF dans Adobe, vous voyez "DocuSeal" comme signataire technique, mais les champs `/Name` et `/Reason` indiquent le vrai signataire. ## 🔑 Architecture ### Tables Supabase utilisées **Pas besoin de nouvelles tables !** Votre système existant est parfait : 1. **`sign_requests`** : La demande de signature 2. **`signers`** : Les signataires (Employeur + Salarié) - **NOUVELLES colonnes** : - `signature_name` : Nom affiché dans la signature (`/Name`) - `signature_reason` : Raison de la signature (`/Reason`) - `signature_location` : Lieu (`/Location`) - `signature_contact_info` : Contact (`/ContactInfo`) 3. **`sign_positions`** : Où placer les signatures 4. **`sign_events`** : Audit trail 5. **`sign_assets`** : Document final scellé ### Workflow de signature ``` 1. Création de la demande ├─ sign_requests (status: pending) ├─ signers[0] (Employeur, signature_name: "Entreprise SARL", signature_reason: "Signature employeur") └─ signers[1] (Salarié, signature_name: "Jean Dupont", signature_reason: "Signature salarié") 2. Employeur signe (interface graphique) ├─ signers[0].signed_at = now() └─ signers[0].signature_image_s3 = "signatures/req-123/employeur.png" 3. Salarié signe (interface graphique) ├─ signers[1].signed_at = now() └─ signers[1].signature_image_s3 = "signatures/req-123/salarie.png" 4. Scellement PAdES (API /seal-document) ├─ Lambda sign avec signers[0] → /Name="Entreprise SARL" /Reason="Signature employeur" │ └─ PDF devient: signed-pades/contract-123-step1.pdf (1 signature PAdES) │ └─ Lambda sign avec signers[1] → /Name="Jean Dupont" /Reason="Signature salarié" └─ PDF final: signed-pades/contract-123-final.pdf (2 signatures PAdES) 5. Résultat └─ sign_assets.signed_pdf_s3_key = "signed-pades/contract-123-final.pdf" ``` ## 🔧 Modifications nécessaires ### 1. Migration Supabase **Fichier** : `supabase/migrations/20251029_add_signature_metadata_to_signers.sql` ```sql ALTER TABLE signers ADD COLUMN signature_name TEXT NOT NULL, ADD COLUMN signature_reason TEXT NOT NULL, ADD COLUMN signature_location TEXT DEFAULT 'France', ADD COLUMN signature_contact_info TEXT; ``` ### 2. Lambda `odentas-pades-sign` **Input modifié** : ```javascript { "s3Key": "source/contract-123.pdf", "signatureMetadata": { "name": "Entreprise SARL", // → /Name dans le dictionnaire de signature "reason": "Signature employeur", // → /Reason "location": "Paris, France", // → /Location "contactInfo": "contact@entreprise.com" // → /ContactInfo }, "signerOrder": 1 // 1ère signature ou 2ème } ``` **Dans la Lambda**, lors de la création du dictionnaire de signature : ```javascript const signatureDict = { Type: 'Sig', Filter: 'Adobe.PPKLite', SubFilter: 'ETSI.CAdES.detached', Name: signatureMetadata.name, // ← ICI Reason: signatureMetadata.reason, // ← ICI Location: signatureMetadata.location, // ← ICI ContactInfo: signatureMetadata.contactInfo, // ← ICI M: `D:${timestamp}`, ByteRange: [0, 0, 0, 0], Contents: '' }; ``` ### 3. API Route `/api/odentas-sign/seal-document` **Fichier** : `app/api/odentas-sign/seal-document/route.ts` Cette route : 1. Récupère tous les `signers` ayant signé 2. Les trie par ordre chronologique (`signed_at`) 3. Appelle la Lambda séquentiellement pour chaque signataire 4. Chaque appel Lambda utilise les métadonnées du signataire (`signature_name`, `signature_reason`) 5. Le PDF de sortie d'une signature devient l'entrée de la suivante 6. Enregistre le PDF final dans `sign_assets` ### 4. Quand créer les signataires **Dans** : `app/api/odentas-sign/requests/create/route.ts` ```typescript const signersData = body.signers.map((s) => ({ request_id: signRequest.id, role: s.role, name: s.name, email: s.email, // Nouvelles colonnes signature_name: s.name, // ou entreprise.name pour l'employeur signature_reason: `Signature du contrat ${signRequest.ref} en tant que ${s.role.toLowerCase()}`, signature_location: 'France', signature_contact_info: s.email, })); ``` ## 📊 Résultat dans Adobe Acrobat Quand vous ouvrez le PDF final dans Adobe : ``` ┌─────────────────────────────────────────┐ │ Signatures du document (2) │ ├─────────────────────────────────────────┤ │ ✓ Odentas Media SAS │ ← Certificat technique │ Nom: Entreprise SARL │ ← /Name │ Raison: Signature employeur │ ← /Reason │ Lieu: Paris, France │ ← /Location │ Contact: contact@entreprise.com │ ← /ContactInfo │ Date: 29/10/2025 14:30:00 │ ├─────────────────────────────────────────┤ │ ✓ Odentas Media SAS │ ← Même certificat │ Nom: Jean Dupont │ ← /Name différent │ Raison: Signature salarié │ ← /Reason différent │ Lieu: France │ │ Contact: jean.dupont@example.com │ │ Date: 29/10/2025 15:45:00 │ └─────────────────────────────────────────┘ ``` ## 🔄 Flux complet ```mermaid sequenceDiagram participant UI as Interface Web participant API as API Odentas Sign participant DB as Supabase participant Lambda as Lambda PAdES Sign participant S3 as AWS S3 UI->>API: POST /requests/create API->>DB: INSERT sign_requests API->>DB: INSERT signers (avec signature_name/reason) API->>S3: Upload PDF source UI->>API: Employeur signe (interface) API->>DB: UPDATE signers[0].signed_at API->>S3: Upload signature image UI->>API: Salarié signe (interface) API->>DB: UPDATE signers[1].signed_at API->>S3: Upload signature image UI->>API: POST /seal-document API->>DB: SELECT signers ORDER BY signed_at loop Pour chaque signataire API->>Lambda: Invoke avec signature_metadata Lambda->>S3: GET PDF (source ou précédent) Lambda->>Lambda: Ajouter signature PAdES Lambda->>S3: PUT PDF signé Lambda-->>API: signedS3Key end API->>DB: INSERT sign_assets (PDF final) API->>DB: UPDATE sign_requests (status: completed) API-->>UI: Document scellé ``` ## ✅ Avantages 1. **Pas de nouvelles tables** : Réutilise `signers` existant 2. **Flexible** : Peut gérer 2, 3 ou N signatures 3. **Conforme PAdES** : Chaque signature est indépendante et valide 4. **Audit trail** : `sign_events` trace tout le processus 5. **Compatible Adobe** : Affiche correctement les noms dans Adobe Acrobat 6. **Identique DocuSeal** : Même principe technique ## 🎯 Prochaines étapes 1. ✅ Appliquer la migration Supabase 2. ⏳ Modifier la Lambda pour accepter `signatureMetadata` 3. ⏳ Tester avec un contrat CDDU 4. ⏳ Vérifier dans Adobe que `/Name` et `/Reason` apparaissent correctement 5. ⏳ Intégrer l'appel à `/seal-document` dans votre workflow existant ## 📝 Notes - Le **certificat est toujours Odentas** (émis par AWS KMS) - Les **métadonnées changent** pour chaque signataire - Les signatures sont **appliquées séquentiellement** (importante pour la validité) - Chaque signature **englobe la précédente** (signature incrémentale PAdES)