Skip to main content
You can customize for a variety of scenarios with Auth0 Actions.

When to customize Adaptive MFA

You should only consider customizing Adaptive MFA if your users are enrolled in MFA and are required to use an email as an identifier.
If your users are not enrolled in , you should use the default policy for Adaptive MFA. If a user is not enrolled in MFA and your Action assesses a high risk, you have limited options to stop a . Before you begin to customize Adaptive MFA, ask yourself a few questions:
  • At what confidence level do you want to trigger MFA?
  • How do you want to measure risk?
  • Do you want Auth0 to measure confidence or do you want a custom measurement?
  • How will you handle users who are not enrolled in MFA?

Confidence scores

Adaptive MFA calculates an overall confidence score based on the analysis of three assessments: NewDevice, ImpossibleTravel, and UntrustedIP. To learn more, read Adaptive MFA: How it works. Each assessment has its own confidence score, and each confidence score has an associated action:
Confidence scoreDescriptionAction
lowLogin transaction does not match patterns previously displayed by user.Require MFA.
mediumLogin transaction somewhat matches patterns previously displayed by user.Do not require MFA.
highLogin transaction closely matches patterns previously displayed by user.Do not require MFA.
neutralN/A. Reserved for future use.N/A. Reserved for future use.
The following table describes high-risk scenarios that result in a low confidence score:
User StateDesired Login FrictionDesired Enrollment PolicyImplementation
Enrolled in MFADo not require MFAN/A (user already enrolled)Use an Action to bypass MFA
Not enrolled in MFARequire email verificationSkip enrollment (do not collect additional authenticators)Default behavior (no MFA-related Action)
Not enrolled in MFARequire email verificationRequire MFA enrollment (collect additional authenticator)Use an Action to force MFA enrollment (template available)
The following table describes low-risk scenarios that result in a high confidence score:
User StateDesired Login FrictionDesired Enrollment PolicyImplementation
Enrolled in MFANo frictionN/A (user already enrolled)Default behavior (no MFA-related Action)
Not enrolled in MFANo frictionSkip enrollment (do not collect additional authenticators)Default behavior (no MFA-related Action)
Not enrolled in MFANo frictionRequire MFA enrollment (collect additional authenticator)Use an Action to force MFA enrollment (template available)
If you want to implement your own method for evaluating the overall confidence score of different scenarios, you can use the data available in the the riskAssessment object, which contains the overall confidence score, versioning information, and details of the individual assessments. You can view the full description, properties, and values of the riskAssessment object in the post-login Actions trigger riskAssessment reference.

Action result outcomes

Actions that trigger MFA take precedence over default Adaptive MFA behavior.
If any of your Actions trigger MFA based on confidence score, the default Adaptive MFA policy triggers MFA when the confidence score is low. The following table shows the possible outcomes based on the combination of Actions and default Adaptive MFA policy actions.
Action resultAdaptive MFA actionOutcome
UnauthorizedTrigger MFAUnauthorized
UnauthorizedNo MFA RequiredUnauthorized
Trigger MFATrigger MFATrigger MFA
Trigger MFANo MFA RequiredTrigger MFA
No MFA RequiredTrigger MFATrigger MFA
No MFA RequiredNo MFA RequiredNo MFA Required

Action templates

Auth0 provides two Action templates based on Adaptive MFA for you to customize: Adaptive MFA and Require MFA Enrollment.

Adaptive MFA template

This template provides an example and starting point for how to build a custom business flow using individual risk assessments.
/**
* Handler that will be called during the execution of a PostLogin flow.
*
* @param {Event} event - Details about the user and the context in which they are logging in.
* @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.
*/
exports.onExecutePostLogin = async (event, api) => {
    // Decide which confidence scores should trigger MFA, for more information refer to
    const promptConfidences = ['low', 'medium'];

    // Example condition: prompt MFA only based on the NewDevice
    // confidence level, this will prompt for MFA when a user is logging in
    // from an unknown device.
    const confidence =
        event.authentication?.riskAssessment?.assessments?.NewDevice
            ?.confidence;
    const shouldPromptMfa =
        confidence && promptConfidences.includes(confidence);

    // It only makes sense to prompt for MFA when the user has at least one
    // enrolled MFA factor.
    const canPromptMfa =
        event.user.multifactor && event.user.multifactor.length > 0;
    if (shouldPromptMfa && canPromptMfa) {
        api.multifactor.enable('any', { allowRememberBrowser: true });
    }
};

Require MFA Enrollment template

This template demonstrates how you could enforce MFA enrollment when using a standard or Adaptive MFA policy. It uses event.user.multifactor to check if the user is enrolled in MFA, and if they’re not, prompts for enrollment.
/**
* Handler that will be called during the execution of a PostLogin flow.
*
* @param {Event} event - Details about the user and the context in which they are logging in.
* @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.
*/
exports.onExecutePostLogin = async (event, api) => {
    if (!event.user.multifactor?.length) {
        api.multifactor.enable('any', { allowRememberBrowser: false });
    }
};

Action use cases

Here are some suggestions for how to build custom Actions based on your use case.
Assess the riskAssessment.confidence property, and then compare it with the constants high, medium, or low:
exports.onExecutePostLogin = async (event, api) => {
  const { riskAssessment } = event.authentication || {};
  const riskIsMedium = riskAssessment && riskAssessment.confidence === 'medium';

  if (riskIsMedium) {
    // ....
  }
}
Confidence scores are discrete values—not in a range—so you cannot use comparison operators (such as < or >) to evaluate multiple values in a single condition.Use multiple conditions to logically combine all the confidence scores you want to handle. For example, if you want to know when the confidence score is greater than low, check if it’s equal to medium or high:
exports.onExecutePostLogin = async (event, api) => {
  const { riskAssessment } = event.authentication || {};
  const riskIsMediumOrHigh = riskAssessment && 
                                  (riskAssessment.confidence === 'high' || 
                                   riskAssessment.confidence === 'medium');

  if (riskIsMediumOrHigh) {
    // ...
  }
}
The riskAssessment object is saved in your tenant logs. You can view log entries to see the risk assessment score and the determining factors (reasons).You can view the riskAssessment object and report the results elsewhere. For example, you can send an email or save a record in an external database.
exports.onExecutePostLogin = async (event, api) => {
  const { riskAssessment } = event.authentication || {};
  const riskIsLow = riskAssessment && riskAssessment.confidence === 'low';

  if (riskIsLow) {
    // log(externalDatabase, riskAssessment);
  }
}
Use the assessments object to access the details for individual assessments, including the code property:
exports.onExecutePostLogin = async (event, api) => {
  const { riskAssessment } = event.authentication || {};
  const { ImpossibleTravel } = riskAssessment && riskAssessment.assessments;

  if (ImpossibleTravel.code === 'impossible_travel_from_last_login') {
    // ...
  }
}
Use the assessments object to access the details for individual assessments, and then use the confidence property, the code property, or both.
Use the assessments object to access the details for individual assessments, including the code property.Block the login transaction from completing by returning the callback function with an UnauthorizedError object as the error parameter. The UnauthorizedError object always sets error to unauthorized, but you can customize the error_message:
exports.onExecutePostLogin = async (event, api) => {
  const { riskAssessment } = event.authentication || {};
  const { ImpossibleTravel } = riskAssessment && riskAssessment.assessments;

  if (ImpossibleTravel.code === 'impossible_travel_from_last_login') {
    return api.access.deny('Login blocked due to impossible travel detected.')
  }
}
This redirects the user back to the application’s callback URL with the error and error_message parameters included.
Auth0 automatically assigns a low confidence score if there is any sort of failure performing the risk assessment.To mitigate this scenario, use the assessments object to inspect the code property for each individual assessment and check if the value is set to assessment_not_available.

Learn more