Skip to Content

Escribir Actions Personalizadas

Las Actions son funciones JavaScript personalizadas que se ejecutan en puntos específicos del pipeline de autenticación de Auris. Permiten extender el comportamiento de la plataforma sin bifurcar ni modificar Auris en sí — añade claims personalizados a los tokens, bloquea inicios de sesión basándose en datos externos, llama a webhooks, aplica reglas de negocio o integra con servicios de terceros.

Cuándo usar Actions frente a otros puntos de extensión

MecanismoIdeal para
ActionsLógica que debe ejecutarse de forma síncrona durante el flujo de autenticación (bloquear un inicio de sesión, añadir claims, modificar metadatos de usuario)
Custom ClaimsEnriquecimiento de tokens estático o basado en atributos que no requiere llamadas externas o lógica condicional
WebhooksNotificaciones asíncronas después de que ocurran eventos (registro de auditoría, analíticas, alertas en Slack)

Si tu caso de uso requiere una llamada a una API externa que debe completarse con éxito antes de que se complete el inicio de sesión, usa una Action. Si solo necesitas notificar a un sistema después del hecho, usa un Webhook.


El Entorno Sandbox

Las Actions se ejecutan en un sandbox de JavaScript restringido. El sandbox proporciona:

APIs Disponibles

APINotas
fetch()Realizar solicitudes HTTP a servicios externos. API Fetch completa.
JSONJSON.parse() y JSON.stringify()
DateConstrucción y manipulación de fechas
MathOperaciones matemáticas
console.log()La salida se captura y es visible en los logs de la action
String, Number, Boolean, Array, ObjectTipos integrados estándar
Promise, async/awaitEl código asíncrono es completamente compatible
URL, URLSearchParamsAnálisis y construcción de URLs
TextEncoder, TextDecoderUtilidades de codificación de texto
crypto.randomUUID()Generación de UUIDs

APIs Bloqueadas

Las siguientes están bloqueadas por seguridad:

BloqueadoMotivo
require(), importSin acceso al sistema de módulos
processSin acceso a variables de entorno ni información del proceso
Funciones de ejecución de código dinámicoSin generación de código en tiempo de ejecución
global, globalThisSin acceso al ámbito global
fs, child_processSin sistema de archivos ni creación de procesos

Las Actions tienen un tiempo de espera de ejecución configurable (por defecto: 5 segundos, máximo: 30 segundos). Si una action supera el tiempo de espera, se termina y se aplica el comportamiento de fallo configurado (permitir o denegar la solicitud).


Tipos de Disparadores

Las Actions se activan en puntos específicos del flujo de autenticación. Cada disparador recibe un objeto context diferente:

DisparadorCuándo se activaUsos comunes
pre-loginAntes de que se verifiquen las credencialesBloquear inicios de sesión por dominio de email, comprobar listas de bloqueo externas
post-loginTras una autenticación exitosa, antes de la emisión de tokensAñadir claims personalizados, sincronizar con sistemas externos, MFA condicional
pre-signupAntes de que se cree un nuevo usuarioValidar dominio de email, comprobar requisitos de invitación
post-signupDespués de que se crea un nuevo usuarioEnviar webhook de bienvenida, asignar a organización, establecer metadatos
post-change-passwordDespués de un cambio de contraseñaNotificar a sistemas externos, invalidar sesiones en caché
pre-m2m-tokenAntes de que se emita un token M2MRestringir scopes, validar el cliente contra reglas externas

El Objeto Context

Cada action recibe un objeto context con información sobre la solicitud actual. La forma varía según el disparador:

pre-login y post-login

{ user: { id: 'user-123', email: '[email protected]', username: 'alice', firstName: 'Alice', lastName: 'Smith', roles: ['editor', 'viewer'], metadata: { plan: 'pro', company: 'Acme' }, }, request: { ip: '203.0.113.42', userAgent: 'Mozilla/5.0...', geoip: { country: 'IT', city: 'Milan' }, }, application: { id: 'app-456', name: 'Dashboard', type: 'WEB', }, tenant: { id: 'tenant-789', name: 'acme-corp', }, }

pre-m2m-token

{ client: { id: 'client-abc', name: 'Billing Service', type: 'M2M', }, requestedScopes: ['read:users', 'manage:billing'], tenant: { id: 'tenant-789', name: 'acme-corp' }, }

El Tipo de Retorno ActionResult

Las Actions devuelven un objeto que controla el resultado:

interface ActionResult { allow: boolean // true = proceder, false = bloquear la solicitud message?: string // Mensaje de error mostrado al usuario cuando allow=false claims?: Record<string, any> // Claims personalizados para añadir al token (post-login, pre-m2m-token) metadata?: Record<string, any> // Metadatos para establecer en el registro del usuario }

Si una action no devuelve un resultado (o devuelve undefined), la solicitud procede con normalidad.


Ejemplos Prácticos

Bloquear inicios de sesión por dominio de email

// Disparador: pre-login // Bloquear proveedores de email personal para que no inicien sesión const blockedDomains = ['gmail.com', 'yahoo.com', 'hotmail.com', 'outlook.com'] const domain = context.user.email.split('@')[1] if (blockedDomains.includes(domain)) { return { allow: false, message: 'Las direcciones de email personales no están permitidas. Por favor, usa tu email corporativo.', } } return { allow: true }

Añadir claims personalizados basados en roles

// Disparador: post-login // Añadir un claim 'plan' y banderas de funcionalidades basadas en metadatos de usuario const plan = context.user.metadata?.plan || 'free' const featureFlags = { free: { maxProjects: 3, analytics: false, exportEnabled: false }, pro: { maxProjects: 50, analytics: true, exportEnabled: true }, enterprise: { maxProjects: -1, analytics: true, exportEnabled: true }, } return { allow: true, claims: { plan, features: featureFlags[plan] || featureFlags.free, 'https://myapp.com/roles': context.user.roles, }, }

Registrar eventos de autenticación en un webhook externo

// Disparador: post-login // Enviar una notificación webhook en cada inicio de sesión try { await fetch('https://hooks.example.com/auth-events', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ event: 'user.login', userId: context.user.id, email: context.user.email, ip: context.request.ip, country: context.request.geoip?.country, timestamp: new Date().toISOString(), }), }) } catch (err) { // No crítico -- registrar pero no bloquear el inicio de sesión console.log('Webhook fallido:', err.message) } return { allow: true }

Restringir scopes M2M basándose en políticas externas

// Disparador: pre-m2m-token // Consultar un servicio de políticas externo antes de emitir tokens M2M const response = await fetch('https://policy.internal/check', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ clientId: context.client.id, scopes: context.requestedScopes, }), }) if (!response.ok) { return { allow: false, message: 'Solicitud de token denegada por el servicio de políticas.', } } const policy = await response.json() return { allow: true, claims: { scope: policy.allowedScopes.join(' '), 'policy-version': policy.version, }, }

Disparador MFA condicional

// Disparador: post-login // Requerir MFA para roles de administrador o inicios de sesión desde nuevos países const isAdmin = context.user.roles.includes('admin') const isNewCountry = context.request.geoip?.country !== context.user.metadata?.lastCountry if (isAdmin || isNewCountry) { return { allow: true, metadata: { lastCountry: context.request.geoip?.country, requireMfa: true, }, } } return { allow: true, metadata: { lastCountry: context.request.geoip?.country }, }

Orden de Ejecución

Cuando se configuran múltiples actions para el mismo disparador, se ejecutan en el orden definido por su campo order (ascendente). Cada action recibe el mismo contexto original — la salida de una action no modifica el contexto para las acciones posteriores.

Sin embargo, los claims y metadatos de todas las actions se fusionan. Si dos actions establecen la misma clave de claim, la última action en el orden gana.

Orden de acciónNombre de la acciónSalida de claims
1Añadir claim de plan{ plan: 'pro' }
2Añadir banderas de funcionalidades{ features: { ... } }
3Añadir claim de región{ region: 'eu' }

Claims finales del token: { plan: 'pro', features: { ... }, region: 'eu' }

Si alguna action devuelve allow: false, el pipeline completo se detiene y la solicitud se deniega, independientemente de las acciones posteriores.


El Editor Visual Blueprint

Para equipos que prefieren un enfoque visual, Auris ofrece un editor Blueprint — un constructor de reglas visuales basado en nodos inspirado en el sistema Blueprint de Unreal Engine.

El editor Blueprint representa las actions como un grafo de nodos conectados:

Tipo de nodoColorPropósito
DisparadorNaranjaEl punto de entrada — qué evento de autenticación activa la regla
CondiciónCianComprobar un campo contra un valor (p. ej., user.email contiene @acme.com)
Puerta lógicaVioletaCombinar condiciones con AND/OR
DenegarRosaBloquear la solicitud con un mensaje de error
Establecer claimsAzulAñadir pares clave-valor al token
Establecer metadatosEsmeraldaActualizar metadatos del usuario
LogVerdeEscribir en los logs de la action

El editor Blueprint y el editor de código son dos vistas de la misma action subyacente. Los cambios en uno se sincronizan con el otro. Puedes empezar con Blueprint para reglas simples y cambiar al código para lógica compleja.

Para acceder al editor Blueprint, abre la página de detalle de una action en la Consola y haz clic en la pestaña Blueprint.


Depuración de Actions

Logs de Actions

Cada ejecución de una action queda registrada. Ver los logs en Consola -> Actions -> [Nombre de la action] -> pestaña Logs. Cada entrada del log incluye:

  • Marca de tiempo de ejecución
  • Evento disparador
  • Duración de ejecución (ms)
  • Estado (éxito, error, timeout)
  • Salida de console.log
  • Valor de retorno

Usar console.log

La salida de console.log() se captura y almacena en el log de la action. Úsalo para depurar:

console.log('Roles del usuario:', JSON.stringify(context.user.roles)) console.log('IP de la solicitud:', context.request.ip) console.log('Datos GeoIP:', JSON.stringify(context.request.geoip)) // Esta salida aparece en la pestaña Logs de la action return { allow: true }

Manejo de Errores

Si una action lanza un error no manejado, el comportamiento depende del ajuste de modo de fallo de la action:

Modo de falloComportamiento
allow (por defecto)La solicitud procede. El error se registra.
denyLa solicitud se bloquea con un mensaje de error genérico.

Usa siempre try/catch alrededor de las llamadas a APIs externas para evitar que los fallos bloqueen los inicios de sesión:

try { const result = await fetch('https://external-api.example.com/check') // procesar resultado } catch (err) { console.log('API externa fallida, procediendo:', err.message) // No bloquear el inicio de sesión porque un servicio externo esté caído } return { allow: true }

Consejos de Rendimiento

  1. Mantén las actions rápidas — Apunta a menos de 100ms de tiempo de ejecución. Cada milisegundo añade latencia al inicio de sesión.
  2. Usa try/catch para llamadas externas — Nunca dejes que el fallo de una API externa no crítica bloquee un inicio de sesión.
  3. Evita llamadas externas secuenciales — Si necesitas múltiples llamadas a la API, usa Promise.all() para ejecutarlas en paralelo.
  4. Caché de búsquedas costosas — Para datos que cambian poco frecuentemente, considera almacenar en caché en los metadatos del usuario y actualizar periódicamente en lugar de consultar en cada inicio de sesión.
  5. Establece timeouts apropiados — Usa AbortController con fetch() para establecer timeouts explícitos más cortos que el timeout de la action:
const controller = new AbortController() setTimeout(() => controller.abort(), 3000) // timeout de 3 segundos const response = await fetch('https://slow-api.example.com/check', { signal: controller.signal, })

Endpoints de API

GET/api/actionsRequires: view:actions

Lista todas las actions para el tenant. Soporta filtrado por tipo de disparador y estado.

POST/api/actionsRequires: manage:actions

Crea una nueva action. Requiere name, trigger, code y opcionales order, timeout, status.

GET/api/actions/:idRequires: view:actions

Obtiene los detalles de una action incluyendo código, disparador, recuento de ejecuciones y recuento de errores.

PATCH/api/actions/:idRequires: manage:actions

Actualiza el código, disparador, orden, timeout o estado de una action.

DELETE/api/actions/:idRequires: manage:actions

Elimina una action.

GET/api/actions/:id/logsRequires: view:actions

Lista los logs de ejecución de una action específica. Incluye duración, estado, salida de consola y valor de retorno.


Permisos Requeridos

OperaciónPermiso
Ver actionsview:actions
Crear, editar, eliminar actionsmanage:actions
Ver logs de actionsview:actions

Guías Relacionadas