Import customers with hashed passwords into Magento 2

Joaquín Ruiz - 23rd August 2018

Import customers with hashed passwords into Magento 2
  • Date:   23rd August 2018
  • Comments:

      0

Import customers into Magento 2.

 

Whether you are migrating your site from Magento 1, or from another platform, like WordPress, Prestashop, Symfony… you likely have a customer database you are interested in migrating to your new Magento 2 site. Once you have imported customers to Magento 2 with personal data, hashed passwords, addresses, etc.. If you have not taken into account the change of the Magento 2 encryption in passwords, you will face problems:

 

 

Magento 2 customer password.

 

Magento 2 has changed how the customer passwords are stored, the password hash method. It still supports the MD5 encryption method, however, if you are not using an integration tool to import your customers, they might have to reset their password on their next login to your store… Really?

The reason is that Magento 2 customer encryption it is not a simple MD5 hash, it contains a salt, and a salt version. Therefore your customers cannot be authenticated with MD5 hash algorithm by default.

Link to Magento 2 core


Magento 2 : {password}:{salt}:{hash version}

Magento 1 : {password}:{salt}

In the image below you can see MD5 hashed passwords, and the new Magento 2 format, that is much longer, and it is structured in three parts.

The solution

 

No worries, your customers won’t need to reset their passwords again. You can write a simple module to extend the Magento 2 authentication method.

 

1. Write a Plugin

 

First of all, we create a Plugin \Magento\Customer\Model\ AccountManagement with a Before method to extend the  authenticate($customerId, $password) 

Eg.


<type name="Magento\Customer\Model\AccountManagement">
  <plugin name="JokiRuiz_LegacyAuthentication_AuthenticationPlugin"
          type="JokiRuiz\LegacyAuthentication\Plugin\AuthenticationPlugin" />
</type> 
/**
 * @param AccountManagement $subject
 * @param $username
 * @param $password
 */
public function beforeAuthenticate(
    AccountManagement $subject,
    $username,
    $password
) {
    if($this->_dataHelper->isEnabled()) {
        $this->_authenticatePasswordRequest($username, $password);
    }
}

 

 

2. Add Configurations to store the old salt and enable/disable the module

 


<section id="jokiruiz_legacyauthentication" 
    showInDefault="1" showInStore="1" showInWebsite="1" sortOrder="10"
    translate="label">
  <label>Legacy Authentication</label>
  <tab>jokiruiz</tab>
  <resource>JokiRuiz_LegacyAuthentication::config</resource>
  <group id="general" showInDefault="1" showInStore="1" showInWebsite="1" 
       sortOrder="10" translate="label">
    <label>General</label>

    <field id="enabled" showInDefault="1" showInStore="1" showInWebsite="1" 
         sortOrder="10" translate="label" type="select">
       <label>Enable MD5 Authentication</label>
       <comment/>
       <source_model>Magento\Config\Model\Config\Source\Enabledisable</source_model>
    </field>
    <field id="md5_salt" showInDefault="1" showInStore="1" showInWebsite="1" 
         sortOrder="10" translate="label" type="text">
       <label>MD5 Salt</label>
       <comment>Leave empty if salt is not used</comment>
    </field>

  </group> 
</section> 

 

 3. And finally, write the code the compatibility 😉

 

This example below will make it compatible with Prestashop, that is md5( COOKIE_KEY + password) but below you can put here whatever legacy encryption you need to make Magento 2 compatible.



/**
 * @param $username
 * @param $password
 * @throws InvalidEmailOrPasswordException
 */
private function _authenticatePasswordRequest($username,$password)
{
  ...
  $md5Hash = $customerSecure->getPasswordHash();  
  if (strlen($md5Hash) == self::MD5_HASH_LENGTH) {
    $md5Salt = $this->_dataHelper->getSalt();
    if ($md5Salt) {
      $password = $md5Salt.$password;
    }
    if ($md5Hash === md5($password)) {
      return true;
    }
    throw new InvalidEmailOrPasswordException(__('Invalid login or password.'));</pre>
  }
  ...
}

 

We have to make our Magento 2 compatible with the old encryption system. But by doing this you are making your Magento 2 less secure… so this has to be temporal solution. Ideally, we have this system until the majority of our customers have logged in the new system, and then we won’t need it anymore… How?

In order to do that, the strategy is going to be to update the password hashes from the old encryption to the new system. Therefore we let the customer to login in ‘legacy mode’ the first time, and when the login is succeed, we generate the new Magento 2 hashed and replace it (at this point we have the original password of the customer, so we can hash it in the Magento 2 way 🙂 ).

...
if (strlen($md5Hash) == self::MD5_HASH_LENGTH &&
     $paIsPasswordPatched->getValue() != '1') {
    if ($this->_authenticateMd5Password($customerSecure, $password)) {
        $customer->setCustomAttribute('jr_is_password_patched', 1);
        $customerSecure->setPasswordHash($this->createPasswordHash($password));
        $this->_customerRepository->save($customer);
    }
    else {
        throw new InvalidEmailOrPasswordException(__('Invalid login or password.'));
    }
}
...

Have you face this problem? Do you have a different solution? You couldn’t make it by following my steps? I’m very happy to get your feedback! You can contact me or write a comment below.

Happy coding!

The Author

Joaquín Ruiz
Computer Engineer, Senior Full-Stack Developer and Lead Developre. Joki has more than 8 years of experience working with multiple PHP frameworks. He knows Magento, WordPress, Laravel, Yii.. like the back of his hand ;)