Custom Password Plugin fails to generate correct hash
-
Description
I have deployed a fusion auth instance on AWS ECS. Fusion auth version 1.17.5. I create a python script that imported all my users from the current database to fusion auth using python client. I imported also the password hash and the salt.
I created a plugin in java and installed it on fusion auth.public class GyoPasswordEncryptor implements PasswordEncryptor { @Override public int defaultFactor() { return 1; } @Override public String encrypt(String password, String salt, int factor) { System.out.println("1. Enter Encrypt Method"); System.out.printf("Password: %s Salt: %s", password,salt); MessageDigest digest1; MessageDigest digest2; try { digest1 = MessageDigest.getInstance("SHA-256"); digest2 = MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException ex) { return null; } byte[] passwordBytes = password.getBytes(StandardCharsets.UTF_8); digest1.update(passwordBytes); byte[] passwordHash = digest1.digest(); StringBuilder sb = new StringBuilder(); for(int i=0; i< passwordHash.length ;i++) { sb.append(Integer.toString((passwordHash[i] & 0xff) + 0x100, 16).substring(1)); } String saltAndPasswordHash = salt+sb.toString(); digest2.update(saltAndPasswordHash.getBytes(StandardCharsets.UTF_8)); byte[] hashedPassword = digest2.digest(); sb = new StringBuilder(); for(int i=0; i< hashedPassword.length ;i++) { sb.append(Integer.toString((hashedPassword[i] & 0xff) + 0x100, 16).substring(1)); } String finalHash = sb.toString(); System.out.printf("Password Hash: %s",finalHash); return finalHash; } }
The password for one of the test users is:
HtZX28CECnaL
and the salt is:18f
. The generated hash from the script isdf6cb6dad23e6f5d98e3edfe7f3492a137f53041093ca6adb39bca588abfb6dd
and the password hash imported from the previous database is the samedf6cb6dad23e6f5d98e3edfe7f3492a137f53041093ca6adb39bca588abfb6dd
.When I try to login with this user the logs that are printed from the password plugin are:
1. Enter Encrypt Method Password: enumerate Salt: dGhpcw== Password Hash: adbc8778a3de37e8912616006d9eb2ab2507fc3d1d1b189bf2b635e11a4690a2
If I change the password from
HtZX28CECnaL
toAdmin@123+
using the admin panel and try to login I get the following logs1. Enter Encrypt Method Password: Admin@123+ Salt: kud+SSpxm1PQHNxzNihLcJ8EiQPiwe0mSGiJpUKjTT0= Password Hash: 1185fe9808d625dac019eb4c5662d83d0fb39d129349a3a30906c99dd5914983
But even in the second time that the password is passed successfully on the encrypt method and the hash generated is the same as the database the fusion auth fails to perform the login, it is still stuck in the login page, it doesn't redirect me to the redirect url.
-
Are you specifying the custom encryption scheme for your user? This is mentioned here: https://fusionauth.io/docs/v1/tech/plugins/password-encryptors and https://fusionauth.io/docs/v1/tech/apis/users (search for
encryptionScheme
). -
@dan Yes I am specifying the encryption schema during the user import. I am using python library to perform the import using import_users. This is how an object looks like on the import script.
template_request['users'].append({ 'sendSetPasswordEmail': False, 'skipVerification': True, "active": True, "birthDate": user.birthdate, "data": { "displayName": user.first_name + " " + user.last_name }, "email": user.email, "encryptionScheme": "gyo-password-encryptor", "expiry": 1571786483322, "factor": 24000, "firstName": user.first_name, "fullName": user.first_name + " " + user.middle_name + " " + user.last_name, "imageUrl": "http://65.media.tumblr.com/tumblr_l7dbl0MHbU1qz50x3o1_500.png", "insertInstant": 1331449200000, "lastName": user.last_name, "middleName": user.middle_name, "password": user.password, "passwordChangeRequired": False, "preferredLanguages": [ "en" ], "salt": user.salt, "timezone": "America/Denver", "twoFactorEnabled": False, "usernameStatus": "ACTIVE", "username": user.username, "verified": True })
The user object is the data that I am getting from the source database that I am trying to import on fusion auth. I am sure that the password plugin is specified correctly since I can see the custom logs that I have added on password plugin.
-
@kejvidoko said in Custom Password Plugin fails to generate correct hash:
But even in the second time that the password is passed successfully on the encrypt method and the hash generated is the same as the database the fusion auth fails to perform the login, it is still stuck in the login page, it doesn't redirect me to the redirect url.
Are you creating a registration for the user for the application in FusionAuth? You need to both create a user and create a registration for a user to associate them with a given application before they can login.
I'm not sure what's going on with the password hashing. Can you push all your code up to a github someplace so that I can take a look?
-
@dan How can I generate a registration for the user. I am using this API https://fusionauth.io/docs/v1/tech/apis/users#import-users here in the documentation doesn't mention anything about registration. Do I have to call this method too
fa_client.generate_registration_verification_id('email','application_id')
? -
Hiya,
I think the best thing would be to use the
registrations
key in theuser
object you are generating. That will associate the user with the application.From the import users section of the docs:
users[x].registrations [Array] Optional The list of registrations for the User. users[x].registrations[x].applicationId [UUID] Required The Id of the Application that this registration is for.
-
Hello @dan ,
I tried exactly as you said but the problem still persists. Here it is the changed script. I added the registration section with my application id and also changed the factor to 1"factor": 1
from fusionauth.fusionauth_client import FusionAuthClient import math from dal import DAL from migration_parameters import MigrationParameters from reconsile_users import reconcile_users from user import User try: # Parse command line arguments migration_parameter = MigrationParameters() fa_client = FusionAuthClient(migration_parameter.fusion_auth_api_key, migration_parameter.fusion_auth_base_url) total_number_of_records = -1 # Get total number of users in db db = DAL(configuration=migration_parameter) result = db.get_single("Select count(*) from users " "where migrated <> 1 OR migrated IS NULL") total_number_of_records = result[0] total_number_of_db_chunks = math.ceil(total_number_of_records / migration_parameter.db_chunk_size) for db_index in range(0, total_number_of_records, migration_parameter.db_chunk_size): current_db_chunk = db.get( "SELECT id, birthdate, age, username, anon_url, `type`, subtype, lock_type, messages_unread, salt, " "password, email, " "first_name, middle_name, last_name, phone_number, timezone_id, `_timezone_id`, street_address," "city_address, city_id, state_address, state_id, zip_code, country, lastupdate, active," "gender, birthday_input, contact_pref_manage, subscription, subscription_start, subscription_expires FROM " "users where migrated <> 1 OR migrated IS NULL" , db_index, migration_parameter.db_chunk_size) total_number_of_chunk_records = len(current_db_chunk) total_number_of_fa_chunks = math.ceil(total_number_of_chunk_records / migration_parameter.fa_chunk_size) for fa_index in range(0, total_number_of_chunk_records, migration_parameter.fa_chunk_size): current_fa_chunk = current_db_chunk[fa_index:fa_index + migration_parameter.fa_chunk_size] template_request = { 'sendSetPasswordEmail': False, 'skipVerification': True, "users": [] } import_successfully_user = [] import_successfully = [] # Prepare request for el in current_fa_chunk: try: user = User.parse(result=el) template_request['users'].append({ 'sendSetPasswordEmail': False, 'skipVerification': True, "active": True, "birthDate": user.birthdate, "data": { "displayName": user.first_name + " " + user.last_name }, "email": user.email, "encryptionScheme": "gyo-password-encryptor", "expiry": 1571786483322, "factor": 1, "firstName": user.first_name, "fullName": user.first_name + " " + user.middle_name + " " + user.last_name, "imageUrl": "http://65.media.tumblr.com/tumblr_l7dbl0MHbU1qz50x3o1_500.png", "insertInstant": 1331449200000, "lastName": user.last_name, "middleName": user.middle_name, "password": user.password, "passwordChangeRequired": False, "preferredLanguages": [ "en" ], "salt": user.salt, "timezone": "America/Denver", "twoFactorEnabled": False, "usernameStatus": "ACTIVE", "username": user.username, "verified": True, "registration": [ { "applicationId": "74e5f2cc-b485-47d5-94d7-8854747edd82" } ] }) import_successfully.append(str(el[0])) import_successfully_user.append(str(el[3])) except Exception as exx: print('Failed to process user with ID: ', el[0], ' with error: ', exx.args) fa_response = fa_client.import_users(template_request) if fa_response.response.status_code == 200: # everything is ok db.update_all("update users set migrated = 1 where id = %s ", [(record,) for record in import_successfully]) reconcile_users(migration_parameter, import_successfully_user) elif fa_response.response.status_code == 400 and 'unique' in fa_response.error_response['generalErrors'][0][ 'message']: # users are already imported on fusion auth but for some reasons are not stored on the db db.update_all("update users set migrated = 1 where id = %s ", import_successfully) reconcile_users(migration_parameter, import_successfully_user) else: print('Failed to import users with starting ID: ', current_fa_chunk[0][0], ' ending ID: ', current_fa_chunk[::len(current_fa_chunk) - 1][1][0]) template_request['users'].clear() except Exception as ex: print(ex)
-
@dan I finally managed to find the fix, it was related to password expiry field being an expired date and the factor number had to be 1. Thanks a lot for your support.
-
That's great!