Auth0 Actions are serverless functions that run at specific points in the authentication pipeline — on login, after registration, before token issuance, and more. They replace the older Rules and Hooks systems and are the correct way to customise Auth0's behaviour in 2026.

What Actions Can Do

  • Enrich the access token with custom claims (user roles, organisation data, feature flags)
  • Block logins based on IP address, device fingerprint, or risk score
  • Send a welcome email after a user registers for the first time
  • Sync user data to your database on login
  • Enforce multi-factor authentication for specific user groups

Creating an Action

  1. Go to Auth0 Dashboard > Actions > Library > Build Custom
  2. Choose a Trigger (e.g. Login / Post Login)
  3. Write your Action code in the editor
  4. Test the Action with a simulated event
  5. Deploy the Action
  6. Add it to a Flow: Actions > Flows > Login, drag your Action into the flow

Recipe: Add Custom Claims to the Access Token

The most common Action adds user roles or subscription data to the JWT so your API can authorise requests without a database lookup.

// Action: Add user roles to access token
// Trigger: Login / Post Login
 
exports.onExecutePostLogin = async (event, api) => {
  // event.user contains the user's Auth0 profile
  const namespace = 'https://myapp.com';
 
  // Add roles from Auth0 user metadata
  const roles = event.authorization?.roles ?? [];
  api.accessToken.setCustomClaim(`${namespace}/roles`, roles);
 
  // Add subscription tier from app_metadata
  const tier = event.user.app_metadata?.subscription_tier ?? 'free';
  api.accessToken.setCustomClaim(`${namespace}/tier`, tier);
};
Always namespace your custom claims with a URL you control (e.g. https://yourdomain.com/claim-name). Auth0 rejects reserved claim names (sub, iss, aud, etc.) without a namespace.

Recipe: Block Logins by Domain

// Action: Only allow corporate email addresses
exports.onExecutePostLogin = async (event, api) => {
  const allowedDomains = ['yourcompany.com', 'partnercompany.com'];
  const email = event.user.email ?? '';
  const domain = email.split('@')[1];
 
  if (!allowedDomains.includes(domain)) {
    api.access.deny(`Access restricted to corporate email addresses.`);
  }
};

Recipe: Sync User to Your Database on Login

// Action: Upsert user in your database on every login
const { Client } = require('pg');
 
exports.onExecutePostLogin = async (event, api) => {
  // Only sync on new users or first login
  if (event.stats.logins_count > 1) return;
 
  const client = new Client({ connectionString: event.secrets.DATABASE_URL });
  await client.connect();
 
  await client.query(
    `INSERT INTO users (auth0_id, email, name)
     VALUES ($1, $2, $3)
     ON CONFLICT (auth0_id) DO UPDATE SET email = $2, name = $3`,
    [event.user.user_id, event.user.email, event.user.name]
  );
 
  await client.end();
};
Store database credentials as Secrets in the Action editor, not hardcoded in the code. Secrets are encrypted and accessible via event.secrets.SECRET_NAME.

Debugging Actions

Use the Real-time Webtask Logs feature in the Auth0 dashboard to see console.log output from your Actions during testing. In production, Auth0 streams logs to the Auth0 Log Stream — configure this to send to Datadog, Splunk, or a webhook for alerting.

Metadata Value
Title Auth0 Actions: Customising the Login Flow with Serverless Logic
Tool Auth0
Primary SEO keyword auth0 actions login flow
Secondary keywords auth0 actions tutorial, auth0 custom claims, auth0 block login, auth0 post login action
Estimated read time 8 minutes
Research date 2026-04-14