Prevent Sign-Up When Email Address Already Exists Using an Action

Overview

This article explains how to prevent a new user from signing up on a Database or Passwordless connection if an account with the same email address already exists in the Auth0 tenant (e.g., from an SSO identity provider). This is achieved by using a Pre-User Registration Action to check for existing users by manually calling the Management API's /api/v2/users-by-email endpoint.

Applies To
  • Database Connection
  • Passwordless Connection
  • Action
  • Pre-User Registration
  • Management API
Cause

By default, the sign-up flow for a database or passwordless does not automatically check if the user's email already exists in other connections, such as an enterprise SSO connection. This can lead to the creation of duplicate accounts (one database, one SSO) associated with the same email address.

Solution

By implementing this pre-user-registration action to block duplicates, be aware that it may expose the application to a User Enumeration Attack. This action causes the sign-up form to respond differently based on whether an email already exists (e.g., "A user with this email address already exists. Please log in instead."). An attacker can use this difference in error messaging to test email addresses and determine which ones are already registered in the system.

This solution uses a Pre-User Registration Action to manually fetch a token for the Management API and then query the /api/v2/users-by-email endpoint. If a user with the specified email is found, the action blocks the new registration.

Create the Pre-User Registration Action and paste the following code into the onExecutePreUserRegistration editor. This code defines functions to get an access token and then uses that token to search for users by email. If any users are returned, it denies the registration.

exports.onExecutePreUserRegistration = async (event, api) => {
  console.log("PRE USER REGISTRATION ACTION RUNS");

  const getAccessToken = async () => {
    const myHeaders = new Headers();
    myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
    myHeaders.append("Accept", "application/json");

    const urlencoded = new URLSearchParams();
    urlencoded.append("grant_type", "client_credentials");
    urlencoded.append("client_id", event.secrets.CLIENT_ID);
    urlencoded.append("client_secret", event.secrets.CLIENT_SECRET);
    urlencoded.append("audience", event.secrets.AUDIENCE);

    const requestOptions = {
      method: "POST",
      headers: myHeaders,
      body: urlencoded,
      redirect: "follow"
    };

    const response = await fetch(`https://${event.secrets.DOMAIN}/oauth/token`, requestOptions);
    const responseJSON = await response.json();
    const accessToken = responseJSON.access_token;
    return accessToken;
  };

  const accessToken = await getAccessToken();

  const findByEmail = async (token) => {
    const myHeaders = new Headers();
    myHeaders.append("Accept", "application/json");
    myHeaders.append("Authorization", `Bearer ${token}`);

    const requestOptions = {
      method: "GET",
      headers: myHeaders,
      redirect: "follow"
    };

    const response = await fetch(`https://${event.secrets.DOMAIN}/api/v2/users-by-email?email=${event.user.email}`, requestOptions);
    const responseJSON = response.json();
    return responseJSON;
  };

  const result = await findByEmail(accessToken);

  if(result.length > 0){
    api.access.deny("EMAIL ADDRESS ALREADY REGISTERED!","A user with this email address already exists. Please log in instead.");
  }

  console.log("PRE USER REGISTRATION ACTION COMPLETED SUCCESSFULLY!");
};

Add the following secrets:

  • CLIENT_ID - the id value of the M2M Application with the scope read:users enabled.
  • CLIENT_SECRET - the secret value of the same M2M Application.
  • AUDIENCE - the value of the ManagementAPI's audience for the tenant. (e.g. https://{domain}/api/v2/ }
  • DOMAIN - the tenant's domain

 

Recommended content

No recommended content found...