firebase-tools: Using auth:import with bcrypt results in login returning auth/internal-error

I am trying to migrate my users (on a system using bcrypt) to firebase, using auth:import. I can’t seem to get the bcrypt mode to work and was hoping for a suggestion or working example.

Here’s an example:

Let’s assume a password of “none”.

If we go here: https://www.dailycred.com/article/bcrypt-calculator

and create a bcrypt for that password with 10 rounds, we will get something like this:

$2a$10$Kuvv6ATRsbtHnJJHB359WeKrC6ImbEzY0wtCmB21KS39Cw5sMecMK

If we deconstruct the bcrypt above, using this template:

$2a$10$TwentytwocharactersaltThirtyonecharacterspasswordhash
$==$==$======================-------------------------------

we get:

password hash: KrC6ImbEzY0wtCmB21KS39Cw5sMecMK salt: Kuvv6ATRsbtHnJJHB359We

bcrypt already has these components base64 encoded, thus we should not have to base64 encode them to pass to auth:import. This would result in an import object like here:

{
	"users": [
		{
			"localId": "abcd-1234-xyz",
			"email": "somebody@gmail.com",
			"emailVerified": false,
			"passwordHash": "KrC6ImbEzY0wtCmB21KS39Cw5sMecMK",
			"salt": "Kuvv6ATRsbtHnJJHB359We",
			"displayName": "A Person",
		}
	]
}

Alas, if you use this (asssume the above in a file named userAccounts.json):

firebase auth:import --hash-algo=‘BCRYPT’ ./userAccounts.json

the import will succeed, but any logins with “signInWithEmailAndPassword” will fail with this:

{code: “auth/internal-error”, message: “{“error”:{“errors”:[{“domain”:“global”,“reason”:“b…code: 7”}],“code”:503,“message”:“Error code: 7”}}”}

What am I missing?

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 1
  • Comments: 20 (7 by maintainers)

Most upvoted comments

I see. The problem is about the 2y prefix in bcrypt hash. Only crypt_blowfish bcrypt implementation use 2y, nobody else adopts this and it’s identical to 2a. So solution will be replacing 2y with 2a.

Your example $2y$10$5IlDVsSEpAD.Knsmcx.7d.sc.XL2urf1GK9BiI/3swjf8iTP6Ljfu will be $2a$10$5IlDVsSEpAD.Knsmcx.7d.sc.XL2urf1GK9BiI/3swjf8iTP6Ljfu. And its base64 will be JDJhJDEwJDVJbERWc1NFcEFELktuc21jeC43ZC5zYy5YTDJ1cmYxR0s5QmlJLzNzd2pmOGlUUDZMamZ1. I verified it can work now.

I see your problem. You don’t need to deconstruct bcrypt hash string to get separate salt and resulting hash value. For bcrypt, the whole $2a$10$... should be used as passwordHash and leave salt blank. BTW, there’s no need to provide any parameter for bcrypt because cost parameter is already encoded in the whole hash string.

Let’s get back to your sample user. We have bcrypt hash string which is $2a$10$Kuvv6ATRsbtHnJJHB359WeKrC6ImbEzY0wtCmB21KS39Cw5sMecMK. We can calculate base64 encoded hash string using the following NodeJS code.

new Buffer('$2a$10$Kuvv6ATRsbtHnJJHB359WeKrC6ImbEzY0wtCmB21KS39Cw5sMecMK').toString('base64')

Result is JDJhJDEwJEt1dnY2QVRSc2J0SG5KSkhCMzU5V2VLckM2SW1iRXpZMHd0Q21CMjFLUzM5Q3c1c01lY01L and this string should be used as passwordHash.

I just tried to upload your sample user with correct passwordHash into my test project and then I can successfully signed in. You can have a try. Thanks.

Tried this based on the example from the docs, it’s not working when I try to sign in a user

process.env.FIREBASE_AUTH_EMULATOR_HOST = "localhost:9099"
const admin = require('firebase-admin');

const users = require('../sandbox/users.js')
    .map(u => ({
        uid: u._id,
        email: `${u.username}@example.com`,
        passwordHash: Buffer.from(u.password),
    }))

admin
    .initializeApp({ projectId: "my-project-123" })
    .auth()
    .importUsers(
        users,
        {
            hash: {
                algorithm: 'BCRYPT'
            },
        }
    )
    .then((results) => {
        results.errors.forEach((indexedError) => {
            console.log(indexedError.error.message);
            console.log(`Error importing user ${indexedError.index}`);
        });
    })
    .catch((error) => {
        console.log('Error importing users :', error);
    });

example password $2a$10$1pkiT/LB2vD90J4Wr58xbuoFD4uWTQKel5.wW9YNyUuQXt1oV1AAC

It’s scrypt