api.authentication.enrollWithAny({type: 'otp'}); not working in login action

Hey,

I’m trying to force MFA enrollment in a post login action. I’m able to successfully challenge users that are already enrolled in MFA, but when I try to enroll users into MFA, it’s sending the user straight through authentication successfully without asking them to enroll.

This is the code I have in my action:

exports.onExecutePostLogin = async (event, api) => {
  api.user.setUserMetadata("login_location", event.request.geoip.cityName);
  api.user.setUserMetadata("last_login_date", Date.now());
  
  // If user has enrolled in MFA...
  if (event.user.enrolledFactors.length) {

    // If login location has changed, require MFA.
    if (event.user.user_metadata.login_location != event.request.geoip.cityName) {
      api.multifactor.enable("any");
    }

    // If last login date is > than 30 days ago, require MFA.
    const THIRTY_DAYS = 30 * 24 * 60 * 60 * 1000;
    if (event.user.user_metadata.last_login_date < Date.now() - 1000) {
          api.multifactor.enable("any");
    }
  }
  
  // If user not enrolled in MFA...
  else {
    api.authentication.enrollWithAny([{type: 'webauthn-roaming'}, {type: 'otp'}]);
  }

}

My expectation from the above code is as follows:

If the user has previously enrolled in MFA and they meet either of the two requirements needed for MFA (location change or last login date > 30 days ago - they are challenged with the MFA.

If the user hasn’t previously enrolled then they should be sent to the screen where they can scan a QR code for authenticator app and enroll with MFA.

As it stands, the api.authentication.enrollWithAny([{type: 'webauthn-roaming'}, {type: 'otp'}]); line isn’t doing anything. The user is authenticated without any MFA challenge or enrollment.

I am having a very similar issue. Any help would be much appreciated!

Hey Alex,

I’ve managed to get this working eventually using the following:

exports.onExecutePostLogin = async (event, api) => {
  api.user.setUserMetadata("login_location", event.request.geoip.cityName);
  api.user.setUserMetadata("last_login_date", Date.now());
  
  // If user has enrolled in MFA...
  if (event.user.enrolledFactors.length) {

    // If login location has changed, require MFA.
    if (event.user.user_metadata.login_location !== event.request.geoip.cityName) {
      api.multifactor.enable("all");
    }

    // If last login date is > than 30 days ago, require MFA.
    const THIRTY_DAYS = 30 * 24 * 60 * 60 * 1000;
    if (event.user.user_metadata.last_login_date < Date.now() - THIRTY_DAYS) {
          api.multifactor.enable("all");
    }
  }
  
  // If user not enrolled in MFA...
  else {
    api.multifactor.enable('any');
    api.authentication.enrollWithAny([{type: 'webauthn-roaming'}, {type: 'otp'}]);
  }

}

I couldn’t see this documented anywhere, but it looks like you also need to enable multifactor before trying to force enrolling. So adding this line is what fixed it for me:

api.multifactor.enable('any');

Note that this requires the MFA option in the Auth0 settings to be set to “Never”.

Hope this helps!

Hey Luke,
Thanks for your reply!
For some reason when I try this, no matter what I do, I receive an SMS code. I can’t seem to enroll in any ‘new’ MFA methods if I already have one set up. Not sure if you ran into this same problem but any advise would be much appreciated!