API Actions Engine
Le Actions sono snippet di codice JavaScript personalizzato che si eseguono in punti specifici durante i flussi di autenticazione. Consentono di estendere Auris con logica personalizzata senza modificare la piattaforma core: arricchire i token con dati esterni, bloccare registrazioni sospette, applicare policy di password personalizzate o sincronizzare i dati utente con sistemi esterni.
Le Actions vengono eseguite in un ambiente sandboxed con scope limitato. Ogni action è associata a un punto di trigger (es. post_login) e viene eseguita in ordine di priorità. Più action possono essere collegate allo stesso trigger.
Tutti gli endpoint delle actions richiedono l’header x-tenant. La creazione e gestione delle actions richiede accesso a livello admin (implicato dal permesso admin:all o dal permesso manage:actions).
Ciclo di Vita di un’Action
- Crea un’action con un tipo di trigger e codice JavaScript.
- Testa l’action esaminando i log di esecuzione.
- Imposta lo stato dell’action su
activeper abilitarla in produzione. - Monitora l’esecuzione tramite l’endpoint dei log.
CRUD Actions
Elenco Actions
/api/actionsRequires: manage:actionsElenca tutte le actions per il tenant. Restituisce i metadati dell’action incluso il tipo di trigger, lo stato, le statistiche di esecuzione e l’ordine. Le actions vengono eseguite in ordine ascendente del campo order per ogni tipo di trigger.
Parametri di query
| Parametro | Tipo | Descrizione |
|---|---|---|
page | integer | Numero di pagina (default: 1) |
limit | integer | Elementi per pagina (default: 20) |
trigger | string | Filtra per tipo di trigger (es. post_login) |
status | active | inactive | Filtra per stato |
Risposta di successo
{
"ok": true,
"data": {
"data": [
{
"id": "act_abc123",
"name": "Arricchisci Token con Dati CRM",
"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"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 5,
"totalPages": 1
}
}
}Crea Action
/api/actionsRequires: manage:actionsCrea una nuova action. L’action viene creata con stato inactive di default. Impostala su active tramite l’endpoint di toggle dopo i test. Il codice JavaScript viene validato per i pattern bloccati prima della memorizzazione.
Corpo della richiesta
{
"name": "Arricchisci Token con Dati CRM",
"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
}| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
name | string | Sì | Nome leggibile dall’utente |
trigger | string | Sì | Punto di trigger (vedi Tipi di Trigger) |
code | string | Sì | Corpo della funzione JavaScript |
order | integer | No | Ordine di esecuzione nel trigger (default: 0, più basso = primo) |
timeout | integer | No | Tempo massimo di esecuzione in millisecondi (default: 5000, max: 10000) |
Risposta di successo
{
"ok": true,
"data": {
"id": "act_ghi789",
"name": "Arricchisci Token con Dati CRM",
"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"
}
}Codici di errore
| Codice | HTTP | Descrizione |
|---|---|---|
VALIDATION_ERROR | 400 | Campi obbligatori mancanti o tipo di trigger non valido |
BLOCKED_PATTERN | 400 | Il codice contiene un pattern bloccato (vedi Restrizioni Sandbox) |
CODE_TOO_LARGE | 400 | Il codice supera la dimensione massima consentita |
Ottieni Action
/api/actions/[id]Requires: manage:actionsRecupera una singola action tramite ID, incluso il codice completo e le statistiche di esecuzione.
Aggiorna Action
/api/actions/[id]Requires: manage:actionsAggiorna il nome, il codice, il trigger, l’ordine o il timeout di un’action. Tutti i campi sono opzionali — vengono aggiornati solo i campi forniti. Il codice aggiornato viene ri-validato per i pattern bloccati.
Corpo della richiesta
{
"name": "Arricchisci Token con Dati CRM v2",
"code": "async function handler(context, api) {\n // Logica aggiornata\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
}Elimina Action
/api/actions/[id]Requires: manage:actionsElimina un’action definitivamente. L’action viene immediatamente rimossa dalla pipeline di esecuzione. I log di esecuzione per questa action vengono conservati.
Risposta di successo
{
"ok": true,
"data": { "deleted": true }
}Attiva/Disattiva Action
/api/actions/[id]Requires: manage:actionsAlterna un’action tra lo stato active e inactive. Solo le action attive vengono eseguite durante i flussi di autenticazione.
Corpo della richiesta
{
"status": "active"
}Valori validi: active, inactive.
Risposta di successo
{
"ok": true,
"data": {
"id": "act_abc123",
"status": "active",
"updatedAt": "2025-02-18T11:30:00Z"
}
}Log di Esecuzione
/api/actions/[id]/logsRequires: manage:actionsRecupera i log di esecuzione per un’action specifica. Ogni voce di log registra se l’esecuzione ha avuto successo o è fallita, la durata e i messaggi di errore. I log sono ordinati per timestamp in ordine decrescente.
Parametri di query
| Parametro | Tipo | Descrizione |
|---|---|---|
page | integer | Numero di pagina (default: 1) |
limit | integer | Elementi per pagina (default: 20) |
Risposta di successo
{
"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 scaduta dopo 5000ms",
"userId": "usr_abc123",
"createdAt": "2025-02-18T09:48:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 14535,
"totalPages": 727
}
}
}Valori di stato del log: success, error.
Tipi di Trigger
Le Actions possono essere collegate a uno dei sei punti di trigger nella pipeline di autenticazione:
| Trigger | Si attiva quando | Casi d’uso |
|---|---|---|
pre_login | Prima che venga tentata l’autenticazione Keycloak | Blocca il login da IP o domini email specifici, rate limiting personalizzato |
post_login | Dopo l’autenticazione riuscita, prima dell’emissione dei token | Arricchisci i token con dati esterni, log analytics personalizzati, sincronizza con CRM |
pre_signup | Prima della creazione di un nuovo account utente | Blocca email usa e getta, applica validazione personalizzata, controlla blocklist esterne |
post_signup | Dopo la creazione di un nuovo account utente | Invia notifica di benvenuto, crea record in sistemi esterni, assegna ruoli predefiniti |
post_change_password | Dopo che un utente cambia la propria password | Invalida credenziali in cache, notifica sistemi esterni, log di audit |
pre_m2m_token | Prima dell’emissione di un token M2M | Valida scope client, aggiungi claim personalizzati, applica restrizioni orarie |
Ordine di Esecuzione
Quando più action attive condividono lo stesso trigger, vengono eseguite in sequenza in ordine ascendente del valore order. Se un’action fallisce (lancia un errore o va in timeout), le action successive per quel trigger vengono comunque eseguite, a meno che l’action fallita non neghi esplicitamente la richiesta.
Oggetto ActionContext
Ogni action riceve un oggetto context come primo argomento. La forma varia per tipo di trigger.
pre_login / post_login
{
user: {
id: "usr_abc123",
email: "[email protected]",
username: "alice",
firstName: "Alice",
lastName: "Rossi",
roles: ["editor", "viewer"],
emailVerified: true,
phoneNumber: "+39021234567",
phoneNumberVerified: true,
metadata: {}
},
connection: {
method: "password", // "password" | "magic_link" | "social" | "sso"
provider: null, // nome del social provider (es. "google") o null
ipAddress: "203.0.113.50",
userAgent: "Mozilla/5.0 ...",
timestamp: "2025-02-18T10:00:00Z"
},
tenant: "acme-corp"
}Per pre_login, l’oggetto user può essere null se l’utente non è ancora stato risolto (es. email errata). connection.method e connection.ipAddress sono sempre disponibili.
pre_signup / post_signup
{
user: {
email: "[email protected]",
username: "nuovoutente",
firstName: "Nuovo",
lastName: "Utente"
},
connection: {
method: "password",
ipAddress: "203.0.113.50",
userAgent: "Mozilla/5.0 ...",
timestamp: "2025-02-18T10:00:00Z"
},
tenant: "acme-corp"
}Per post_signup, l’oggetto user include anche id e roles.
post_change_password
{
user: {
id: "usr_abc123",
email: "[email protected]"
},
tenant: "acme-corp"
}pre_m2m_token
{
application: {
id: "app_xyz789",
name: "Servizio Backend",
clientId: "m2m-client-id",
type: "M2M"
},
requestedScopes: ["read:users", "manage:roles"],
tenant: "acme-corp"
}Oggetto ActionResult (API)
Il secondo argomento passato alle actions è l’oggetto api, che fornisce metodi per influenzare il flusso di autenticazione:
| Metodo | Disponibile in | Descrizione |
|---|---|---|
api.setCustomClaim(key, value) | post_login, pre_m2m_token | Aggiunge un claim personalizzato all’access token |
api.setMetadata(key, value) | post_login, post_signup | Imposta metadati utente (persistiti nel database) |
api.deny(reason) | pre_login, pre_signup, pre_m2m_token | Nega il tentativo di autenticazione con un motivo |
api.log(message) | Tutti i trigger | Scrive un messaggio nel log di esecuzione dell’action |
api.fetch(url, options) | Tutti i trigger | Effettua una richiesta HTTP (fetch con timeout limitato) |
Esempio: Nega Registrazione per Email Usa e Getta
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('Gli indirizzi email usa e getta non sono consentiti');
return;
}
api.log('Registrazione consentita per il dominio: ' + domain);
}Esempio: Arricchisci Token Dopo il 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 arricchito: plan=' + data.subscriptionPlan);
} else {
api.log('Ricerca CRM fallita: ' + response.status);
}
} catch (error) {
api.log('Errore ricerca CRM: ' + error.message);
// Non negare il login se l'arricchimento fallisce
}
}Esempio: Blocca Token M2M Fuori dall’Orario Lavorativo
async function handler(context, api) {
var hour = new Date().getUTCHours();
if (hour < 6 || hour > 22) {
api.deny('I token M2M non possono essere emessi fuori dall\'orario lavorativo (06:00-22:00 UTC)');
return;
}
api.log('Token M2M emesso per ' + context.application.name + ' all\'ora UTC ' + hour);
}Restrizioni Sandbox
Le Actions vengono eseguite in un ambiente sandboxed con scope limitato. I seguenti pattern vengono rilevati e bloccati al momento della validazione del codice (durante la creazione e l’aggiornamento). Il codice contenente uno di questi pattern viene rifiutato con un errore BLOCKED_PATTERN:
require(— nessun import di moduli CommonJSimport— nessun import di moduli ESprocess.— nessun accesso all’oggetto process di Node.jschild_process— nessuna esecuzione di shellfs./fs/promises— nessun accesso al filesystemglobal./globalThis.— nessun accesso allo scope globale- Pattern di valutazione dinamica del codice — nessuna generazione di codice a runtime da stringhe
Il metodo api.fetch() è fornito come alternativa sicura alle librerie HTTP esterne. Supporta i metodi GET, POST, PUT, PATCH e DELETE con body JSON o testo. Il timeout viene ereditato dall’impostazione timeout dell’action.
Limiti di Runtime
| Limite | Valore |
|---|---|
| Tempo massimo di esecuzione | Configurabile per action (default 5000ms, max 10000ms) |
| Dimensione massima del codice | 64 KB |
Dimensione massima risposta api.fetch() | 1 MB |
| Globali disponibili | JSON, Date, Math, String, Number, Array, Object, Map, Set, Promise, RegExp, console.log (reindirizzato a api.log) |
Gestione degli Errori
Quando un’action lancia un errore non gestito o va in timeout:
- L’errore viene registrato nel log di esecuzione dell’action.
- L’
errorCountsull’action viene incrementato. - Il flusso di autenticazione continua (le action non bloccano l’auth di default a meno che non venga chiamato
api.deny()). - Se l’action è critica, usa
api.deny()esplicitamente nel tuo gestore degli errori.
Gli errori delle action non bloccano l’autenticazione di default. Se hai bisogno che un’action fallita impedisca il login (es. un controllo di conformità), devi chiamare api.deny() esplicitamente nel tuo blocco catch. Altrimenti, l’utente verrà autenticato anche se l’action fallisce.
Blueprint Visual Editor
La Console Auris include un editor visuale a nodi (Blueprint Editor) per creare action tramite un’interfaccia drag-and-drop invece di scrivere JavaScript. Il Blueprint Editor genera definizioni di regole JSON che vengono compilate in JavaScript equivalente a runtime.
Il Blueprint Editor è un’alternativa all’editor di codice — entrambi producono lo stesso risultato. Le action create con il Blueprint Editor possono essere visualizzate e modificate come codice e viceversa.
Consulta la documentazione della Console per i dettagli sul Blueprint Editor.
Pagine Correlate
- Actions e Esecuzione Sandboxed — Come funziona il motore di esecuzione sandboxed
- Guida alle Actions Personalizzate — Guida passo-passo alla creazione di action
- Actions Engine — Crea e gestisci le action dalla Console
- API Webhook — Consegna eventi esterni che complementa le action