Scrivere Actions Personalizzate
Le Actions sono funzioni JavaScript personalizzate che vengono eseguite in punti specifici della pipeline di autenticazione Auris. Consentono di estendere il comportamento della piattaforma senza fare fork o modificare Auris — aggiungi claim personalizzati ai token, blocca i login in base a dati esterni, chiama webhook, applica regole di business o integra servizi di terze parti.
Quando usare Actions vs altri punti di estensione
| Meccanismo | Adatto Per |
|---|---|
| Actions | Logica che deve essere eseguita in modo sincrono durante il flusso auth (blocca un login, aggiungi claim, modifica metadata utente) |
| Custom Claims | Arricchimento del token statico o basato su attributi che non richiede chiamate esterne o logica condizionale |
| Webhook | Notifiche asincrone dopo che gli eventi si verificano (audit logging, analytics, alert Slack) |
L’Ambiente Sandbox
Le Actions vengono eseguite in una sandbox JavaScript ristretta.
API Disponibili
| API | Note |
|---|---|
fetch() | Fare richieste HTTP a servizi esterni. Fetch API completa. |
JSON | JSON.parse() e JSON.stringify() |
Date | Costruzione e manipolazione di date |
Math | Operazioni matematiche |
console.log() | L’output viene catturato e visibile nei log delle action |
Promise, async/await | Il codice asincrono è completamente supportato |
URL, URLSearchParams | Parsing e costruzione URL |
crypto.randomUUID() | Generazione UUID |
API Bloccate
| Bloccato | Motivo |
|---|---|
require(), import | Nessun accesso al sistema di moduli |
process | Nessun accesso alle variabili d’ambiente o alle info di processo |
global, globalThis | Nessun accesso allo scope globale |
fs, child_process | Nessun filesystem o avvio processi |
Le Actions hanno un timeout di esecuzione configurabile (default: 5 secondi, max: 30 secondi). Se un’action supera il timeout, viene terminata e si applica il comportamento di fallimento configurato (consenti o nega la richiesta).
Tipi di Trigger
| Trigger | Quando si Attiva | Usi Comuni |
|---|---|---|
pre-login | Prima della verifica delle credenziali | Blocca login per dominio email, controlla blocklist esterne |
post-login | Dopo l’autenticazione riuscita, prima dell’emissione del token | Aggiungi claim personalizzati, sincronizza con sistemi esterni |
pre-signup | Prima della creazione di un nuovo utente | Valida il dominio email, controlla i requisiti di invito |
post-signup | Dopo la creazione di un nuovo utente | Invia webhook di benvenuto, assegna all’organizzazione |
post-change-password | Dopo un cambio password | Notifica sistemi esterni, invalida sessioni cached |
pre-m2m-token | Prima dell’emissione di un token M2M | Limita scope, valida client contro regole esterne |
L’Oggetto Context
Ogni action riceve un oggetto context con informazioni sulla richiesta corrente:
{
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' },
}Il Tipo di Ritorno ActionResult
interface ActionResult {
allow: boolean // true = procedi, false = blocca la richiesta
message?: string // Messaggio di errore mostrato all'utente se allow=false
claims?: Record<string, any> // Claim personalizzati da aggiungere al token
metadata?: Record<string, any> // Metadata da impostare sul record utente
}Esempi Pratici
Blocca login per dominio email
// Trigger: pre-login
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: 'Gli indirizzi email personali non sono consentiti. Usa la tua email aziendale.',
}
}
return { allow: true }Aggiungi claim personalizzati in base ai ruoli
// Trigger: post-login
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,
},
}Registra eventi di autenticazione su un webhook esterno
// Trigger: post-login
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) {
// Non critico -- registra ma non bloccare il login
console.log('Webhook fallito:', err.message)
}
return { allow: true }Restrizione geografica del login
// Trigger: pre-login
const allowedCountries = ['IT', 'DE', 'FR', 'ES', 'GB', 'US']
const country = context.request.geoip?.country
if (country && !allowedCountries.includes(country)) {
return {
allow: false,
reason: 'Il login non è disponibile dalla tua posizione attuale.',
}
}
return { allow: true }Ordine di Esecuzione
Quando più actions sono configurate per lo stesso trigger, vengono eseguite nell’ordine definito dal campo order (ascendente). I claim e i metadata di tutte le actions vengono uniti. Se due actions impostano la stessa chiave del claim, l’ultima action nell’ordine vince.
Se una qualsiasi action restituisce allow: false, l’intera pipeline viene interrotta e la richiesta negata.
L’Editor Blueprint Visuale
Per i team che preferiscono un approccio visuale, Auris fornisce un editor Blueprint — un costruttore di regole visuale basato su nodi.
| Tipo di Nodo | Colore | Scopo |
|---|---|---|
| Trigger | Arancione | Il punto di ingresso |
| Condizione | Ciano | Controlla un campo rispetto a un valore |
| Logic Gate | Viola | Combina condizioni con AND/OR |
| Deny | Rosa | Blocca la richiesta con un messaggio di errore |
| Set Claims | Blu | Aggiungi coppie chiave-valore al token |
| Set Metadata | Smeraldo | Aggiorna i metadata utente |
| Log | Verde | Scrivi sui log delle action |
Debug delle Actions
Log delle Action
Ogni esecuzione di action viene registrata. Visualizza i log in Console → Actions → [Nome Action] → tab Log.
Utilizzo di console.log
console.log('Ruoli utente:', JSON.stringify(context.user.roles))
console.log('IP richiesta:', context.request.ip)
// Questo output appare nel tab Log dell'action
return { allow: true }Gestione degli Errori
Se un’action lancia un errore non gestito, il comportamento dipende dall’impostazione modalità di fallimento dell’action:
| Modalità di Fallimento | Comportamento |
|---|---|
allow (default) | La richiesta procede. L’errore viene registrato. |
deny | La richiesta viene bloccata con un messaggio di errore generico. |
Consigli per le Performance
- Mantieni le actions veloci — Punta a meno di 100ms di tempo di esecuzione.
- Usa try/catch per le chiamate esterne — Non lasciare mai che un’API esterna non critica blocchi un login.
- Evita chiamate esterne sequenziali — Usa
Promise.all()per eseguirle in parallelo. - Imposta timeout appropriati — Usa
AbortControllerconfetch().
const controller = new AbortController()
setTimeout(() => controller.abort(), 3000) // 3 secondi di timeout
const response = await fetch('https://slow-api.example.com/check', {
signal: controller.signal,
})Permessi Richiesti
| Operazione | Permesso |
|---|---|
| Visualizza actions | view:actions |
| Crea, modifica, elimina actions | manage:actions |
| Visualizza log action | view:actions |
Guide Correlate
- Custom JWT Claims — Arricchimento dichiarativo dei claim (nessun codice richiesto)
- Configurare i Webhook — Notifiche asincrone degli eventi
- Ruoli & Permessi — Gestire i permessi referenziati nelle actions