@ivt-argos/facial-liveness-sdk
v1.0.0
Published
SDK de captura facial con prueba de vida (liveness) y firmado criptográfico seguro
Maintainers
Readme
@tu-org/facial-liveness-sdk
SDK de captura facial con prueba de vida (liveness) mejorada y firmado criptográfico seguro.
Corre 100% en el navegador usando MediaPipe. No envía video a ningún servidor externo.
Flujo de seguridad
App Web SDK (browser) Backend
│ │ │
│── new LivenessSDK(...) ──►│ │
│ │── POST /verify/ │
│ │ session-token ───────►│
│ │◄── { session_token } ───│
│ │ │
│ │ [MediaPipe local] │
│ │ Parpadeo detectado │
│ │ Giro detectado │
│ │ Anti-spoof OK │
│ │ Captura frame │
│ │ │
│ │── POST /verify ────────►│
│ │ { image, timestamp, │
│ │ nonce, signature, │ ← firmado con session_token
│ │ session_token } │ ← token se destruye aquí
│ │◄── { autenticado, ... } │
│◄── resultado ─────────────│ │La SECRET_KEY nunca sale del servidor. El browser solo recibe un token temporal de 3 minutos.
Instalación
npm install @tu-org/facial-liveness-sdkUso básico (React)
import { useRef } from 'react'
import { LivenessSDK } from '@tu-org/facial-liveness-sdk'
function VerificacionFacial({ userId, authToken }) {
const containerRef = useRef(null)
const iniciarVerificacion = async () => {
const sdk = new LivenessSDK({
container: containerRef.current,
backendUrl: 'http://localhost:8000',
userId,
authToken, // JWT del usuario logueado
onStateChange: (estado) => {
console.log('Estado:', estado)
},
})
try {
const resultado = await sdk.iniciar()
console.log('Autenticado:', resultado.autenticado)
console.log('Similitud:', resultado.confianza_similitud)
} catch (err) {
console.error('Error:', err.code, err.message)
}
}
return (
<div>
{/* El SDK inyecta toda la UI aquí */}
<div ref={containerRef} style={{ maxWidth: 480 }} />
<button onClick={iniciarVerificacion}>
Iniciar Verificación
</button>
</div>
)
}Parámetros del constructor
| Parámetro | Tipo | Requerido | Descripción |
|-----------|------|-----------|-------------|
| container | HTMLElement | ✅ | Div vacío donde montar la UI |
| backendUrl | string | ✅ | URL base del backend (sin slash final) |
| userId | string | ✅ | ID del usuario a verificar |
| authToken | string | ⚠️ Recomendado | JWT del usuario para autenticar el request del token |
| onStateChange | Function | No | Callback con el estado actual del flujo |
| config.maxReintentos | number | No | Reintentos ante error de red (default: 2) |
Estados del flujo (LIVENESS_STATE)
import { LIVENESS_STATE } from '@tu-org/facial-liveness-sdk'
// INICIANDO → Abriendo cámara y cargando modelo
// RETO_PARPADEO → Esperando que el usuario parpadee
// RETO_GIRO → Esperando giro de cabeza a la derecha
// VERIFICANDO → Analizando anti-spoof (textura facial)
// APROBADO → Liveness pasado, enviando al backend
// RECHAZADO → Falló algún reto o se detectó suplantación
// ERROR → Error técnico (cámara, red, etc.)Manejo de errores
import { SDKError } from '@tu-org/facial-liveness-sdk'
try {
await sdk.iniciar()
} catch (err) {
if (err instanceof SDKError) {
switch (err.code) {
case 'CAMERA_ERROR': // Permiso denegado o sin cámara
case 'LIVENESS_TIMEOUT': // No completó los retos en 45s
case 'SPOOF_DETECTED': // Foto/pantalla detectada
case 'TOKEN_FETCH_FAILED':// No se pudo obtener token del backend
case 'NETWORK_ERROR': // Sin conexión al backend
case 'BACKEND_ERROR': // El backend rechazó la petición (4xx/5xx)
}
}
}Configuración del backend (addon requerido)
El SDK necesita que el backend exponga el endpoint POST /verify/session-token.
Copia src/backend_addon/session_tokens.py a tu proyecto backend e inclúyelo en main.py:
from app.session_tokens import router as session_router, validar_y_consumir_token
app.include_router(session_router)Y en el endpoint /verify, reemplaza la validación de firma por:
from app.session_tokens import validar_y_consumir_token
# En lugar de verificar_firma():
session_token = body.get("session_token")
if not validar_y_consumir_token(session_token, user_id):
return {"statusCode": 403, "error": "Token de sesión inválido o expirado"}Build local
npm install
npm run build # genera dist/index.esm.js y dist/index.cjs.js
npm run dev # watch mode
npm test # tests unitarios con VitestNiveles de liveness implementados
| Reto | Qué detecta | Ataque que previene | |------|-------------|---------------------| | Parpadeo (EAR) | Ojos reales que se cierran y abren | Fotos impresas estáticas | | Giro de cabeza | Movimiento tridimensional | Pantallas planas 2D | | Anti-spoof (textura) | Varianza de píxeles en región facial | Fotos de alta resolución, deepfakes simples | | Análisis de iris | Asimetría de reflejo entre ojos | Pantallas con luz uniforme |
Notas de seguridad
- La
SECRET_KEYnunca llega al browser - Los tokens de sesión expiran en 3 minutos y son de un solo uso
- El video nunca se graba ni se envía a ningún servidor — solo un frame JPEG al aprobar
- HTTPS es obligatorio en producción (el browser bloquea
getUserMediasin TLS)
