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:
| Credencial | Prefijo | Dónde se usa | Alcance | Vida |
|---|---|---|---|---|
| Secret key | allsign_live_sk_ | Solo tu backend (server-side) | Crear documentos, signing sessions, leer datos, verificar firmas | Permanente (hasta que la rotes) |
| Client secret | as_sess_..._secret_... | Tu frontend (browser) | Montar UNA sola session de firma | 15 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
¿Por qué no hay una "publishable key" como Stripe? Porque no aporta nada en nuestro modelo. Stripe necesita una pk para permitir que el browser tokenice datos sensibles (tarjetas, cuentas bancarias) antes de que exista una PaymentIntent — la pk autentica ese momento "pre-session". En AllSign el browser nunca crea nada antes de una session: siempre es tu backend el que genera la session con la secret key, y el frontend recibe un client_secret ya scoped a esa session específica. El client_secret ya hace todo el trabajo que una pk haría: identifica al tenant, restringe las acciones permitidas, y es seguro para el browser porque expira rápido y es single-use. Agregar una pk sería ceremonia sin beneficio — y Plaid, que tenía exactamente ese modelo híbrido, lo eliminó en 2020 por la misma razón.
Signing Sessions (client_secret)
El client_secret generado por POST /v2/signing-sessions tiene estas propiedades:
| Propiedad | Valor |
|---|---|
| Tiempo de vida | 15 minutos desde la generación |
| Uso | Single-use — una vez que se abre, el token se consume |
| Almacenamiento | El token se hashea con SHA-256 antes de guardarse en DB |
| Contenido | Session ID + firmante + documento + timestamp + nonce encriptado |
| Revocación | Se invalida automáticamente si el documento cambia de estado |
| Alcance | Solo 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_secreten 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...
});
El SDK maneja toda la validación de origen internamente. Tú solo interactúas con la API de eventos (session.on()). Nunca necesitas escuchar postMessage directamente.
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_secretse crea justo antes de abrir el widget, no al cargar la página (TTL de 15 min) - El
client_secretno 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_modeestá desactivado
Siguiente paso
- Quickstart — Implementa tu primera firma embebida
- Webhooks — Configura y verifica webhooks con HMAC

