Écrire des Actions Personnalisées
Les Actions sont des fonctions JavaScript personnalisées qui s’exécutent à des points spécifiques du pipeline d’authentification Auris. Elles permettent d’étendre le comportement de la plateforme sans fork ou modification d’Auris — ajoute des claims personnalisés aux tokens, bloque des connexions en fonction de données externes, appelle des webhooks, applique des règles métier ou intègre des services tiers.
Quand utiliser les Actions vs autres points d’extension
| Mécanisme | Adapté pour |
|---|---|
| Actions | Logique devant s’exécuter de façon synchrone pendant le flux auth (bloquer une connexion, ajouter des claims, modifier les metadata utilisateur) |
| Custom Claims | Enrichissement de token statique ou basé sur des attributs ne nécessitant pas d’appels externes ou de logique conditionnelle |
| Webhook | Notifications asynchrones après que les événements se produisent (audit logging, analytics, alertes Slack) |
L’Environnement Sandbox
Les Actions s’exécutent dans une sandbox JavaScript restreinte.
APIs Disponibles
| API | Notes |
|---|---|
fetch() | Effectuer des requêtes HTTP vers des services externes. API Fetch complète. |
JSON | JSON.parse() et JSON.stringify() |
Date | Construction et manipulation de dates |
Math | Opérations mathématiques |
console.log() | La sortie est capturée et visible dans les logs d’action |
Promise, async/await | Le code asynchrone est entièrement supporté |
URL, URLSearchParams | Parsing et construction d’URL |
crypto.randomUUID() | Génération d’UUID |
APIs Bloquées
| Bloqué | Raison |
|---|---|
require(), import | Pas d’accès au système de modules |
process | Pas d’accès aux variables d’environnement ou aux infos de processus |
global, globalThis | Pas d’accès au scope global |
fs, child_process | Pas de système de fichiers ou de lancement de processus |
Les Actions ont un timeout d’exécution configurable (défaut : 5 secondes, max : 30 secondes). Si une action dépasse le timeout, elle est terminée et le comportement de fallback configuré s’applique (autoriser ou refuser la requête).
Types de Déclencheurs
| Déclencheur | Quand il s’active | Usages courants |
|---|---|---|
pre-login | Avant la vérification des credentials | Bloquer les connexions par domaine e-mail, vérifier des blocklists externes |
post-login | Après une authentification réussie, avant l’émission du token | Ajouter des claims personnalisés, synchroniser avec des systèmes externes |
pre-signup | Avant la création d’un nouvel utilisateur | Valider le domaine e-mail, vérifier les exigences d’invitation |
post-signup | Après la création d’un nouvel utilisateur | Envoyer un webhook de bienvenue, assigner à une organisation |
post-change-password | Après un changement de mot de passe | Notifier des systèmes externes, invalider des sessions en cache |
pre-m2m-token | Avant l’émission d’un token M2M | Restreindre les scopes, valider le client par rapport à des règles externes |
L’Objet Context
Chaque action reçoit un objet context avec des informations sur la requête courante :
{
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: 'FR', city: 'Paris' },
},
application: { id: 'app-456', name: 'Dashboard', type: 'WEB' },
tenant: { id: 'tenant-789', name: 'acme-corp' },
}Le Type de Retour ActionResult
interface ActionResult {
allow: boolean // true = continuer, false = bloquer la requête
message?: string // Message d'erreur affiché à l'utilisateur si allow=false
claims?: Record<string, any> // Claims personnalisés à ajouter au token
metadata?: Record<string, any> // Metadata à définir sur l'enregistrement utilisateur
}Exemples Pratiques
Bloquer les connexions par domaine e-mail
// Déclencheur : 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: 'Les adresses e-mail personnelles ne sont pas autorisées. Utilise ton e-mail professionnel.',
}
}
return { allow: true }Ajouter des claims personnalisés selon les rôles
// Déclencheur : 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,
},
}Enregistrer des événements d’authentification sur un webhook externe
// Déclencheur : 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 critique -- enregistrer mais ne pas bloquer la connexion
console.log('Webhook échoué :', err.message)
}
return { allow: true }Restriction géographique de connexion
// Déclencheur : pre-login
const allowedCountries = ['FR', 'BE', 'CH', 'DE', 'ES', 'GB', 'US']
const country = context.request.geoip?.country
if (country && !allowedCountries.includes(country)) {
return {
allow: false,
reason: 'La connexion n\'est pas disponible depuis ta localisation actuelle.',
}
}
return { allow: true }Ordre d’Exécution
Quand plusieurs actions sont configurées pour le même déclencheur, elles s’exécutent dans l’ordre défini par le champ order (ascendant). Les claims et metadata de toutes les actions sont fusionnés. Si deux actions définissent la même clé de claim, la dernière action dans l’ordre gagne.
Si une action retourne allow: false, tout le pipeline est interrompu et la requête refusée.
L’Éditeur Blueprint Visuel
Pour les équipes qui préfèrent une approche visuelle, Auris fournit un éditeur Blueprint — un constructeur de règles visuel basé sur des nœuds.
| Type de Nœud | Couleur | Objectif |
|---|---|---|
| Trigger | Orange | Le point d’entrée |
| Condition | Cyan | Vérifie un champ par rapport à une valeur |
| Logic Gate | Violet | Combine des conditions avec AND/OR |
| Deny | Rose | Bloque la requête avec un message d’erreur |
| Set Claims | Bleu | Ajoute des paires clé-valeur au token |
| Set Metadata | Émeraude | Met à jour les metadata utilisateur |
| Log | Vert | Écrit dans les logs d’action |
Débogage des Actions
Logs d’Action
Chaque exécution d’action est enregistrée. Affiche les logs dans Console → Actions → [Nom de l’Action] → onglet Logs.
Utilisation de console.log
console.log('Rôles utilisateur :', JSON.stringify(context.user.roles))
console.log('IP de la requête :', context.request.ip)
// Cette sortie apparaît dans l'onglet Logs de l'action
return { allow: true }Gestion des Erreurs
Si une action lance une erreur non gérée, le comportement dépend du paramètre mode de fallback de l’action :
| Mode de Fallback | Comportement |
|---|---|
allow (défaut) | La requête continue. L’erreur est enregistrée. |
deny | La requête est bloquée avec un message d’erreur générique. |
Conseils de Performance
- Garde les actions rapides — Vise moins de 100ms de temps d’exécution.
- Utilise try/catch pour les appels externes — Ne laisse jamais une API externe non critique bloquer une connexion.
- Évite les appels externes séquentiels — Utilise
Promise.all()pour les exécuter en parallèle. - Définis des timeouts appropriés — Utilise
AbortControlleravecfetch().
const controller = new AbortController()
setTimeout(() => controller.abort(), 3000) // timeout de 3 secondes
const response = await fetch('https://slow-api.example.com/check', {
signal: controller.signal,
})Permissions Requises
| Opération | Permission |
|---|---|
| Visualiser les actions | view:actions |
| Créer, modifier, supprimer les actions | manage:actions |
| Visualiser les logs d’action | view:actions |
Guides Associés
- Claims JWT Personnalisés — Enrichissement déclaratif des claims (aucun code requis)
- Configurer les Webhooks — Notifications asynchrones d’événements
- Rôles & Permissions — Gérer les permissions référencées dans les actions