MFA migrating from rules to action causes OTP Auth failed

Hello, we’ve migrated from rules to actions where we had conditional MFA enabled for certain users.
This however is causing OTP Auth Failed errors for users which have had MFA enabled before the migration. Manually deleting the MFA and having the users setup MFA again solves the issue.
How can I make this migration/transition uninterrupting for our users?

This is our action code used

const domainNeedsMFA = (userEmail) => {
	const domainsWithMFA = [
		'@example.com',
	];
	
	// check if the specific user needs MFA
	return domainsWithMFA.some((domain)=>{
		if (userEmail.endsWith(domain)){
			return true;
		}
		return false;
	});
}

/**
* 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) => {
	/** 
 	 * The following code block will skip this Action if Rule "MFA enforcement" 
 	 * was previously executed in the transaction in order to avoid duplication 
 	 * of logic.
 	 */ 
 	if (api.rules.wasExecuted("rul_XXXXXXXXX")) { 
 		return;
 	} 

	// check if the specific user needs MFA
	const mfaRequired = domainNeedsMFA(event.user.email) 
	if (!mfaRequired) {
		return;
	}

	// bypass mfa for refresh token requests
	// see https://auth0.com/docs/secure/multi-factor-authentication/customize-mfa#bypass-mfa-for-refresh-token-requests
	if (event.transaction?.protocol === 'oauth2-refresh-token') {
		return;
	}


	// force the user to setup MFA
	const enrolledFactors = event.user.multifactor || [];
	const mfaProvider = event.user.multifactor[0] || 'any'
	if (enrolledFactors.length === 0) {
		// The user has not enrolled in any MFA factor yet, trigger an MFA enrollment
		api.multifactor.enable('any', { allowRememberBrowser: false });
	}
	else {
		// check if session already did a MFA
		// if the array of authentication methods is valid and contains a method named 'mfa', mfa has been done in this session already
		if (
			!event.authentication ||
			!Array.isArray(event.authentication.methods) ||
			!event.authentication.methods.find((method) => method.name === 'mfa')
		) {
			api.multifactor.enable(
				mfaProvider,
				{
					// optional, defaults to true. Set to false to force authentication every time.
					// See https://auth0.com/docs/secure/multi-factor-authentication/customize-mfa#change-frequency-of-mfa-prompts for details
					allowRememberBrowser: false
				}
			);
		}
	}
};

Hi @japrescott,

Welcome to the Auth0 Community!

I have reviewed your code and can confirm that the logic is correct. I have tested your code on my end, and it worked as expected.

In this case, could you please check your tenant logs to see what might be causing the OTP Auth to fail?

And then, could you share your findings with me?

Cheers,
Rueben

1 Like

Hey @rueben.tiow

Thanks for reviewing the code and confirming that its not an obvious error.

Both test-users facing issues had setup multifactor on 2021-08-12T07:45:42.915Z and use guardian type. Have there been any changes to MFA/2FA in recent years at auth0?

Or is there any option/field within auth0 which would “reset” the MFA/2FA for all users somehow?

Following a redacted tenant log of a failed auth which just says OTP is invalid.

{
  "date": "2024-06-25T12:28:47.420Z",
  "type": "gd_auth_failed",
  "description": "Guardian - Second factor verification failed: invalid otp",
  "ip": "212.25.XX.XX",
  "user_agent": "Safari 17.5.0 / Mac OS X 10.15.7",
  "user_id": "google-oauth2|XXXXXXXXXXXXXXXXXXXXXXXX",
  "details": {
    "request": {
      "method": "POST",
      "path": "/api/verify-otp",
      "query": {},
      "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15",
      "body": {
        "type": "manual_input",
        "code": "*****"
      },
      "ip": "212.25.XX.XX",
      "auth": {
        "subject": "google-oauth2|XXXXXXXXXXXXXXXXXXXXXXXX",
        "strategy": "jwt_transaction_token",
        "scopes": ""
      }
    },
    "response": {}
  },
  "log_id": "00000000000000000000000000000000000000000",
  "tenant_name": "our-tenant-name",
  "_id": "00000000000000000000000000000000000000000",
  "isMobile": false,
  "id": "00000000000000000000000000000000000000000"
}

Any pointers/hints are greatly appreciated.

Best

Hi @japrescott,

Thanks for the reply.

AFAIK, there have been no changes to MFA/2FA in recent years. I don’t believe there is any option that enforces an MFA/2FA reset for all users, so this shouldn’t be possible. However, you can delete (reset) a user’s MFA enrollments through the Management API’s delete an authenticator endpoint.

Judging from the error log you shared, it looks to be an issue with the OTP being invalid.

There might be issues with the Guardian app being out of sync. Usually, in this case, you would need to re-enroll in MFA to resolve this issue.

Now, you mentioned previously that this issue originated after migrating from Rules to Actions.

Could you double-check if your test users can log in as usual if you disable your Action script? I am checking to see if back-tracking your changes addresses this issue.

Additionally, you can monitor your Rules/Action script success/failure using the Real-time Webtask Logs Extension with console.log() statements.

Thanks,
Rueben

1 Like

Hi @rueben.tiow

Thank you for your hints and validating that the code works as intended.

I now double checked the code with real customers and it indeed works as you had confirmed. Something must be wrong with our test users and I will thus reset their MFA.

Thanks for your support.

Best,
JAP

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.