espace-paie-odentas/lib/odentas-sign/crypto.ts
odentas b790faf12c feat: Implémentation complète du système Odentas Sign
- 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)
2025-10-27 19:03:07 +01:00

81 lines
1.9 KiB
TypeScript

import crypto from 'crypto';
import bcrypt from 'bcryptjs';
/**
* Génère un code OTP à 6 chiffres
*/
export function generateOTP(): string {
return crypto.randomInt(100000, 999999).toString();
}
/**
* Hash un code OTP avec bcrypt
*/
export async function hashOTP(otp: string): Promise<string> {
const salt = await bcrypt.genSalt(10);
return bcrypt.hash(otp, salt);
}
/**
* Vérifie un code OTP contre son hash
*/
export async function verifyOTP(otp: string, hash: string): Promise<boolean> {
return bcrypt.compare(otp, hash);
}
/**
* Calcule la date d'expiration d'un OTP (15 minutes)
*/
export function getOTPExpiration(): Date {
const now = new Date();
now.setMinutes(now.getMinutes() + 15);
return now;
}
/**
* Vérifie si un OTP est expiré
*/
export function isOTPExpired(expiresAt: string | null): boolean {
if (!expiresAt) return true;
return new Date(expiresAt) < new Date();
}
/**
* Génère une référence unique pour une demande de signature
* Format: REQ-YYYYMMDD-XXXXXX (6 caractères aléatoires)
*/
export function generateRequestRef(contractRef?: string): string {
if (contractRef) {
// Si on a une référence de contrat, l'utiliser comme base
return contractRef;
}
const date = new Date();
const dateStr = date.toISOString().slice(0, 10).replace(/-/g, '');
const random = crypto.randomBytes(3).toString('hex').toUpperCase();
return `REQ-${dateStr}-${random}`;
}
/**
* Génère un nom de fichier sécurisé pour S3
*/
export function sanitizeFilename(filename: string): string {
return filename
.replace(/[^a-zA-Z0-9._-]/g, '_')
.replace(/_{2,}/g, '_')
.substring(0, 255);
}
/**
* Calcule le SHA-256 d'un buffer
*/
export function calculateSHA256(buffer: Buffer): string {
return crypto.createHash('sha256').update(buffer).digest('hex');
}
/**
* Génère un ID de session unique
*/
export function generateSessionId(): string {
return crypto.randomBytes(32).toString('hex');
}