Last Updated: Oct 16, 2024
Overview
Auth0 allows multiple users to register the same number of SMS MFA.
This article explain why this may happen and if it is possible to enforce any checks to determine whether the same number is shared across multiple user accounts.
Applies To
- SMS Multifactor Authentication (MFA)
- Phone Numbers
Cause
In general terms, the users in separate connections (databases) are considered different unrelated users, even if the details such as email, phone number email are the same. For example, one user instance resides in the Regular Database connection (auth0|…) and another in the Passwordless Database connection (sms|…).
Multiple accounts can have the same phone number registered as MFA, even within the same connection. This is by design and there is currently no built-in option to prevent this from happening.
Solution
There is no simple out-of-the-box approach for this use case due to the fact that there is no available method to search users by phone numbers (the phone_number field is used by Passwordless connections and not for MFA enrollments). For more information, refer to Search Query Syntax - Searchable Fields
Despite the workaround mentioned below, it’s important to keep in mind that enforcing uniqueness for MFA phone numbers is generally not recommended. This is due to various factors like the possibility of enumeration attacks, users requiring to have multiple accounts that may need sharing the phone number for SMS, or users depending on their family’s phone numbers. However, if your business case requires these phone numbers to be unique, read on.
The only way to look up a number that is used for MFA is to iterate through every user, calling the endpoint Get List of Authentication Methods. However, that approach would also require disabling phone number obfuscation, as mentioned in How to get SMS MFA enrollments with full phone numbers. This is a very intensive operation every time a user tries to enroll a new phone number, as every existing user would have to be interrogated behind the scenes before allowing the enrolment.
The best way to accomplish this is to use the “Send Phone Message” Action trigger, which could be set to filter only enrolment attempts. It could look up “taken” phone numbers on a customer-hosted database and then throw an error if there is a match. Here is an example of how this could implemented in the Action:
exports.onExecuteSendPhoneMessage = async (event, api) => {
if (event.message_options.action === 'enrollment') { //only run for enrollments
const result = // make await call to external database here, passing the value of event.message_options.recipient and returning a boolean result
if (result === true) {
throw new Error("Number in use");
} else {
//update external database with new number
}
}
// call your SMS provider here
};
The downside to using the Action is that the Classic Universal Login experience does not show an error. It quietly fails the enrollment (although a tenant log is created with the error text provided). By contrast, the New Universal Login will throw an error at the end user, and the error message text can be customized. For more information, refer to Customize New Universal Login Text Prompts