Seguridad

Cómo AllSign protege cada sesión de firma embebida — desde la generación del token hasta la entrega del PDF firmado.

Modelo de seguridad

Inspirado en Plaid Link moderno (post-2020) y Veriff (session tokens). Cada sesión tiene múltiples capas:

┌─ Tu backend (secret key: allsign_live_sk_xxx) ───────────┐
│  POST /v2/signing-sessions                                │
│  → Crea documento + firmantes + config                    │
│  → Devuelve client_secret (token efímero, 15 min)         │
│  → La secret key NUNCA sale de tu backend                 │
└───────────────────────────────────────────────────────────┘

          │  client_secret (single-use, scoped a 1 session)

┌─ Tu frontend (solo el client_secret) ────────────────────┐
│  JS SDK monta iFrame con el client_secret                 │
│  → client_secret es la UNICA credencial en el browser     │
│  → postMessage con origin validation                      │
│  → CSP frame-ancestors restringe dominios                 │
└───────────────────────────────────────────────────────────┘

          │  iFrame carga sign.allsign.io

┌─ AllSign ────────────────────────────────────────────────┐
│  Token hash (SHA-256) — nunca se almacena en texto plano  │
│  Sesión atada a: documento + firmante + timestamp         │
│  OTP opcional pre-firma (email o WhatsApp)                │
│  Camera permissions para IDV embebido                     │
└───────────────────────────────────────────────────────────┘

Secret key vs Client secret

AllSign usa una sola credencial en el frontend: el client_secret. No hay publishable key. Este es el mismo modelo que Plaid adoptó en 2020 cuando deprecó su publicKey:

CredencialPrefijoDónde se usaAlcanceVida
Secret keyallsign_live_sk_Solo tu backend (server-side)Crear documentos, signing sessions, leer datos, verificar firmasPermanente (hasta que la rotes)
Client secretas_sess_..._secret_...Tu frontend (browser)Montar UNA sola session de firma15 minutos, single-use
// ✅ Backend — usa la secret key
const response = await fetch('https://api.allsign.io/v2/signing-sessions', {
  method: 'POST',
  headers: { 'Authorization': 'Bearer allsign_live_sk_xxx' },
  body: JSON.stringify({ document_id, signer_email })
});
const session = await response.json();
// session.client_secret → pásalo a tu frontend

// ✅ Frontend — usa SOLO el client_secret
const signing = AllSign.init({ clientSecret: session.client_secret });
signing.modal();

// ❌ NUNCA pongas la secret key en el frontend
// El SDK detecta este error y lo rechaza con un mensaje claro

Signing Sessions (client_secret)

El client_secret generado por POST /v2/signing-sessions tiene estas propiedades:

PropiedadValor
Tiempo de vida15 minutos desde la generación
UsoSingle-use — una vez que se abre, el token se consume
AlmacenamientoEl token se hashea con SHA-256 antes de guardarse en DB
ContenidoSession ID + firmante + documento + timestamp + nonce encriptado
RevocaciónSe invalida automáticamente si el documento cambia de estado
AlcanceSolo puede montar el widget — no puede leer ni modificar datos

Buenas prácticas

  • Crea la session justo antes de que el usuario necesite firmar — no la pre-generes
  • Nunca expongas el client_secret en logs — trátalo como credencial temporal
  • Si expira, crea una nueva session — el endpoint es idempotente para el mismo firmante
// ✅ Correcto: crea la session al momento de abrir
async function openSigning() {
  const { client_secret } = await fetch('/api/create-session').then(r => r.json());
  allsign.modal({ clientSecret: client_secret });
}

// ❌ Incorrecto: pre-crear la session al cargar la página
const session = await fetch('/api/create-session').then(r => r.json());
// ... 20 minutos después ...
allsign.modal({ clientSecret: session.client_secret }); // SESSION_EXPIRED

Content Security Policy (CSP)

Para que el iFrame de AllSign funcione en tu sitio, tu CSP debe permitir frames desde el dominio de AllSign.

Configuración requerida

Agrega estas directivas a tu Content Security Policy:

frame-src https://sign.allsign.io;

Si usas el CDN del SDK:

script-src https://cdn.allsign.io;
frame-src https://sign.allsign.io;

Ejemplo en meta tag

<meta http-equiv="Content-Security-Policy"
  content="frame-src https://sign.allsign.io; script-src 'self' https://cdn.allsign.io;">

Ejemplo en header HTTP (Next.js)

// next.config.js
const securityHeaders = [
  {
    key: 'Content-Security-Policy',
    value: "frame-src https://sign.allsign.io; script-src 'self' https://cdn.allsign.io;"
  }
];

Frame-ancestors (del lado de AllSign)

AllSign configura frame-ancestors en las respuestas del iFrame para restringir qué dominios pueden embeber la firma. Por defecto, se permiten todos los orígenes para sesiones embebidas. Si necesitas restringir esto a dominios específicos, contacta soporte.


Validación de origen (postMessage)

El SDK usa postMessage para comunicación entre tu página y el iFrame de AllSign. Internamente, el SDK valida que los mensajes provengan del origen correcto:

// Esto lo hace el SDK automáticamente — no necesitas implementarlo
window.addEventListener('message', (event) => {
  if (event.origin !== 'https://sign.allsign.io') return;
  // Procesar evento...
});

Verificación de identidad pre-firma

Para documentos sensibles, puedes requerir verificación de identidad antes de que el firmante acceda al documento. Esto se configura al crear el documento:

{
  "title": "Contrato de arrendamiento",
  "signers": [{
    "name": "Juan Pérez",
    "email": "juan@empresa.com",
    "embedded": true
  }],
  "signature_validations": {
    "autografa": true,
    "identity_verification": {
      "enabled": true,
      "document_scan": true,
      "selfie_capture": true,
      "biometric_face_match": true
    }
  }
}

Cuando el widget se monta, el firmante primero pasa por el pipeline de verificación (escaneo de INE, selfie, match biométrico) y solo después puede firmar.

Flujo con verificación

Widget se monta
  → Escaneo de INE (OCR + validación)
  → Selfie del firmante
  → Face match biométrico (INE vs selfie)
  → ✅ Identidad verificada
  → Mostrar documento para firma
  → Firma (autógrafa o simple)
  → ✅ Documento firmado con evidencia de identidad

Todos estos pasos ocurren dentro del widget embebido — el firmante nunca sale de tu aplicación.


OTP pre-firma (opcional)

Agrega una capa adicional de verificación con OTP (One-Time Password) por email o WhatsApp:

{
  "signers": [{
    "name": "Juan Pérez",
    "email": "juan@empresa.com",
    "phone": "+521234567890",
    "embedded": true,
    "otp_verification": {
      "enabled": true,
      "channel": "whatsapp"
    }
  }]
}

El firmante recibe un código de 6 dígitos que debe ingresar antes de poder firmar. El código expira en 10 minutos y tiene máximo 5 intentos.


Checklist de seguridad

Antes de ir a producción con firma embebida, verifica:

  • Tu secret key (allsign_live_sk_*) está en variables de entorno del backend — nunca en el frontend, nunca commiteada al repo
  • Las Signing Sessions se crean server-side con tu secret key — el frontend solo recibe el client_secret
  • El client_secret se crea justo antes de abrir el widget, no al cargar la página (TTL de 15 min)
  • El client_secret no se escribe a logs ni se guarda en storage cliente (localStorage, cookies, etc.)
  • Tu CSP permite frame-src https://sign.allsign.io
  • Si embedes IDV (escaneo de INE), tu iFrame tiene allow="camera"
  • Tienes webhooks configurados como fuente de verdad (no solo callbacks del SDK)
  • Si usas HMAC en webhooks, verificas la firma en cada request
  • Las acciones críticas (pagos, activaciones) dependen del webhook o return URL, no del callback del SDK
  • Para documentos sensibles, tienes verificación de identidad habilitada
  • En producción, test_mode está desactivado

Siguiente paso

  • Quickstart — Implementa tu primera firma embebida
  • Webhooks — Configura y verifica webhooks con HMAC

Was this page helpful?