使用 Python 将用户从 keycloak 迁移到 Firebase

Migrate Users from keycloak to Firebase with Python

我们已经从 Keycloak 身份验证切换到 Firebase 身份验证,我需要使用 Python 将用户从 Keycloak 迁移到 Firebase。 这些用户由 Keycloak 存储在 Postgres 数据库中。我使用 Admin SDK 将用户从 Postgres DB 中取出,并已使用 Firebase 的说明成功将它们保存到 Firebase:https://firebase.google.com/docs/auth/admin/import-users。用户也已成功创建并显示在 Firebase 中。使用身份提供者登录的用户喜欢 Google Auth。可以毫无问题地登录。只有使用电子邮件和密码登录的用户才会收到错误消息 INVALID_PASSWORD、CODE:400。 我当时的猜测是 salt and/or 哈希没有正确传递或者它们存储错误(keycloak 哈希算法:pbkdf2_sha256 和迭代:27500(默认值))。 将 Python 传递给 firebase 属性时:password_hash 和 password_salt 需要一个字节数组,我将其转换为 bytes(user.password_hash/.password_salt, encoding ='utf-8') 到一个字节 大批。在一些帖子中(比如 https://github.com/firebase/firebase-admin-python/issues/182),我看到我应该解码 base64 中的哈希,然后它应该已经可以工作了。

users = []
for user in list_users:        
    record = auth.ImportUserRecord(
        uid=user.uid,
        email= user.email,
        display_name= user.display_name,
        email_verified= user.email_verified,
        password_hash= None if user.password_hash is None else ab64_decode(bytes(user.password_hash, encoding='utf-8')),
        password_salt= None if user.password_salt is None else bytes(user.password_salt, encoding='utf-8'),
        provider_data= None if user.providers == [] else getProviderRecords(user.providers),
    )
    users.append(record)

但我所知道的是,keycloak 已经将 hash 和 salt 以 base 64 格式存储到数据库中。盐也是我用在线工具测试的(https://8gwifi.org/pbkdf.jsp) and it could validate. When saving in Firebase I don't get an exception and I also reconstructed the saving of the hash (https://github.com/firebase/firebase-admin-python/blob/master/firebase_admin/_user_import.py)。 但是,auth.UserImportHash 可能有问题,但我也从 Firebase 说明中获得了这段代码。

iteraions_round=27500
hash_alg = auth.UserImportHash.pbkdf2_sha256(rounds=iteraions_round)
try:
    result = auth.import_users(users, hash_alg=hash_alg)
    print('Successfully imported {0} users. Failed to import {1} users.'.format(
        result.success_count, result.failure_count))
    for err in result.errors:
        print('Failed to import {0} due to {1}'.format(users[err.index].uid, err.reason))
except exceptions.FirebaseError:
    # Some unrecoverable error occurred that prevented the operation from running.
    pass

此外,我在保存之前再次输出了用户,但是 password_hash 和 password_salt 也像 Firebas 指令中那样用 b'hash/salt' 保存。

“如果盐存储为 base64 编码字符串,则必须在将其传递给 Firebase 之前对其进行解码。上面的代码似乎只对密码哈希进行解码。” hiranya911

以下代码按预期运行:

...
password_hash= None if user.password_hash is None else ab64_decode(bytes(user.password_hash, encoding='utf-8')),
password_salt= None if user.password_salt is None else ab64_decode(bytes(user.password_salt, encoding='utf-8')),
...