Documentation Index
Fetch the complete documentation index at: https://docs-staging.auth0-mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Auth0 Actions allow you to customize authentication with custom code. When using multiple custom domains, you can access custom domain information in your Actions to implement domain-specific logic, route users to organizations, enforce access policies, and customize notifications.
Accessing custom domain information
Actions provide access to custom domain information through the event.custom_domain object. This object contains the domain name and any metadata configured for that domain.
Available triggers
The event.custom_domain object is available in the following Action triggers:
| Trigger | Event object reference |
|---|
| Credentials Exchange | Event object |
| Custom Email Provider | Event object |
| Custom Phone Provider | Event object |
| Post Login | Event object |
| Post User Registration | Event object |
| Post Password Change | Event object |
| Pre User Registration | Event object |
| Reset Password Post Challenge | Event object |
The event.custom_domain object is only available in triggers where the request originates from a custom domain context. If no custom domain was used, this property will be undefined.
Event object structure
Using login.example.com domain configured with domain metadata:
| Key | Value |
|---|
| allow_list | example1.com,example2.com |
exports.onExecutePostLogin = async (event, api) => {
console.log(event.custom_domain);
};
Would log at the console:
{
"domain": "login.example.com",
"domain_metadata": {
"allow_list": "example1.com,example2.com"
}
}
Common use cases
Conditional logic by domain
Apply different rules based on whether a custom domain or canonical domain is used:
exports.onExecutePostLogin = async (event, api) => {
const domain = event.custom_domain?.domain;
if (
domain === undefined ||
domain.includes('.auth0.com') || // Public Cloud
domain.includes('.auth0app.com') // Private Cloud
) {
return api.access.deny('Please use the custom domain to log in.');
}
};
Email domain-based access control
Enforce email domain-specific access policies using custom domain metadata:
const getEmailDomain = (email) => {
if (!email || typeof email !== 'string') {
return null;
}
const parts = email.split('@');
// Ensure there is exactly one '@' (or at least that a domain exists)
// handles cases like "invalid-email" or "user@"
if (parts.length < 2 || !parts[1]) {
return null;
}
return parts[1].toLowerCase().trim();
}
exports.onExecutePreUserRegistration = async (event, api) => {
const domain = event.custom_domain?.domain;
if (domain === undefined) {
return api.access.deny(
'access_denied',
`Access denied - Users cannot access without custom domain.`
);
}
const email = event.user.email;
if (email === undefined) {
return api.access.deny(
'access_denied',
`Access denied - Users cannot access without email.`
);
}
const domainAllowList = event.custom_domain?.domain_metadata?.allow_list?.split(',') || [];
const emailDomain = getEmailDomain(email);
if (domainAllowList.includes(emailDomain) === false) {
return api.access.deny(
'access_denied',
`Access denied - Users from ${emailDomain} cannot access ${domain}.`
);
}
};
Enforce application and domain groups access policies using application and domain metadata:
exports.onExecuteCredentialsExchange = async (event, api) => {
const domain = event.custom_domain?.domain;
if (
domain === undefined ||
domain.includes(event.tenant.id)
) {
return; // Skip for canonical
}
const applicationGroup = new Set(event.client.metadata?.domain_group?.split(',') || []);
const domainGroup = event.custom_domain?.domain_metadata?.domain_group?.split(',') || [];
const intersection = domainGroup.filter(x => applicationGroup.has(x));
if (intersection.length === 0) {
return api.access.deny(
'invalid_request',
`Access denied - Cannot get access from application ${event.client.name} and ${domain}.`
);
}
};
Enforce connection and domain groups access policies using connection and domain metadata:
exports.onExecutePostLogin = async (event, api) => {
const domain = event.custom_domain?.domain;
if (domain === undefined) {
return api.access.deny(
`Access denied - Users cannot access without custom domain.`
);
}
const connectionGroup = new Set(event.connection.metadata?.domain_group?.split(',') || []);
const domainGroup = event.custom_domain?.domain_metadata?.domain_group?.split(',') || [];
const intersection = domainGroup.filter(x => connectionGroup.has(x));
if (intersection.length === 0) {
return api.access.deny(
`Access denied - Users cannot access connection ${event.connection.name} from ${domain}.`
);
}
};
Enforce organization and domain groups access policies using organization and domain metadata:
exports.onExecutePostLogin = async (event, api) => {
const organization = event.organization;
if (organization === undefined) {
return; // Skip for non-organization authentication
}
const domain = event.custom_domain?.domain;
if (domain === undefined) {
return api.access.deny(
`Access denied - Users cannot access without custom domain.`
);
}
const organizationGroup = new Set(organization.metadata?.domain_group?.split(',') || []);
const domainGroup = event.custom_domain?.domain_metadata?.domain_group?.split(',') || [];
const intersection = domainGroup.filter(x => organizationGroup.has(x));
if (intersection.length === 0) {
return api.access.deny(
`Access denied - Users cannot access organization ${organization.name} from ${domain}.`
);
}
};
Region-based requests
Make requests to region-specific external services based on custom domain metadata:
exports.onExecuteCustomEmailProvider = async (event, api) => {
const regionServiceEndpoint = event.custom_domain?.domain_metadata?.region_service_endpoint;
if (regionServiceEndpoint === undefined) {
return api.notification.drop(`Missing regional service endpoint configuration at custom domain.`);
}
const notification = event.notification;
const messageBody = {
body: notification.html
};
try {
await fetch(regionServiceEndpoint, {
method: 'POST',
headers: {
'X-API-Key': event.secrets.API_KEY,
},
body: JSON.stringify(messageBody),
});
} catch (err) {
api.notification.drop('External service failure');
}
};
Learn more
Actions documentation
Event object references