You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ExportedUserRecord.password_hash returns the redacted placeholder instead of None #1007
Operating System version: macOS 26 (also expected on any platform)
Firebase SDK version: firebase-admin 7.8.0 (main at 740b9ee)
Firebase Product: auth
Python version: 3.11
Describe the problem
When the service account is not allowed to read password hashes, the Auth backend returns base64("REDACTED"), i.e. the JSON string "UkVEQUNURUQ=", in the passwordHash field of accounts:batchGet. PR #352 added code so that ExportedUserRecord.password_hash returns None in that case, and the docstring says: "If no password is set, or if the service account doesn't have permission to read the password, then this is None."
This does not work. The marker is defined as B64_REDACTED = base64.b64encode(b'REDACTED'), which is a bytes object (b'UkVEQUNURUQ='). The value from the decoded JSON response is a str, and in Python 3 'UkVEQUNURUQ=' == b'UkVEQUNURUQ=' is False. So password_hash returns 'UkVEQUNURUQ=', which looks like a real base64 hash. The existing unit test passes a bytes value into ExportedUserRecord, so it never covers the real JSON path.
The Node.js SDK compares against a string: const B64_REDACTED = Buffer.from('REDACTED').toString('base64'); and then response.passwordHash === B64_REDACTED.
Impact: code that follows the docs and checks user.password_hash is None to skip accounts without a readable hash (for example, when exporting users for import_users()) instead gets the placeholder, and may import the literal bytes REDACTED as the password hash.
Steps to reproduce:
importbase64fromfirebase_adminimportauthuser=auth.ExportedUserRecord({
'localId': 'user',
'passwordHash': base64.b64encode(b'REDACTED').decode(), # what the JSON response contains
})
print(repr(user.password_hash)) # 'UkVEQUNURUQ=', expected None
The same happens end to end through auth.list_users() when the mocked HTTP response body contains "passwordHash": "UkVEQUNURUQ=". A real hash such as "aGFzaA==" is returned unchanged, as expected.
Activity