skip to Main Content

I’m importing user from an old Firebase project to another.
The import ends successfully with no errors but from the new application, user cannot authenticate with same credentials and the following error is returned:

[auth/wrong-password]: The password is invalid or the user does not have a password.

This is the code I’m using to migrate users:

private async Task<MigrationResult> MigrateUsers(AbstractFirebaseAuth authFrom, AbstractFirebaseAuth authTo, UserImportOptions options, string? pageToken = null, MigrationResult? result = null)
{
    result ??= new MigrationResult
    {
        Success = false,
        SuccessCount = 0,
        FailureCount = 0
    };
    
    try
    {
        /* DOCS
         * - GET PASSWORD HASH AND SALT: https://firebase.google.com/docs/auth/admin/manage-users?hl=en#password_hashes_of_listed_users
         */
        var usersToMigrate = await authFrom.ListUsersAsync(new ListUsersOptions
        {
            PageSize = 1000,
            PageToken = pageToken
        }).ReadPageAsync(1000);
    
        var users = usersToMigrate.Select(u => new ImportUserRecordArgs
        {
            Uid = u.Uid,
            PasswordHash = Encoding.ASCII.GetBytes(u.PasswordHash),
            PasswordSalt = Encoding.ASCII.GetBytes(u.PasswordSalt),
            Email = u.Email,
            EmailVerified = u.EmailVerified,
            PhoneNumber = u.PhoneNumber,
            DisplayName = u.DisplayName,
            PhotoUrl = u.PhotoUrl,
            Disabled = u.Disabled,
            CustomClaims = u.CustomClaims,
            UserProviders = u.ProviderData.Select(p => new UserProvider
            {
                Uid = p.Uid,
                DisplayName = p.DisplayName,
                PhotoUrl = p.PhotoUrl,
                Email = p.Email,
                ProviderId = p.ProviderId
            })
        });
    
        var importResult = await authTo.ImportUsersAsync(users, options);
    
        _logger.LogInformation($"{nameof(MigrateUsers)}: Successfully imported {importResult.SuccessCount} users");
    
        if (importResult.FailureCount > 0)
        {
            _logger.LogWarning($"{nameof(MigrateUsers)}: Failed to import {importResult.FailureCount} users");
            foreach (ErrorInfo indexedError in importResult.Errors)
                _logger.LogWarning($"{nameof(MigrateUsers)}: Failed to import user at index: {indexedError.Index} due to error: {indexedError.Reason}");
        }
    
        result.Success = true;
        result.SuccessCount += importResult.SuccessCount;
        result.FailureCount += importResult.FailureCount;
    
        if (!string.IsNullOrWhiteSpace(usersToMigrate.NextPageToken))
            return await MigrateUsers(authFrom, authTo, options, usersToMigrate.NextPageToken, result);
    
        return result;
    }
    catch (FirebaseAuthException ex)
    {
        _logger.LogWarning($"{nameof(MigrateUsers)}: {nameof(FirebaseAuthException)} | {ex.AuthErrorCode} | From tenant {(authFrom is TenantAwareFirebaseAuth ? (authFrom as TenantAwareFirebaseAuth)?.TenantId : "Default")} to tenant {(authTo is TenantAwareFirebaseAuth ? (authTo as TenantAwareFirebaseAuth)?.TenantId : "Default")}", ex);
    
        result.Success = false;
    
        return result;
    }
}

and this is the ImportUsersAsync option object:

var options = new UserImportOptions()
{
    Hash = hash
};

where hash is:

var hash = new Scrypt()
{
    Key = Encoding.ASCII.GetBytes("[base64_signer_key from the OLD project]"),
    SaltSeparator = Encoding.ASCII.GetBytes("salt separator from the OLD project"),
    Rounds = 8,
    MemoryCost = 14
}

From the documentation this code should be fine and when the user opens up the new app, all they need to do is sign in again and the password should be rehashed and everything should works fine. But this is not happening.

What am I doing wrong?

Thank you!

2

Answers


  1. Chosen as BEST ANSWER

    @Rainy sidewalks

    This is the config in the old project:

    enter image description here

    In the new project obviously the value for base64_signer_key is different but the algorithm and other parameters are the same.

    So I confirm that the Scrypt algorithm is the one used in the old project, based on the screenshot. I confirm that the values of [base64_signer_key from the OLD project] and "salt separator from the OLD project" are correct, I've triple checked this.

    Any idea?


  2. 1st please verify the following

    1.the hash object used for password hashing in the new project is correctly initialized with the values from the old project.
    Double-check that the [base64_signer_key from the OLD project] and "salt separator from the OLD project" values are correct and match the values used in the old project.

    2.Confirm that the Scrypt algorithm is the correct algorithm used for password hashing in the old project.(possibly it uses different algorithm, such as Bcrypt or PBKDF2)
    check the above and let us know

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search