How to securely authenticate users with an external database connection?

I don’t understand how to securely implement the external database login flow. Based on the examples provided in the documentation: Custom Database Action Script Execution Best Practices

You are supposed to hash the password before sending it to your external database API. But in our database we also store a separately hashed password. How are we supposed to authenticate a hashed password against another hashed password? The only way I can think of is to first retrieve the user’s password hash from the external database, then hash the plaintext password with the same salt, then send that hash back to the external database. This requires two API calls and also exposes an endpoint to retrieve password hashes for any user.

Am I missing something?

Hi @steve-ir,

There should be built-in Database Action scripts that you can use to implement this for you.

(Reference: Custom Database Action Script Templates)

However, if you decide to implement the login script yourself, you must hash the password before sending it to your external database API. You are supposed to take the password supplied by the user during login and encrypt it before sending it to your external database.

E.x

function login(userNameOrEmail, password, callback) {
  // Send credentials to external database API
  let hashedPassword = hash(password);

  let options = {
    url: "https://example.com/api/authenticate",
    body: {
      email: userNameOrEmail,
      password: hashedPassword
    }
  };

  send(options, (err, profileData) => {
    // Return error in callback if authentication is unsuccessful
    if (err) {
      return callback(new WrongUsernameOrPasswordError(userNameOrEmail, "My custom error message."));
    } else {
      // Return profile data in callback if authentication is successful
      let profile = {
        username: profileData.username,
        email: profileData.emailAddress,
        user_id: profileData.userId
      };

      return callback(null, profile);
    }
  });
}

See the Login Script Templates documentation.

Thanks,
Rueben

Hi @rueben.tiow thanks for the response.

I’m unclear on what you said here:

you must hash the password before sending it to your external database API. You are supposed to take the password supplied by the user during login and encrypt it before sending it to your external database.

Are we supposed to hash the plaintext password or encrypt it?
From Auth0’s own blog, there is a distinct difference between the two: Encoding, Encryption, and Hashing

If it really is supposed to be hashed, then the example script makes no sense because the user’s password is also stored as a hash in the external DB. We can’t compare two hashes without using the same salt, but the salt is unique for every password so the connection script cannot know that ahead of time. Thus we would need to make two api calls, one to retrieve the salt and the second to authenticate the user, which is very inefficient.

If the password should be encrypted, that would make a little bit more sense, but that isn’t how the example script is written. It would introduce a shared secret that both Auth0 and the external API would need to know and isn’t detailed in the documentation.

The last option I can think of is to retrieve the entire user profile from the external API, then verify the password on the connection script side.

Which is the recommended solution? Or is there something else I didn’t think of?

Hi @steve-ir,

Thanks for the reply.

You are supposed to encrypt the password, which is the recommended solution. You can find more details here.

Additionally, the script I shared is a pseudo-javascript example of how you could implement the login function. For language-specific examples, read Language-specific script examples.

Generally, you should be able to use one of those scripts to securely authenticate your users without having to build the script yourself. Have you had a chance to try it yet?

Thanks,
Rueben

1 Like

@rueben.tiow Ok thanks for the clarification.

The example you’ve pointed to here uses bcrypt to hash the password, which is not encryption and is not a reversible operation. The documentation needs to be updated to reflect this.

The second link correctly demonstrates how to use bcrypt to verify the password against dbPasswordHash but it doesn’t detail how to retrieve dbPasswordHash in the first place. Also, it’s not useable out of the box:

const msg = 'Please implement the Login script for this database connection ' +
    'at https://manage.auth0.com/#/connections/database';

That said, implementing these functions is pretty trivial besides the question about how to securely compare the plaintext password with the hashed value stored in our external database. I’m able to connect Auth0 to my external database API, and all of the scripts are working.

I am going to follow your recommendation to encrypt the plaintext password and then decrypt it on our end. I just would like to point out that the current state of documentation around this does not reflect that solution.