Management API Jobs Users Import Fails

I am attempting to bulk import users via the users import job API endpoint but cannot successfully log in with the provided password after successful import. Our underlying code base is using the Elixir Comeonein library version 2.6. The users’ password hashes are generated using Bcrypt with a cost of 12 rounds and base64 encoded. It appears the custom_password_hash field does not allow the combination of bcrypt for the algorithm and base64 for the string encoding (see docs).

Examples of our attempts:

[
  {
    "custom_password_hash": {
      "algorithm": "bcrypt",
      "hash": {
        "value": "$2b$12$uyNzoPKSNtJKw0O9ddLrk.6fhRnUwIpfnpekf2PTFOJitVWsGdkHm"
      }
    },
    "email": "l+20210315C@tbot.com",
    "name": "l+20210315C"
  }
]

The above results in the error message in the UI:

“YOU NEED TO UPDATE YOUR PASSWORD BECAUSE THIS IS THE FIRST TIME YOU ARE LOGGING IN, OR BECAUSE YOUR PASSWORD HAS EXPIRED.”

The logs include the failure message:

Verification failed for the provided custom_password_hash: {‘algorithm’:‘bcrypt’,‘hash’:{‘encoding’:‘utf8’,‘value’:‘$2b$12$uyNzoPKS…’},‘salt’:{‘value’:‘’}}

A payload that includes the encoding also fails but with a different message:

[
  {
    "custom_password_hash": {
      "algorithm": "bcrypt",
      "hash": {
        "encoding": "base64",
        "value": "$2b$12$.CmuHxBer7oUKltamlml2euiG8/nLM2OC9krf10UnFGhAaS1yI2LS"
      }
    },
    "email": "l+20210315D@tbot.com",
    "name": "l+20210315D"
  }
]

The error from the job error_details endpoint:

[
  {
    "user": {
      "custom_password_hash": {
        "algorithm": "bcrypt",
        "hash": {
          "encoding": "base64",
          "value": "*****"
        }
      },
      "email": "l+20210315D@tbot.com",
      "name": "l+20210315D"
    },
    "errors": [
      {
        "code": "ONE_OF_MISSING",
        "message": "",
        "path": "custom_password_hash"
      }
    ]
  }
]

I suspect the issue is simply that the bcrypt algorithm requires utf8.

Any suggestions for how I might get this working? Thanks.

Hi @lance_at_tbot !
I believe you should be fine with utf8. This is the encoding of the “full” hash string (including the headers $2b$12$). What’s base64-encoded is the salt and hash portions of the string (i.e. uyNzoPKSNtJKw0O9ddLrk. is base64 representation of the salt, and 6fhRnUwIpfnpekf2PTFOJitVWsGdkHm is the base64 encoding of the hash).

Can you provide an example with its associated password? Maybe I can put a quick program to check it (outside of Auth0).

1 Like

@nicolas_sabena @andrew.hsu I ran an import job just a few minutes ago. Here is the JSON that was submitted:

[
  {
    "custom_password_hash": {
      "algorithm": "bcrypt",
      "hash": {
        "value": "$2b$12$YG8.lrv2zHkrtC6pamiQw.uZ0MP3cNuWRqC5En6vETGsTC8yDCwHS"
      }
    },
    "email": "lance+20210317A@thoughtbot.com",
    "name": "lance+20210317A"
  }
]

The plain text password is superS3CR3T!A. When I attempt to login using that email address and password I receive the following error in the UI:

image

The logs for sign in attempt are:

{
  "date": "2021-03-17T18:38:26.031Z",
  "type": "fp",
  "description": "Password change required.",
  "connection": "Username-Password-Authentication",
  "connection_id": "*****",
  "client_id": "****",
  "client_name": "*****",
  "ip": "*****",
  "user_agent": "Firefox 86.0.0 / Mac OS X 10.16.0",
  "details": {
    "error": {
      "message": "Password change required.",
      "reason": "Verification failed for the provided custom_password_hash: {'algorithm':'bcrypt','hash':{'value':'$2b$12$YG8.lrv2...'},'salt':{'value':''}}"
    }
  },
  "user_id": "auth0|60524abd9af3fa0019b3d149",
  "user_name": "lance+20210317A@thoughtbot.com",
  "strategy": "auth0",
  "strategy_type": "database",
  "log_id": "90020210317183831094031929055360732044418897773208797186",
  "_id": "90020210317183831094031929055360732044418897773208797186",
  "isMobile": false
}

So I wanted to test this outside Auth0, to understand if the bcrypt hash you are providing would work with a generic bcrypt library. I tested it using node.js, with this code:

const bcrypt = require('bcrypt');

// known good
check('$2b$12$dt9jJcPaShXeU6ivRbWUhuc0dPO6vgum9PLDfbGx7glOD5hxIPQeC', 'nico123!');
check('JDJiJDEyJGR0OWpKY1BhU2hYZVU2aXZSYldVaHVjMGRQTzZ2Z3VtOVBMRGZiR3g3Z2xPRDVoeElQUWVD', 'nico123!', 'base64')

// the one you provided, which doesn't work
check('$2b$12$YG8.lrv2zHkrtC6pamiQw.uZ0MP3cNuWRqC5En6vETGsTC8yDCwHS', 'superS3CR3T!A');

// using the salt from the provided string, I re-generated the hash and got this, which works
check('$2b$12$YG8.lrv2zHkrtC6pamiQw.pbi6bTh/qL9PdsRupK2iRp1sqI3oDP.', 'superS3CR3T!A');

function check(bcryptHash, plainTextPwd, hashEncoding = 'utf8') {

  const hashBuffer = Buffer.from(bcryptHash, hashEncoding);
  const reencodedHash = hashBuffer.toString('utf8');
  
  console.log(`Checking hash ${reencodedHash} for password ${plainTextPwd}`);

  if (bcrypt.compareSync(plainTextPwd, reencodedHash)) {
    console.log(`The password matches.`);
  } else {
    console.log(`The password does not match.`);
    
    if (reencodedHash.startsWith('$2a$') || reencodedHash.startsWith('$2b$')) {
      const salt = reencodedHash.substr(0, reencodedHash.length - 31); // the last 31 characters are the actual hash
      console.log('Salt from the provided hash:' + salt);
      const generatedHash = bcrypt.hashSync(plainTextPwd, salt);
      console.log('If we were to use the same salt, this is the generated hash:' + generatedHash);
    } else {
      console.log('The provided hash is not a bcrypt hash.')
    }
  };
}

I found this online tool and got the same results: https://bcrypt-generator.com/. So, essentially, Auth0 is giving these errors because the hash does not match the password.

Hope these tools help you test different variations and find what’s wrong with the data source.

1 Like

@nicolas_sabena Thank you for providing the Node example and confirmation. This helped diagnose the issue and we have things working now.

1 Like

We are here for you Lance!