Skip to Content

API del Actions Engine

Las acciones son fragmentos de código JavaScript personalizado que se ejecutan en puntos específicos durante los flujos de autenticación. Permiten extender Auris con lógica personalizada sin modificar la plataforma central: enriquecer tokens con datos externos, bloquear registros sospechosos, aplicar políticas de contraseñas personalizadas o sincronizar datos de usuarios con sistemas externos.

Las acciones se ejecutan en un entorno sandbox con alcance restringido. Cada acción está asociada a un punto de disparo (p. ej., post_login) y se ejecuta en orden de prioridad. Se pueden adjuntar múltiples acciones al mismo disparador.

Todos los endpoints de acciones requieren el encabezado x-tenant. Crear y gestionar acciones requiere acceso de nivel administrador (implícito en el permiso admin:all o en el permiso manage:actions).

Ciclo de Vida de una Acción

  1. Crea una acción con un tipo de disparador y código JavaScript.
  2. Prueba la acción revisando los registros de ejecución.
  3. Establece el estado de la acción en active para habilitarla en producción.
  4. Monitorea la ejecución a través del endpoint de registros.

CRUD de Acciones

Listar Acciones

GET/api/actionsRequires: manage:actions

Lista todas las acciones del tenant. Devuelve metadatos de la acción incluyendo tipo de disparador, estado, estadísticas de ejecución y orden. Las acciones se ejecutan en orden ascendente del campo order para cada tipo de disparador.

Parámetros de consulta

ParámetroTipoDescripción
pageintegerNúmero de página (por defecto: 1)
limitintegerElementos por página (por defecto: 20)
triggerstringFiltrar por tipo de disparador (p. ej., post_login)
statusactive | inactiveFiltrar por estado

Respuesta exitosa

{ "ok": true, "data": { "data": [ { "id": "act_abc123", "name": "Enrich Token with CRM Data", "trigger": "post_login", "status": "active", "order": 1, "timeout": 5000, "executionCount": 14523, "errorCount": 12, "lastExecutedAt": "2025-02-18T09:50:00Z", "createdAt": "2025-01-10T10:00:00Z", "updatedAt": "2025-02-15T14:00:00Z" }, { "id": "act_def456", "name": "Block Disposable Emails", "trigger": "pre_signup", "status": "active", "order": 1, "timeout": 3000, "executionCount": 892, "errorCount": 0, "lastExecutedAt": "2025-02-18T08:30:00Z", "createdAt": "2025-01-20T10:00:00Z", "updatedAt": "2025-01-20T10:00:00Z" } ], "pagination": { "page": 1, "limit": 20, "total": 5, "totalPages": 1 } } }

Crear Acción

POST/api/actionsRequires: manage:actions

Crea una nueva acción. La acción se crea con estado inactive por defecto. Cámbiala a active a través del endpoint de alternancia después de probarla. El código JavaScript se valida en busca de patrones bloqueados antes de almacenarse.

Cuerpo de la solicitud

{ "name": "Enrich Token with CRM Data", "trigger": "post_login", "code": "async function handler(context, api) {\n const response = await api.fetch('https://crm.example.com/api/user', {\n headers: { 'X-User-Email': context.user.email }\n });\n const crmData = await response.json();\n api.setCustomClaim('crm_id', crmData.id);\n api.setCustomClaim('account_tier', crmData.tier);\n}", "order": 1, "timeout": 5000 }
CampoTipoRequeridoDescripción
namestringSíNombre legible por humanos
triggerstringSíPunto de disparo (ver Tipos de Disparador)
codestringSíCuerpo de la función JavaScript
orderintegerNoOrden de ejecución dentro del disparador (por defecto: 0, menor = primero)
timeoutintegerNoTiempo máximo de ejecución en milisegundos (por defecto: 5000, máx: 10000)

Respuesta exitosa

{ "ok": true, "data": { "id": "act_ghi789", "name": "Enrich Token with CRM Data", "trigger": "post_login", "status": "inactive", "code": "async function handler(context, api) { ... }", "order": 1, "timeout": 5000, "executionCount": 0, "errorCount": 0, "createdAt": "2025-02-18T10:00:00Z" } }

Códigos de error

CódigoHTTPDescripción
VALIDATION_ERROR400Faltan campos requeridos o el tipo de disparador es inválido
BLOCKED_PATTERN400El código contiene un patrón bloqueado (ver Restricciones del Sandbox)
CODE_TOO_LARGE400El código supera el tamaño máximo permitido

Obtener Acción

GET/api/actions/[id]Requires: manage:actions

Recupera una acción por ID, incluyendo su código completo y estadísticas de ejecución.

Respuesta exitosa

{ "ok": true, "data": { "id": "act_abc123", "name": "Enrich Token with CRM Data", "trigger": "post_login", "status": "active", "code": "async function handler(context, api) {\n const response = await api.fetch('https://crm.example.com/api/user', {\n headers: { 'X-User-Email': context.user.email }\n });\n const crmData = await response.json();\n api.setCustomClaim('crm_id', crmData.id);\n api.setCustomClaim('account_tier', crmData.tier);\n}", "order": 1, "timeout": 5000, "executionCount": 14523, "errorCount": 12, "lastExecutedAt": "2025-02-18T09:50:00Z", "createdAt": "2025-01-10T10:00:00Z", "updatedAt": "2025-02-15T14:00:00Z" } }

Actualizar Acción

PUT/api/actions/[id]Requires: manage:actions

Actualiza el nombre, código, disparador, orden o tiempo de espera de una acción. Todos los campos son opcionales — solo se actualizan los campos proporcionados. El código actualizado se revalida en busca de patrones bloqueados.

Cuerpo de la solicitud

{ "name": "Enrich Token with CRM Data v2", "code": "async function handler(context, api) {\n // Lógica actualizada\n const data = await api.fetch('https://crm.example.com/v2/user/' + context.user.id);\n const user = await data.json();\n api.setCustomClaim('crm_id', user.id);\n}", "timeout": 8000 }

Respuesta exitosa

{ "ok": true, "data": { "id": "act_abc123", "name": "Enrich Token with CRM Data v2", "trigger": "post_login", "status": "active", "code": "async function handler(context, api) { ... }", "order": 1, "timeout": 8000, "updatedAt": "2025-02-18T11:00:00Z" } }

Eliminar Acción

DELETE/api/actions/[id]Requires: manage:actions

Elimina una acción de forma permanente. La acción se elimina inmediatamente de la cadena de ejecución. Los registros de ejecución de esta acción se conservan.

Respuesta exitosa

{ "ok": true, "data": { "deleted": true } }

Alternar Estado de Acción

PATCH/api/actions/[id]Requires: manage:actions

Alterna una acción entre los estados active e inactive. Solo las acciones activas se ejecutan durante los flujos de autenticación.

Cuerpo de la solicitud

{ "status": "active" }

Valores válidos: active, inactive.

Respuesta exitosa

{ "ok": true, "data": { "id": "act_abc123", "status": "active", "updatedAt": "2025-02-18T11:30:00Z" } }

Registros de Ejecución

GET/api/actions/[id]/logsRequires: manage:actions

Recupera los registros de ejecución de una acción específica. Cada entrada de registro indica si la ejecución fue exitosa o fallida, la duración y cualquier mensaje de error. Los registros se ordenan por marca de tiempo descendente.

Parámetros de consulta

ParámetroTipoDescripción
pageintegerNúmero de página (por defecto: 1)
limitintegerElementos por página (por defecto: 20)

Respuesta exitosa

{ "ok": true, "data": { "data": [ { "id": "log_abc123", "actionId": "act_abc123", "trigger": "post_login", "status": "success", "duration": 234, "userId": "usr_xyz789", "createdAt": "2025-02-18T09:50:00Z" }, { "id": "log_def456", "actionId": "act_abc123", "trigger": "post_login", "status": "error", "duration": 5001, "error": "Action timed out after 5000ms", "userId": "usr_abc123", "createdAt": "2025-02-18T09:48:00Z" }, { "id": "log_ghi789", "actionId": "act_abc123", "trigger": "post_login", "status": "error", "duration": 112, "error": "TypeError: Cannot read property 'email' of undefined", "userId": "usr_def456", "createdAt": "2025-02-18T09:45:00Z" } ], "pagination": { "page": 1, "limit": 20, "total": 14535, "totalPages": 727 } } }

Valores de estado del registro: success, error.

Tipos de Disparador

Las acciones pueden adjuntarse a uno de los seis puntos de disparo en la cadena de autenticación:

DisparadorSe activa cuandoCasos de uso
pre_loginAntes de que se intente la autenticación en KeycloakBloquear login desde IPs o dominios de email específicos, limitación de velocidad personalizada
post_loginDespués de una autenticación exitosa, antes de emitir tokensEnriquecer tokens con datos externos, registrar analíticas personalizadas, sincronizar con CRM
pre_signupAntes de crear una nueva cuenta de usuarioBloquear emails desechables, aplicar validaciones personalizadas, verificar listas de bloqueo externas
post_signupDespués de crear una nueva cuenta de usuarioEnviar notificación de bienvenida, crear registros en sistemas externos, asignar roles predeterminados
post_change_passwordDespués de que un usuario cambia su contraseñaInvalidar credenciales en caché, notificar sistemas externos, registro de auditoría
pre_m2m_tokenAntes de emitir un token M2MValidar scopes del cliente, agregar claims personalizados, aplicar restricciones de horario

Orden de Ejecución

Cuando múltiples acciones activas comparten el mismo disparador, se ejecutan secuencialmente en orden ascendente de order. Si una acción falla (lanza un error o supera el tiempo de espera), las acciones siguientes para ese disparador aún se ejecutan, a menos que la acción fallida deniegue explícitamente la solicitud.

Objeto ActionContext

Cada acción recibe un objeto context como primer argumento. La forma varía según el tipo de disparador.

pre_login / post_login

{ user: { id: "usr_abc123", email: "[email protected]", username: "alice", firstName: "Alice", lastName: "Smith", roles: ["editor", "viewer"], emailVerified: true, phoneNumber: "+39021234567", phoneNumberVerified: true, metadata: {} }, connection: { method: "password", // "password" | "magic_link" | "social" | "sso" provider: null, // nombre del proveedor social (p. ej., "google") o null ipAddress: "203.0.113.50", userAgent: "Mozilla/5.0 ...", timestamp: "2025-02-18T10:00:00Z" }, tenant: "acme-corp" }

Para pre_login, el objeto user puede ser null si el usuario aún no ha sido resuelto (p. ej., email incorrecto). Los campos connection.method y connection.ipAddress siempre están disponibles.

pre_signup / post_signup

{ user: { email: "[email protected]", username: "newuser", firstName: "New", lastName: "User" }, connection: { method: "password", ipAddress: "203.0.113.50", userAgent: "Mozilla/5.0 ...", timestamp: "2025-02-18T10:00:00Z" }, tenant: "acme-corp" }

Para post_signup, el objeto user también incluye id y roles.

post_change_password

{ user: { id: "usr_abc123", email: "[email protected]" }, tenant: "acme-corp" }

pre_m2m_token

{ application: { id: "app_xyz789", name: "Backend Service", clientId: "m2m-client-id", type: "M2M" }, requestedScopes: ["read:users", "manage:roles"], tenant: "acme-corp" }

ActionResult (Objeto API)

El segundo argumento que se pasa a las acciones es el objeto api, que provee métodos para influir en el flujo de autenticación:

MétodoDisponible enDescripción
api.setCustomClaim(key, value)post_login, pre_m2m_tokenAñade un claim personalizado al token de acceso
api.setMetadata(key, value)post_login, post_signupEstablece metadatos del usuario (persistidos en la base de datos)
api.deny(reason)pre_login, pre_signup, pre_m2m_tokenDeniega el intento de autenticación con una razón
api.log(message)Todos los disparadoresEscribe un mensaje en el registro de ejecución de la acción
api.fetch(url, options)Todos los disparadoresRealiza una solicitud HTTP (fetch restringido con tiempo de espera)

Ejemplo: Denegar Registro por Emails Desechables

async function handler(context, api) { const disposableDomains = ['tempmail.com', 'throwaway.email', 'guerrillamail.com']; const domain = context.user.email.split('@')[1]; if (disposableDomains.includes(domain)) { api.deny('Las direcciones de email desechables no están permitidas'); return; } api.log('Registro permitido para el dominio: ' + domain); }

Ejemplo: Enriquecer Token Después del Login

async function handler(context, api) { try { const response = await api.fetch('https://crm.example.com/api/lookup', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer crm-api-key' }, body: JSON.stringify({ email: context.user.email }) }); if (response.ok) { const data = await response.json(); api.setCustomClaim('crm_id', data.customerId); api.setCustomClaim('plan', data.subscriptionPlan); api.log('Token enriquecido: plan=' + data.subscriptionPlan); } else { api.log('Consulta CRM fallida: ' + response.status); } } catch (error) { api.log('Error en consulta CRM: ' + error.message); // No denegar el login si falla el enriquecimiento } }

Ejemplo: Bloquear Token M2M Fuera del Horario Laboral

async function handler(context, api) { var hour = new Date().getUTCHours(); if (hour < 6 || hour > 22) { api.deny('Los tokens M2M no pueden emitirse fuera del horario laboral (06:00-22:00 UTC)'); return; } api.log('Token M2M emitido para ' + context.application.name + ' a la hora UTC ' + hour); }

Restricciones del Sandbox

Las acciones se ejecutan en un entorno sandbox con alcance restringido. Los siguientes patrones se detectan y bloquean durante la validación del código (en creación y actualización). El código que contiene alguno de estos patrones se rechaza con un error BLOCKED_PATTERN:

  • require( — sin importaciones de módulos CommonJS
  • import — sin importaciones de módulos ES
  • process. — sin acceso al objeto process de Node.js
  • child_process — sin ejecución de shell
  • fs. / fs/promises — sin acceso al sistema de archivos
  • global. / globalThis. — sin acceso al alcance global
  • Patrones de evaluación dinámica de código — sin generación de código en tiempo de ejecución a partir de cadenas

El método api.fetch() se proporciona como alternativa segura a las bibliotecas HTTP externas. Soporta los métodos GET, POST, PUT, PATCH y DELETE con cuerpos JSON o de texto. El tiempo de espera se hereda de la configuración timeout de la acción.

Límites de Ejecución

LímiteValor
Tiempo máximo de ejecuciónConfigurable por acción (por defecto 5000ms, máx 10000ms)
Tamaño máximo del código64 KB
Tamaño máximo de respuesta de api.fetch()1 MB
Globales disponiblesJSON, Date, Math, String, Number, Array, Object, Map, Set, Promise, RegExp, console.log (redirigido a api.log)

Manejo de Errores

Cuando una acción lanza un error no manejado o supera el tiempo de espera:

  1. El error se registra en el registro de ejecución de la acción.
  2. El errorCount de la acción se incrementa.
  3. El flujo de autenticación continúa (las acciones no bloquean la autenticación por defecto a menos que se llame a api.deny()).
  4. Si la acción es crítica, usa api.deny() explícitamente en tu manejador de errores.

Los errores de las acciones no bloquean la autenticación por defecto. Si necesitas que una acción fallida impida el login (p. ej., una verificación de cumplimiento), debes llamar a api.deny() explícitamente en tu bloque catch. De lo contrario, el usuario será autenticado aunque la acción falle.

Editor Visual Blueprint

La Consola de Auris incluye un editor visual de nodos (Blueprint Editor) para crear acciones mediante una interfaz de arrastrar y soltar en lugar de escribir JavaScript. El Blueprint Editor genera definiciones de reglas en JSON que se compilan a JavaScript equivalente en tiempo de ejecución.

El Blueprint Editor es una alternativa al editor de código — ambos producen el mismo resultado. Las acciones creadas con el Blueprint Editor pueden verse y editarse como código, y viceversa.

Consulta la documentación de la Consola para obtener detalles sobre el Blueprint Editor.


Relacionado