Customize MFA Selection in New Universal Login
Auth0 supports a variety of factors for securing user access with multi-factor authentication (MFA). Using post-login
Actions, you can customize your MFA flows to challenge users with a specific factor or sequence of factors. You can also use contextual information about users and their organizations to create more individualized experiences. For example, you can customize your flows to challenge users with specific factors based on their membership in certain Organizations or their assigned user roles.
Customization is only available in the New Universal Login Experience.
How it works
You can use Actions to customize your MFA flows. Specifically, you can modify the post-login
trigger of the Login Flow with the following Authentication API methods:
challengeWith
: Specifies the factor or factors users must use to authenticate, such as a one-time password (OTP). This method presents a default challenge to users and can optionally provide access to a factor picker that allows them to choose a different authentication method.challengeWithAny
: Sets a group of factors users can choose from when authenticating, such as email and OTP. By default, this method presents a factor picker to users rather than a specific challenge, in accordance with the following conditions:If two or more factors are specified, a factor picker displays to the user.
If the user has only enrolled in one of the specified factors (or only one factor is supplied), the factor picker is skipped.
If the user has not enrolled in any of the specified factors, the command fails.
You can use a combination of these methods to tailor your MFA flows as needed. You can also incorporate user metadata, such as roles or previously used factors, into these methods to create more tailored flows.
When choosing MFA challenges for your commands, you can use the factors listed below or the enrolledFactors
value. enrolledFactors
represents the list of active factors associated with a user's account.
otp
email
push-notification
otpFallback
phone
preferredMethod: voice
preferredMethod: sms
preferredMethod: both
webauthn-platform
webauthn-roaming
The array event.authentication.methods
includes a type
field when the name of the method is set to mfa
. type is a string that contains factor values matching those used by the type
field from enrolledFactors
(listed above). When an MFA challenge is performed, methods
contains an object of name:mfa
with type
set to the factor used for that challenge. methods
is only updated when an Action begins. To see the results of a challenge, methods
must be accessed in the next Action in the flow.
To learn more, review the following resources:
Sequenced and contextual flows
With the challengeWith
or challengeWithAny
commands, you can use contextual information to determine the best challenge or series of challenges to present to users. Specifically, you can leverage the following:
Sequenced flows: Challenge users with a series of different factors in a specific order.
Contextual flows: Determine which factor to next challenge the user with based on previous challenges in the flow.
To help illustrate these flows, consider the following example:
// ACTION 1
api.authentication.challengeWithAny([{ type: 'sms'}, { type: 'push-notification' }]);
// ============================================
// ACTION 2
// Decide based on what the user did in the previous action
if(event.authentication.methods[1]?.type === 'sms' && event.user.app_metadata.isAdmin) {
api.authentication.challengeWith({ type: 'push-notification' });
}
Was this helpful?
In this scenario, a user is first challenged with SMS via the challengeWith
command in Action 1. Then, Action 2 challenges the user with a push notification because they have the Admin user role and also completed the SMS challenge.
In this flow, you can make decisions about which factor to challenge the user with due to the following:
The flow pauses after executing Action 1.
The user completes the MFA flow prompted by Action 1.
event.authentication.methods.type
in Action 2 populates with information from the previous MFA challenge.The flow resumes to execute Action 2 using contextual information from Action 1.
While this example presents a similar experience to using redirects in your Actions, commands using challengeWith
and challengeWithAny
offer the following unique benefits:
Flows pause after each command, allowing you to accumulate user information that can be used in subsequent Actions. Comparatively, redirects only occur a single time as the final command in a flow.
MFA is triggered after each Action containing
challengeWith
orchallengeWithAny
commands is executed. With redirects, MFA runs as the final Action in the pipeline.
Note: This method of executing Actions only applies to those containing challengeWith
or challengeWithAny
commands. Actions serving other purposes are not affected.
Prepare your tenant
To get started, ensure MFA is enabled in your tenant, and your users have enrolled in the appropriate factors. Users must be enrolled in authenticators before they can be challenged by post-login
Action commands.
On the Auth0 Dashboard, you can configure MFA under Security > Multifactor Auth.
To learn more about the setup process, review Enable Multi-Factor Authentication.
For information about configuring specific factors, review Multi-factor Authentication Factors.
After MFA has been enabled, ensure your users enroll in the factors configured for your tenant. After a user signs up or is created in your tenant, you can create enrollments with the Management API authentication-methods endpoint or manage users' enrollments through their profile pages in the Auth0 Dashboard.
Customize your MFA flows
Once your tenant is ready, you can create post-login
Actions to customize your MFA flows. Steps and example use cases are provided below.
Create your post-login Action
On your Auth0 Dashboard, navigate to Actions > Flows and select Login.
Under Add Action, select Custom and choose Create Action.
On the Create Action popup:
Enter a name for your Action.
Select Login / Post-Login as the trigger.
Use Node 18 (Recommended) for the runtime.
Review the popup to ensure accuracy. Then, select Create.
After creation, the code editor displays the
onPostExecute
command. Add your custom code or code sample to the command.When your command is ready, select Deploy.
Select Add to Flow on the successful deployment notification.
Note: If the notification has closed, choose Back to Flow above the code editor.
Drag and drop your new command from the Add Action panel into your Login flow. Then, select Apply.
To make additional updates to your Action, navigate to Actions > Library > Custom and select your command. You can then update and redeploy your code as needed.
Test your post-login Action
To ensure your commands function appropriately, you can test your Action through the Auth0 Dashboard:
Navigate to Authentication > Authentication Profile.
Select Try to open a sample login prompt in a new tab.
Enter your credentials and test your new MFA flow.
If the flow is successful, a confirmation screen displays. If you encounter any issues, you can update your code by navigating to Actions > Library > Custom on your Auth0 Dashboard.
Example use cases
The examples below outline common use cases for customizing MFA flows.
Use current enrollments to determine challenge method
The following sample challenges a user with MFA if they are enrolled with the following factors:
One-time password (OTP)
Phone
exports.onExecutePostLogin = async (event, api) => {
api.authentication.challengeWithAny([{type: 'otp'}, {type: 'phone'}]);
}
Was this helpful?
Use roles to determine challenge method
The following sample challenges all users with OTP. If a user has the Admin role and requires a higher level of access to your application, they are challenged with an additional factor as a form of step-up authentication.
exports.onExecutePostLogin = async (event, api) => {
api.authentication.challengeWith({type: 'otp'});
const isAdmin = event.authorization.roles.some('admin');
if(isAdmin) {
api.authentication.challengeWith({type: 'phone'});
}
}
Was this helpful?
Use metadata to determine challenge method
In this example, MFA factors are enabled at the Organization level. This sample uses different categories of metadata to determine the right challenge for individual users:
Organization metadata: Organization-level data, such as the specific factors enabled for an Organization.
User metadata: User-level data, such as whether a user has a phone number associated with their profile.
exports.onExecutePostLogin = async (event, api) => {
const orgFactors = event.organization.metadata.factors ?? [];
// Get the intersection of factors available for the user and factors enabled for the org
const availableFactors = orgFactors.filter(f => event.user.enrolledFactors.some(ef => ef.type === f));
// Prefer push if available
if(availableFactors.includes('push-notification')) {
api.authentication.challengeWith({ type: 'push-notification' });
return;
}
// If the user has a verified phone number and the organization
// allows for SMS and email, prefer SMS and allow email as a fallback
// if available
if(event.user.phone_number &&
event.user.phone_verified &&
availableFactors.includes('phone')) {
if(availableFactors.includes('email') {
api.authentication.challengeWith({ type: 'phone' }, {
additionalFactors: [{
type: 'email'
}]
});
} else {
api.authentication.challengeWith({ type: 'phone' });
}
return;
}
// If push-notifications and/or phone couldn't be prioritized, fallback to email if
// enabled for the org, otherwise fail.
if(availableFactors.includes('email') {
api.authentication.challengeWith({ type: "email" });
return;
}
api.access.deny("No MFA factors available for this org + user");
};
Was this helpful?
Allow users to select an alternate method of authentication
For a more flexible experience, you can present users with a Try Another Method link as part of their MFA challenge. This link allows users to select a different method of authentication than the default challenge.
To achieve this, include the additionalFactors
parameter in your Actions code. You can set this parameter to a specific factor for all users or use enrolledFactors
to let users choose their preferred factor.
Specific Factor
The following sample challenges users with OTP by default. If desired, users can access the Try Another Method link to authenticate with email instead.
exports.onExecutePostLogin = async (event, api) => {
api.authentication.challengeWith({ type: 'otp' },
{ additionalFactors: [{type: 'email'}] })
};
Was this helpful?
Enrolled Factors
The following sample challenges users with OTP by default. If desired, users can access the Try Another Method link to authenticate with one of their other enrolled factors.
exports.onExecutePostLogin = async (event, api) => {
const enrolledFactors = event.user.enrolledFactors.map((f) => ({type: f.type}));
api.authentication.challengeWith({ type: 'otp' },
{ additionalFactors: enrolledFactors })
};
Was this helpful?
Early Access considerations
During the Early Access period, some functionality may not yet be available as we continue to build out our customization options. Keep the following items in mind when considering this feature for your application:
Actions (or series of Actions) in a tenant can only execute three
challengeWith
orchallengeWithAny
commands per user flow. An authentication error occurs if a user is challenged with a fourthchallengeWith
orchallengeWithAny
command.Progressive enrollment is not currently available. You can use existing methods of managing enrollments, such as:
Allowing users to complete their own enrollments as determined by your MFA policies.
Creating new enrollments for users through the Management API authentication-methods endpoint.
Manually creating authenticators for users through custom enrollment tickets.
Directly managing enrollments through users' profile pages in the Auth0 Dashboard.
Customization is not currently available for WS-Fed, SAML, or non-interactive flows (including the ROPG flow).
If you have any questions or wish to provide feedback, reach out to your Auth0 point of contact.