• Home
  • Categories
  • Recent
  • Popular
  • Pricing
  • Contact us
  • Docs
  • Login
FusionAuth
  • Home
  • Categories
  • Recent
  • Popular
  • Pricing
  • Contact us
  • Docs
  • Login

Custom Password Plugin fails to generate correct hash

Scheduled Pinned Locked Moved
Q&A
2
9
1.4k
Loading More Posts
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • K
    kejvidoko
    last edited by 21 Jul 2020, 11:57

    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 is df6cb6dad23e6f5d98e3edfe7f3492a137f53041093ca6adb39bca588abfb6dd and the password hash imported from the previous database is the same df6cb6dad23e6f5d98e3edfe7f3492a137f53041093ca6adb39bca588abfb6dd.

    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 to Admin@123+ using the admin panel and try to login I get the following logs

    1. 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.

    1 Reply Last reply Reply Quote 0
    • D
      dan
      last edited by 21 Jul 2020, 15:10

      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).

      --
      FusionAuth - Auth for devs, built by devs.
      https://fusionauth.io

      K 1 Reply Last reply 21 Jul 2020, 20:49 Reply Quote 0
      • K
        kejvidoko @dan
        last edited by 21 Jul 2020, 20:49

        @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.

        1 Reply Last reply Reply Quote 1
        • D
          dan
          last edited by 21 Jul 2020, 21:48

          @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?

          --
          FusionAuth - Auth for devs, built by devs.
          https://fusionauth.io

          K 1 Reply Last reply 21 Jul 2020, 22:04 Reply Quote 0
          • K
            kejvidoko @dan
            last edited by 21 Jul 2020, 22:04

            @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') ?

            1 Reply Last reply Reply Quote 0
            • D
              dan
              last edited by 22 Jul 2020, 19:59

              Hiya,

              I think the best thing would be to use the registrations key in the user 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.
              
              

              --
              FusionAuth - Auth for devs, built by devs.
              https://fusionauth.io

              K 1 Reply Last reply 23 Jul 2020, 11:05 Reply Quote 0
              • K
                kejvidoko @dan
                last edited by kejvidoko 23 Jul 2020, 11:05

                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)
                
                
                1 Reply Last reply Reply Quote 0
                • K
                  kejvidoko
                  last edited by 23 Jul 2020, 11:37

                  @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.

                  1 Reply Last reply Reply Quote 2
                  • D
                    dan
                    last edited by 23 Jul 2020, 16:01

                    That's great!

                    --
                    FusionAuth - Auth for devs, built by devs.
                    https://fusionauth.io

                    1 Reply Last reply Reply Quote 0
                    9 out of 9
                    • First post
                      9/9
                      Last post