Benutzerdefinierte Actions schreiben
Actions sind benutzerdefinierte JavaScript-Funktionen, die an bestimmten Punkten in der Auris-Authentifizierungspipeline ausgeführt werden. Sie ermöglichen es dir, das Plattformverhalten zu erweitern, ohne Auris selbst zu forken oder zu modifizieren — füge benutzerdefinierte Claims zu Tokens hinzu, blockiere Logins basierend auf externen Daten, rufe Webhooks auf, setze Geschäftsregeln durch oder integriere Drittanbieterdienste.
Wann Actions vs. andere Erweiterungspunkte verwenden
| Mechanismus | Am besten für |
|---|---|
| Actions | Logik, die synchron während des Auth-Flows laufen muss (Login blockieren, Claims hinzufügen, Benutzer-Metadaten ändern) |
| Custom Claims | Statische oder attributbasierte Token-Anreicherung, die keine externen Aufrufe oder bedingte Logik erfordert |
| Webhooks | Asynchrone Benachrichtigungen nach dem Auftreten von Ereignissen (Audit-Logging, Analytics, Slack-Alerts) |
Wenn dein Anwendungsfall einen externen API-Aufruf erfordert, der erfolgreich sein muss, bevor der Login abgeschlossen ist, verwende eine Action. Wenn du ein System nur im Nachhinein benachrichtigen musst, verwende einen Webhook.
Die Sandbox-Umgebung
Actions werden in einer eingeschränkten JavaScript-Sandbox ausgeführt. Die Sandbox bietet:
Verfügbare APIs
| API | Hinweise |
|---|---|
fetch() | HTTP-Anfragen an externe Dienste stellen. Vollständige Fetch-API. |
JSON | JSON.parse() und JSON.stringify() |
Date | Datumskonstruktion und -manipulation |
Math | Mathematische Operationen |
console.log() | Ausgabe wird erfasst und in Action-Logs sichtbar |
String, Number, Boolean, Array, Object | Standardmäßige eingebaute Typen |
Promise, async/await | Asynchroner Code wird vollständig unterstützt |
URL, URLSearchParams | URL-Parsing und -Konstruktion |
TextEncoder, TextDecoder | Text-Encoding-Hilfsmittel |
crypto.randomUUID() | UUID-Generierung |
Blockierte APIs
Folgende sind aus Sicherheitsgründen blockiert:
| Blockiert | Grund |
|---|---|
require(), import | Kein Modulsystemzugriff |
process | Kein Zugriff auf Umgebungsvariablen oder Prozessinfos |
| Dynamische Code-Ausführungsfunktionen | Keine Laufzeit-Code-Generierung |
global, globalThis | Kein Zugriff auf den globalen Bereich |
fs, child_process | Kein Dateisystem- oder Prozess-Spawning |
Actions haben ein konfigurierbares Ausführungs-Timeout (Standard: 5 Sekunden, max: 30 Sekunden). Wenn eine Action das Timeout überschreitet, wird sie beendet und das konfigurierte Fehlverhalten greift (Anfrage erlauben oder ablehnen).
Trigger-Typen
Actions feuern an bestimmten Punkten im Authentifizierungsflow. Jeder Trigger erhält ein anderes context-Objekt:
| Trigger | Wann er feuert | Häufige Verwendungen |
|---|---|---|
pre-login | Bevor Anmeldedaten verifiziert werden | Logins nach E-Mail-Domain blockieren, externe Blocklisten prüfen |
post-login | Nach erfolgreicher Authentifizierung, vor Token-Ausgabe | Benutzerdefinierte Claims hinzufügen, mit externen Systemen synchronisieren, bedingtes MFA |
pre-signup | Bevor ein neuer Benutzer erstellt wird | E-Mail-Domain validieren, Einladungsanforderungen prüfen |
post-signup | Nachdem ein neuer Benutzer erstellt wurde | Willkommens-Webhook senden, zu Organisation hinzufügen, Metadaten setzen |
post-change-password | Nach einer Passwortänderung | Externe Systeme benachrichtigen, gecachte Sitzungen invalidieren |
pre-m2m-token | Bevor ein M2M-Token ausgegeben wird | Scopes einschränken, Client gegen externe Regeln validieren |
Das Context-Objekt
Jede Action erhält ein context-Objekt mit Informationen über die aktuelle Anfrage. Die Form variiert je nach Trigger:
pre-login und 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' },
}Der ActionResult-Rückgabetyp
Actions geben ein Objekt zurück, das das Ergebnis steuert:
interface ActionResult {
allow: boolean // true = fortfahren, false = Anfrage blockieren
message?: string // Fehlermeldung für den Benutzer bei allow=false
claims?: Record<string, any> // Benutzerdefinierte Claims zum Token hinzufügen (post-login, pre-m2m-token)
metadata?: Record<string, any> // Metadaten im Benutzerdatensatz setzen
}Wenn eine Action kein Ergebnis zurückgibt (oder undefined zurückgibt), wird die Anfrage normal fortgesetzt.
Praktische Beispiele
Logins nach E-Mail-Domain blockieren
// Trigger: pre-login
// Persönliche E-Mail-Anbieter beim Login blockieren
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: 'Persönliche E-Mail-Adressen sind nicht erlaubt. Bitte verwende deine Firmen-E-Mail.',
}
}
return { allow: true }Benutzerdefinierte Claims basierend auf Rollen hinzufügen
// Trigger: post-login
// Einen 'plan'-Claim und Feature-Flags basierend auf Benutzer-Metadaten hinzufügen
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,
},
}Authentifizierungsereignisse an einen externen Webhook protokollieren
// Trigger: post-login
// Webhook-Benachrichtigung für jeden Login senden
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) {
// Nicht kritisch -- protokollieren, aber Login nicht blockieren
console.log('Webhook fehlgeschlagen:', err.message)
}
return { allow: true }M2M-Scopes basierend auf externer Richtlinie einschränken
// Trigger: pre-m2m-token
// Externen Richtlinien-Service prüfen, bevor M2M-Tokens ausgestellt werden
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: 'Token-Anfrage vom Richtlinien-Service abgelehnt.',
}
}
const policy = await response.json()
return {
allow: true,
claims: {
scope: policy.allowedScopes.join(' '),
'policy-version': policy.version,
},
}Bedingter MFA-Trigger
// Trigger: post-login
// MFA für Admin-Rollen oder Logins aus neuen Ländern verlangen
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 },
}Ausführungsreihenfolge
Wenn mehrere Actions für denselben Trigger konfiguriert sind, werden sie in der durch ihr order-Feld definierten Reihenfolge (aufsteigend) ausgeführt. Jede Action erhält denselben ursprünglichen Kontext — die Ausgabe einer Action ändert den Kontext für nachfolgende Actions nicht.
Claims und Metadaten aus allen Actions werden jedoch zusammengeführt. Wenn zwei Actions denselben Claim-Schlüssel setzen, gewinnt die zuletzt in der Reihenfolge stehende Action.
| Action-Reihenfolge | Action-Name | Claims-Ausgabe |
|---|---|---|
| 1 | Plan-Claim hinzufügen | { plan: 'pro' } |
| 2 | Feature-Flags hinzufügen | { features: { ... } } |
| 3 | Region-Claim hinzufügen | { region: 'eu' } |
Finale Token-Claims: { plan: 'pro', features: { ... }, region: 'eu' }
Wenn eine Action allow: false zurückgibt, wird die gesamte Pipeline angehalten und die Anfrage abgelehnt, unabhängig von nachfolgenden Actions.
Der Blueprint Visual Editor
Für Teams, die einen visuellen Ansatz bevorzugen, bietet Auris einen Blueprint-Editor — einen knotenbasierten visuellen Regel-Builder, inspiriert von Unreal Engines Blueprint-System.
Der Blueprint-Editor stellt Actions als einen Graphen verbundener Knoten dar:
| Knotentyp | Farbe | Zweck |
|---|---|---|
| Trigger | Orange | Der Einstiegspunkt — welches Auth-Ereignis die Regel auslöst |
| Condition | Cyan | Ein Feld gegen einen Wert prüfen (z. B. user.email contains @acme.com) |
| Logic Gate | Violett | Bedingungen mit AND/OR kombinieren |
| Deny | Pink | Die Anfrage mit einer Fehlermeldung blockieren |
| Set Claims | Blau | Schlüssel-Wert-Paare zum Token hinzufügen |
| Set Metadata | Smaragdgrün | Benutzer-Metadaten aktualisieren |
| Log | Grün | In Action-Logs schreiben |
Der Blueprint-Editor und der Code-Editor sind zwei Ansichten derselben zugrunde liegenden Action. Änderungen in einer werden mit der anderen synchronisiert. Du kannst mit Blueprint für einfache Regeln beginnen und für komplexe Logik zum Code wechseln.
Um auf den Blueprint-Editor zuzugreifen, öffne die Detailseite einer Action in der Console und klicke auf den Blueprint-Reiter.
Actions debuggen
Action-Logs
Jede Action-Ausführung wird protokolliert. Logs können in Console → Actions → [Action-Name] → Logs-Reiter angesehen werden. Jeder Log-Eintrag enthält:
- Ausführungs-Zeitstempel
- Trigger-Ereignis
- Ausführungsdauer (ms)
- Status (Erfolg, Fehler, Timeout)
- console.log-Ausgabe
- Rückgabewert
console.log verwenden
console.log()-Ausgabe wird erfasst und im Action-Log gespeichert:
console.log('Benutzer-Rollen:', JSON.stringify(context.user.roles))
console.log('Anfrage-IP:', context.request.ip)
console.log('GeoIP-Daten:', JSON.stringify(context.request.geoip))
// Diese Ausgabe erscheint im Logs-Reiter der Action
return { allow: true }Fehlerbehandlung
Wenn eine Action einen unbehandelten Fehler wirft, hängt das Verhalten von der Fehlermodus-Einstellung der Action ab:
| Fehlermodus | Verhalten |
|---|---|
allow (Standard) | Die Anfrage wird fortgesetzt. Der Fehler wird protokolliert. |
deny | Die Anfrage wird mit einer generischen Fehlermeldung blockiert. |
Verwende immer try/catch rund um externe API-Aufrufe, um zu verhindern, dass Fehler Logins blockieren:
try {
const result = await fetch('https://external-api.example.com/check')
// Ergebnis verarbeiten
} catch (err) {
console.log('Externe API fehlgeschlagen, fortfahren:', err.message)
// Login nicht blockieren, weil ein externer Service ausgefallen ist
}
return { allow: true }Performance-Tipps
- Actions schnell halten — Strebe weniger als 100ms Ausführungszeit an. Jede Millisekunde erhöht die Login-Latenz.
- try/catch für externe Aufrufe verwenden — Lass niemals einen nicht-kritischen externen API-Fehler einen Login blockieren.
- Sequentielle externe Aufrufe vermeiden — Wenn du mehrere API-Aufrufe benötigst, verwende
Promise.all(), um sie parallel auszuführen. - Teure Lookups cachen — Für Daten, die sich selten ändern, erwäge das Caching in Benutzer-Metadaten und periodische Aktualisierung, anstatt bei jedem Login abzufragen.
- Angemessene Timeouts setzen — Verwende
AbortControllermitfetch(), um explizite Timeouts kürzer als das Action-Timeout zu setzen:
const controller = new AbortController()
setTimeout(() => controller.abort(), 3000) // 3-Sekunden-Timeout
const response = await fetch('https://slow-api.example.com/check', {
signal: controller.signal,
})API-Endpunkte
/api/actionsRequires: view:actionsAlle Actions für den Tenant auflisten. Unterstützt Filterung nach Trigger-Typ und Status.
/api/actionsRequires: manage:actionsEine neue Action erstellen. Erfordert name, trigger, code und optional order, timeout, status.
/api/actions/:idRequires: view:actionsAction-Details abrufen, einschließlich Code, Trigger, Ausführungsanzahl und Fehleranzahl.
/api/actions/:idRequires: manage:actionsCode, Trigger, Reihenfolge, Timeout oder Status einer Action aktualisieren.
/api/actions/:idRequires: manage:actionsEine Action löschen.
/api/actions/:id/logsRequires: view:actionsAusführungslogs für eine bestimmte Action auflisten. Enthält Dauer, Status, Konsolenausgabe und Rückgabewert.
Erforderliche Berechtigungen
| Operation | Berechtigung |
|---|---|
| Actions anzeigen | view:actions |
| Actions erstellen, bearbeiten, löschen | manage:actions |
| Action-Logs anzeigen | view:actions |
Verwandte Leitfäden
- Benutzerdefinierte JWT-Claims — Deklarative Claim-Anreicherung (kein Code erforderlich)
- Webhooks einrichten — Asynchrone Ereignisbenachrichtigungen
- Rollen & Berechtigungen — Verwaltung der in Actions referenzierten Berechtigungen