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.
Related
I am currently using the Pyrebase wrapper to get information (such as their email and created date) of all users. I tried looking through the documentation and cross reference it to Pyrebase documentation however i don't seem to get what i'm looking for. Currently i have tried this:
import pyrebase
config={all required information, including path to service account .json file}
firebase=pyrebase.initialize_app(config)
db=firebase.database()
auth=firebase.auth()
extract_user = db.child('users').child('userId').get()
for x in extract_user.each():
print(x.val())
auth.get_account_info(user[x.val()])
However i still failed, i know im missing something but im not sure what.
Note: I saved the users userID in the database under userId. So i looped through each and every ID to be used in the 'get_account_info'
Any suggestions or ways i can get this to be done?
from firebase_admin import credentials
from firebase_admin import auth
cred = credentials.Certificate("./key.json")
initialize_app(cred, {'databaseURL' : "your database..."})
page = auth.list_users()
while page:
for user in page.users:
print("user: ", user.uid)
page = page.get_next_page()
Then, after you get user id that looks like "F5aQ0kAe41eV2beoasfhaksfjh2323alskjal" you can see the actual email by:
user = auth.get_user("F5aQ0kAe41eV2beoasfhaksfjh2323alskjal")
print("user email: ", user.email)
The db.child('users').child('userId').get() in your code reads users from the Realtime Database, where they'll only exist if your application added the there explicitly. Adding a user to Firebase Authentication does not automatically also add it to the Realtime Database.
While Pyrebase allows you to initialize it with a service account, it doesn't replicate all administrative functionality of the Firebase Admin SDKs. As far as I can see in Pyrebase's code, Pyrebase's does not implement a way to list users.
Consider using the Firebase Admin SDK, which has a built-in API to list users.
I'm new to Flask and I'm trying out Flask-LDAP3-Login.
I've followed the documentation here and i have it working which is great: https://flask-ldap3-login.readthedocs.io/en/latest/index.html
How would i go about authenticating a user based on whether they are a member of a specific group? I see the docs mention group filtering but i'm not sure how to put it all together.
If anyone is curious, i solved this myself doing the following:
First, i integrated flask-ldap3-login with Flask-SQLAlchemy using steps here - https://github.com/nickw444/flask-ldap3-login/issues/26
My save user method now looks like this:
#ldap_manager.save_user
def save_user(dn, username, data, memberships):
id=int(data.get("uidNumber"))
if 'group-goes-here' in data.get("memberOf"):
user=User.query.filter_by(id=id).first()
if not user:
user=User(
id=int(id),
dn=dn,
username=username,
email=data['mail'],
firstname=data['givenName'],
lastname=data['sn']
)
db.session.add(user)
db.session.commit()
return user
So basically provided the user enters valid LDAP credentials it goes to AD to retrieve their group memberships and its a simple if 'group-goes-here' in data.get("memberOf"): determines whether to save the user in my User model and return it back to the handler.
#auth.route('/login', methods=['GET', 'POST'])
def login():
# Redirect users who are not logged in.
form = LDAPLoginForm()
if form.validate_on_submit():
if form.user:
login_user(form.user)
else:
flash('Login Failed', 'warning')
return redirect(url_for('auth.login'))
return redirect(url_for('main.home'))
Hope this helps!
The LDAP_USER_OBJECT_FILTER can be used to check group membership. If a user is not found within the group, an authentication fail will be produced. This is taken from the flask-ldap3-login docs:
Specifies what object filter to apply when searching for users. Defaults to '(objectclass=person)'
Matching direct members of a group:
app.config['LDAP_USER_OBJECT_FILTER'] = '(objectclass=person)(memberOf=<DN of Group>)'
For nested group membership you can use LDAP_MATCHING_RULE_IN_CHAIN:
app.config['LDAP_USER_OBJECT_FILTER'] = '(objectclass=person)(memberOf:1.2.840.113556.1.4.1941:=<DN of Group>)'
The Microsoft Search Filter Syntax docs state:
This is a special "extended" match operator that walks the chain of ancestry in objects all the way to the root until it finds a match.
Do you have a ldap server ? if not go to https://www.openldap.org/ and follow instructions on how to install openldap server
if you prefer a docker container then go here
https://github.com/osixia/docker-openldap and follow steps to get the container up and running
then go here https://ldap3.readthedocs.io/tutorial.html
pip install ldap3 on the machine which has your python env or another python container (same bridge network as your ldap container)
open python console and type the following commands
>>> from ldap3 import Server, Connection, ALL
>>> server = Server('ipa.demo1.freeipa.org')
>>> conn = Connection(server)
>>> conn.bind()
True
first with this free ldap server ipa.demo1.freeipa.org
and then with your own ldap server ip address
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))
I have a webapp on google app engine that checks to see if a user is logged in (through the federated id) with the following code. I just changed domain names and now for some reason it isn't recognizing any of my current users. This is because the openID changes depending on the domain name which I found through a little testing. Is there any workaround or way to let these users log in?
user = users.get_current_user()
currentregistereduser = None
try:
user_db_qry = User.query(User.theid == user.federated_identity())
user_db_list = user_db_qry.fetch(1)
currentregistereduser = user_db_list[0]
# I go on here to do all of the stuff for a logged in user
#if the user does not exist yet
except IndexError:
logging.error("indexerror" + str(User.theid) + " and " + str(user.federated_identity()))
user_db = User(
name=user.nickname(),
email=user.email(),
theid=user.federated_identity(),
visits = 0,
medals = 0,
prestige = 1,
)
user_db.put()
#they go on to create their profile data here
self.redirect("/profile")
This seems to be a similar problem to the one faced by StackExchange when it implemented OpenID. You can see how they dealt with it here:
https://blog.stackoverflow.com/2010/04/openid-one-year-later/
From that page:
So our cross-site user account matching now works this way:
Match by GUID. This is something we generate and assign during account association, so it’s a perfect fingerprint.
match by OpenID URL. This works for the vast majority of OpenID providers.
match by OpenID provided email address … if you are on our trust whitelist. This works for those rare OpenID providers (currently, only Google GMail) who generate domain-specific identifiers.
You could also try using a OpenID Library. Several are listed here:
http://openid.net/developers/libraries/
Hope this helps.
im using FriendlyFormPlugin, but would like to retrieve the username that was input as part of the request.params, but its no longer there when i check. this way i can set the default for username if the password is incorrect. thanks
I think what you need to do is to setup a post login handler action when you setup the middleware. In that action you can then check params, set a session var, etc. I had to hook into here in order to create a message to the user that their login had failed. I check for a 'login_failed' param on the login form.
def post_login(self):
""" Handle logic post a user's login
I want to create a login_handler that's redirected to after login. This would
check
- if user was logged in, if not then send back to login
- if user is admin, go to job list
- adjust the max age on the existing cookie to XX remember me timeframe
"""
if auth.check(not_anonymous()):
log.debug('checked auth')
else:
# login failed, redirect back to login
log.debug('failed auth')
redirect_to(controller="root", action="login", login_failed=True)
# expire this cookie into the future
ck = request.cookies['authtkt']
response.set_cookie('authtkt', ck,
max_age=60*60*24*7,
path='/'
)
redirect_to(controller="job", action="list")
In response for more details, too big to add as another comment:
So I've got a few things you can look at. First, this is my docs I'm writing as a repoze 'summary' to help explain to other devs how this stuff works/terminology used:
http://72.14.191.199/docs/morpylons/auth_overview.html
I started out using the repoze sql quickstart plugin:
http://code.gustavonarea.net/repoze.what-quickstart/
I then ripped out their setup_sql_auth and modified it for our own needs since we do both SQL and LDAP auth in our apps. Go make sure to look at the plugin source for setup_sql_auth and go through it until you really understand what it's doing.
and since you asked on middleware config...
app = setup_morpace_auth(app, User, Group, Permission, meta.Session,
post_login_url='/root/post_login',
post_logout_url='/login',
log_level='debug',
log_file='stdout'
)