We have switched from Keycloak authentication to Firebase authentication and I need to migrate the users from Keycloak to Firebase using Python.
These users were stored by Keycloak in a Postgres DB. I used the Admin SDK to get the users out of the Postgres DB and have already successfully saved them to Firebase, using the instructions from Firebase: https://firebase.google.com/docs/auth/admin/import-users. The users are also successfully created and displayed in Firebase. The users who logged in with the identity provider like Google Auth. can log in without problems. Only those users who log in with email and password get an error message INVALID_PASSWORD, CODE:400.
My guess then was that the salt and/or hash are not passed correctly or that they are stored wrong (keycloak hashing alg.:pbkdf2_sha256 and iterations: 27500 (default value)).
When passing in Python to the firebase attributes: password_hash and password_salt a byte array is expected, which I convert with bytes(user.password_hash/.password_salt, encoding='utf-8') to a byte
array. In some posts (like https://github.com/firebase/firebase-admin-python/issues/182) I saw that I should decode the hash in base64 and after that it should already work.
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)
But what I know is that keycloak already stores hash and salt in format base 64 into the DB. The salt was also tested by me with an online tool (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).
However, there could be something wrong with auth.UserImportHash, but I also got this code from the Firebase instructions.
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
Also, I output the users again before saving, but password_hash and password_salt are also saved with b'hash/salt' as in the Firebas instructions.
"If the salt is stored as a base64 encoded string, you have to decode that before passing it to Firebase. The code above only seems to decode the password hash." hiranya911
following code worked as expected:
...
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')),
...
Related
I'm using Firebase authentication to manage my users accounts.
Now, I need to change the uid of the users, then I'm trying to delete the user and import it again with the same password using python.
I'm trying to follow the documentation. But I might be missing something.
So, in the Firebase authentication page, I'm going to menu (in the right upper corner) and getting the base64_signer_key and base64_salt_separator values.
And trying to use the code below to delete the user, import the user and update the other fields:
for user in auth.list_users().iterate_all():
if user.email == 'myname#yahoo.com':
newId = CPF(cpf()).rawValue
oldId = user.uid
print('User: {}'.format(user._data))
# Delete the user
auth.delete_user(oldId)
# Recreate the user
users = [
auth.ImportUserRecord(
uid=newId,
email=user.email,
password_hash=user.password_hash.encode('utf-8'),
password_salt=None
),
]
hash_alg = auth.UserImportHash.scrypt(
key=base64.b64decode(base64_signer_key),
salt_separator=base64.b64decode(base64_salt_separator),
rounds=8,
memory_cost=14
)
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 Exception as e:
print(e)
# Update user
auth.update_user(
newId,
phone_number=user.phone_number,
email_verified=user.email_verified,
display_name=user.display_name,
disabled=user.disabled
)
I'm following this documentation https://firebase.google.com/docs/auth/admin/import-users#import_users_with_firebase_scrypt_hashed_passwords
I'm able to delete and recreate the user, but when I try to login with the same user/password I'm getting FirebaseInvalidPasswordError.
What should I do recreate the user with same password and be able to authenticate in the standard way ?
After many tests, maybe I've managed to find a working way to solve the problem.
First of all, if you have created a new service account private key, go to GCP console here https://console.cloud.google.com/iam-admin/iam?authuser=0&project=[your_firebase-proect-id] and make sure your service account have the "Firebase Authentication" admin rights
(note the service account)
(check permission)
This was my first problem since without that permission, the firebase admin SDK always returns an empty password_salt and the string "UkVEQUNURUQ=" for the password_hash (which translates to "REDACTED").
Once I got the correct password hash and salt for user, your code should looks like this
# Recreate the user
users = [
auth.ImportUserRecord(
uid=newId,
email=user.email,
password_hash=base64.urlsafe_b64decode(user.password_hash),
password_salt=base64.urlsafe_b64decode(user.password_salt)
),
]
Note the base64.urlsafe_b64decode part? I've tried to manually export my probject users with the firebase cli though
firebase auth:export --project [project-id] users.csv
and noticed a big difference: Python password hash was
utfZLdz4phgAnRIKRUOxxFTKmbUEenbV1CbkQC0o4iorXpx-BJsdwofjAQkb1mUAgs_sO49cBv_lT8QuCztRzA== while CSV password hash was utfZLdz4phgAnRIKRUOxxFTKmbUEenbV1CbkQC0o4iorXpx+BJsdwofjAQkb1mUAgs/sO49cBv/lT8QuCztRzA== (in python slashes are undercores)
Don't know if my approach would cover all cases, but exporting auth from the cli and comparing their hashes with the python ones could lead you to solve further cases.
I want to create an admin application to monitor data collection. For this the user registration process is based on the database access i.e, when we create a new database user through MongoDB atlas, they will immediately be able to log into the admin application with their database username and password. How do I get a mongo document/response containing the list of database users and their hashed passwords using python?
I made a function that accepts the Mongo database user's username and password and uses that to connect to a Mongo client. I then attempt to perform a basic read operation (read operation requires the least user privilege) If the read op is successful then it means the username and password provides are authentic and I return true, else check if the operation failed because of a failed authentication and return false.
from pymongo import MongoClient
from pymongo.errors import OperationFailure
def check_dbuser(username, password):
""" Attempts to connect to mongo atlas using the username and password. It then attempts a basic operation which is
to list the database names of the cluster. If the operation works, the username and password are authentic and
return True.
Else if there is an OperationFailure we check that the error is due to failed authentication and return False
"""
auth = False
failed_message = 'bad auth Authentication failed.' # this is the err message returned on a failed authentication
uri = f'mongodb+srv://{username}:{password}#cluster-connection-string'
client = MongoClient(uri)
try:
client.list_database_names()
except OperationFailure as e:
assert(e.details['errmsg'] == failed_message) # assert that the error message is that of an authentication failure
else:
auth = True
return auth
I'm making a QGIS plugin in which I ask the for authentication object (with QgsProcessingParameterAuthConfig) for the PostgreSQL connection (which is already set in the connecions list of the user).
My goal is to take the login and password with PyQGIS and use these to connect with psycopg2.
The asked parameter QgsProcessingParameterAuthConfig returns a string with the identification key of the authentication object.
I can get the QgsAuthMethodConfig object with this key but the
password is empty.
I didn't found a method to access the password, nor other plugin doing that.
It is possible to know the SQLite database where the password is saved, but the are encrypted and I don't know the method to decrypt them.
So it seems like you do the following with the string (id):
# get the config id as a string
auth_method_id = self.parameterAsString(
parameters,
self.AUTH_CONFIG,
context
)
# get the application's authenticaion manager
auth_mgr = QgsApplication.authManager()
# create an empty authmethodconfig object
auth_cfg = QgsAuthMethodConfig()
# load config from manager to the new config instance and decrypt sensitive data
auth_mgr.loadAuthenticationConfig(auth_method_id, auth_cfg, True)
# get the configuration information (including username and password)
auth_cfg.configMap()
I got this from various places in documentation:
https://qgis.org/pyqgis/master/core/QgsAuthManager.html
https://qgis.org/pyqgis/master/core/QgsAuthMethodConfig.html
https://qgis.org/pyqgis/master/core/QgsProcessingParameterAuthConfig.html
Minor addition to isamson's helpful response above:
You will need to add QgsApplication, QgsAuthManager, QgsAuthMethodConfig to your from qgis.core import call, if not already there.
You will want to assign that last object to a variable, which you can then pull credentials out of:
auth_info = auth_cfg.configMap()
try:
user = auth_info['username']
password = auth_info['password']
I need to validate if servicenow login credentials are correct or not.
I am using pysnow
def validate_conn(self, data):
instance = data['url']
user = data['uname']
password = data['pwd']
try:
pysnow.client.Client(instance=instance, host=None, user=user, password='xfgdfgdf', raise_on_empty=None,
request_params=None, session=None)
print("valid")
except:
print("invalid")
return data['pwd']
In the above code I gave the invalid password so it have to come to the except block but i am getting valid as output. I need to validate if credentials are valid or not
The ServiceNow Web Services API does not provide a mechanism to validate credentials. One way to validate credentials from a Web Services client is to attempt to read a record that you know exists. One record that must exist is your own user record from sys_user.
The following code attempts to read your own user record from sys_user. If you are unable to read this record, then something must be wrong.
try:
client = pysnow.Client(instance=instance, user=user, password=password)
response = (client
.resource(api_path='/table/sys_user')
.get(query={'user_name': user})
.one())
print('credentials are valid')
except:
print('credentials are not valid')
Among the users in IAM, I want to programmatically get the list of all password enabled users. From AWS Console, I can easily spot them. But, how to get their list programmatically? I want to use python boto to get that.
I was reading up here http://boto3.readthedocs.io/en/latest/reference/services/iam.html#iam, but by most of the ways listed in this doc, I can only see option of using 'PasswordLastUsed' which would be null in three cases
The user does not have a password
The password exists but has never been used
there is no sign-in data associated with the user.
So just by checking if 'PasswordLastUsed' is null I can not claim that user does not have password and thereby, can not get all the users with password. Am I missing something here? Any other way or any other python resource I can use to do this?
import boto3
iam = boto3.resource('iam')
def isPasswordEnabled(user):
login_profile = iam.LoginProfile(user)
try:
login_profile.create_date
print True
except:
print False
>>> isPasswordEnabled('user1')
True
>>> isPasswordEnabled('user2')
False
I can see that there is a way, just where you would expect it to be...
http://boto3.readthedocs.io/en/latest/reference/services/iam.html#IAM.Client.get_credential_report
In the report, field password_enabled set to false indicates no password.
You could use the GetLoginProfile API request to determine if an IAM user has a login profile or not. If there is no login profile associated with the user this request will return a 404 response. Some code like this should work:
import boto3
iam = boto3.client('iam')
user_name = 'bob'
try:
response = iam.get_login_profile(UserName=user_name)
except Exception, e:
if e.response['ResponseMetadata']['HTTPStatusCode'] == 404:
print('User {} has no login profile'.format(user_name))