How To Verify the Customer’s Passwords on the Edit Page in Magento 2?

When customers want to change their password on the edit page, it’s essential to verify their identity by checking their current password and to keep their accounts safe in Magento 2. 

Ensure that the new password is strong enough to protect their account from hackers. It’s also important to confirm that the new password hasn’t been used before to prevent potential security breaches. 

We will help you validate that the customer has entered their current password and make sure that the new password is different from the current one. This blog provides a step-by-step guide to implementing these measures in Magento 2.

Module Usage

  • Current Password Validation Implement a validation mechanism on the edit page to require customers to provide their current password before proceeding with the password change.
  • New Password Validation Enforce password complexity requirements, such as minimum length and inclusion of special characters, to ensure that the new password meets security standards.
  • Password Reuse Check Compare the new password with the customer’s previous passwords stored in the database to prevent reuse. If the new password matches any of the previous ones, prompt the customer to choose a different password.

Steps To Verify the Customer’s Passwords on the Edit Page in Magento 2

To create and verify the Customer’s Current and New Passwords follow the below steps:

Step 1: Create a “registration.php”

app/code/Dckap/CustomerEditValidate/registration.php

<?php
/**
* @package    Dckap_CustomerEditValidate
* @copyright  Copyright (c) 2023 DCKAP Inc (http://www.dckap.com)
* @license    http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
*/
\Magento\Framework\Component\ComponentRegistrar::register(
   \Magento\Framework\Component\ComponentRegistrar::MODULE,
   'Dckap_CustomerEditValidate',
   __DIR__
);

Step 2: Create a “Module.xml” file

The following path has: 

app/code/Dckap/CustomerEditValidate/etc/module.xml

<?xml version="1.0"?>
<!-- /**
* @package    Dckap_CustomerEditValidate
* @copyright  Copyright (c) 2023 DCKAP Inc (http://www.dckap.com)
* @license    http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
*/ -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Module/etc/module.xsd">
   <module name="Dckap_CustomerEditValidate" setup_version="1.0.0">
       <sequence>
           <module name="Magento_Customer" />
       </sequence>
   </module>
</config>

Step 3: Create a “routes.xml” file

The following path has: 

app/code/Dckap/CustomerEditValidate/etc/frontend/routes.xml

<?xml version="1.0"?>
<!-- /**
* @package    Dckap_ForGotValidate
* @copyright  Copyright (c) 2023 DCKAP Inc (http://www.dckap.com)
* @license    http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
*/ -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../lib/internal/Magento/Framework/App/etc/routes.xsd">
   <router id="standard">
       <route id="customereditvalidate" frontName="customereditvalidate">
           <module name="Dckap_CustomerEditValidate" />
       </route>
   </router>
</config>

Step 4: Create one more file “CurrentPassword.php” 

A file path that has the following: 

app/code/Dckap/CustomerEditValidate/Controller/Account/CurrentPassword.php

<?php
namespace Dckap\CustomerEditValidate\Controller\Account;


use Magento\Framework\App\Action\Context;
use Magento\Customer\Model\Session;
use Magento\Framework\Controller\Result\JsonFactory;
use Psr\Log\LoggerInterface;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Framework\Encryption\EncryptorInterface;
use \Magento\Customer\Model\CustomerRegistry;


class CurrentPassword extends \Magento\Framework\App\Action\Action
{


   protected $customerSession;
   protected $resultJsonFactory;
   protected $logger;
   protected $customerRepository;
   protected $encryptor;
   protected $CustomerRegistry;


   public function __construct(
       Context $context,
       Session $customerSession,
       JsonFactory $resultJsonFactory,
       LoggerInterface $logger,
       CustomerRepositoryInterface $customerRepository,
       EncryptorInterface $encryptor,
       CustomerRegistry $CustomerRegistry
   ) {
       $this->customerSession = $customerSession;
       $this->resultJsonFactory = $resultJsonFactory;
       $this->logger = $logger;
       $this->customerRepository = $customerRepository;
       $this->encryptor = $encryptor;
       $this->CustomerRegistry = $CustomerRegistry;
       parent::__construct($context);
   }


   public function execute()
   {


       $result = $this->resultJsonFactory->create();


       $currentPassword = $this->getRequest()->getParam('current_password');
       if(!empty($currentPassword)){
           $customerId = $this->customerSession->getCustomerId();
           $retrieveSecureData = $this->CustomerRegistry->retrieveSecureData($customerId);
           $hashedPassword = $retrieveSecureData->getPasswordHash();
           if ($this->encryptor->validateHash($currentPassword, $hashedPassword)) {
               return $result->setData(false);
           } else {
               return $result->setData(true);
           }
       }else{
           return $result->setData('no results');
       }
   }


}

Step 5: Create one more file “customer_account_edit.xml” 

A file path that has the following: 

app/code/Dckap/CustomerEditValidate/view/frontend/layout/customer_account_edit.xml

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
   <update handle="customer_account"/>
   <body>
       <referenceContainer name="content">
           <block class="Magento\Customer\Block\Form\Edit" name="customer_edit" template="Dckap_CustomerEditValidate::form/edit.phtml" cacheable="false">
               <container name="form.additional.info" as="form_additional_info"/>
           </block>
       </referenceContainer>
   </body>
</page>

Step 6: Now, create one more file “edit.phtml” 

A file path that has the following: 

We need to override this page from the vendor

app/code/Dckap/CustomerEditValidate/view/frontend/templates/form/edit.phtml

<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/


use Magento\Customer\Block\Widget\Name;


/** @var \Magento\Customer\Block\Form\Edit $block */
/** @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer */
?>
<form class="form form-edit-account"
     action="<?= $block->escapeUrl($block->getUrl('customer/account/editPost')) ?>"
     method="post" id="form-validate"
     enctype="multipart/form-data"
     data-hasrequired="<?= $block->escapeHtmlAttr(__('* Required Fields')) ?>"
     autocomplete="off">
   <fieldset class="fieldset info">
       <?= $block->getBlockHtml('formkey') ?>
       <legend class="legend"><span><?= $block->escapeHtml(__('Account Information')) ?></span></legend><br>
       <?= $block->getLayout()->createBlock(Name::class)->setObject($block->getCustomer())->toHtml() ?>


       <?php $_dob = $block->getLayout()->createBlock(\Magento\Customer\Block\Widget\Dob::class) ?>
       <?php $_taxvat = $block->getLayout()->createBlock(\Magento\Customer\Block\Widget\Taxvat::class) ?>
       <?php $_gender = $block->getLayout()->createBlock(\Magento\Customer\Block\Widget\Gender::class) ?>
       <?php if ($_dob->isEnabled()): ?>
           <?= $_dob->setDate($block->getCustomer()->getDob())->toHtml() ?>
       <?php endif ?>
       <?php if ($_taxvat->isEnabled()): ?>
           <?= $_taxvat->setTaxvat($block->getCustomer()->getTaxvat())->toHtml() ?>
       <?php endif ?>
       <?php if ($_gender->isEnabled()): ?>
           <?= $_gender->setGender($block->getCustomer()->getGender())->toHtml() ?>
       <?php endif ?>
       <div class="field choice">
           <input type="checkbox" name="change_email" id="change-email" data-role="change-email" value="1"
                  title="<?= $block->escapeHtmlAttr(__('Change Email')) ?>" class="checkbox" />
           <label class="label" for="change-email">
               <span><?= $block->escapeHtml(__('Change Email')) ?></span>
           </label>
       </div>
       <div class="field choice">
           <input type="checkbox" name="change_password" id="change-password" data-role="change-password" value="1"
                  title="<?= $block->escapeHtmlAttr(__('Change Password')) ?>"
               <?php if ($block->getChangePassword()): ?> checked="checked"<?php endif; ?> class="checkbox" />
           <label class="label" for="change-password">
               <span><?= $block->escapeHtml(__('Change Password')) ?></span>
           </label>
       </div>
       <?= $block->getChildHtml('fieldset_edit_info_additional') ?>
   </fieldset>


   <fieldset class="fieldset password" data-container="change-email-password">
       <legend class="legend">
           <span data-title="change-email-password"><?= $block->escapeHtml(__('Change Email and Password')) ?></span>
       </legend><br>
       <div class="field email required" data-container="change-email">
           <label class="label" for="email"><span><?= $block->escapeHtml(__('Email')) ?></span></label>
           <div class="control">
               <input type="email" name="email" id="email" autocomplete="email" data-input="change-email"
                      value="<?= $block->escapeHtmlAttr($block->getCustomer()->getEmail()) ?>"
                      title="<?= $block->escapeHtmlAttr(__('Email')) ?>"
                      placeholder="<?= $block->escapeHtmlAttr(__('Email')) ?>"
                      class="input-text"
                      data-validate="{required:true, 'validate-email':true}" />
           </div>
       </div>
       <div class="field password current required">
           <label class="label" for="current-password">
               <span><?= $block->escapeHtml(__('Current Password')) ?></span>
           </label>
           <div class="control">
               <input type="password" class="input-text" name="current_password" id="current-password"
                      data-input="current-password"
                      placeholder="<?= $block->escapeHtml(__('Current Password')) ?>"
                      title="<?= $block->escapeHtml(__('Current Password')) ?>"
                      autocomplete="off" />
                   <div id="password-strength-meter-current"> </div>
           </div>
       </div>
       <div class="field new password required" data-container="new-password">
           <label class="label" for="password"><span><?= $block->escapeHtml(__('New Password')) ?></span></label>
           <div class="control">
               <?php $minCharacterSets = $block->getRequiredCharacterClassesNumber() ?>
               <input type="password" class="input-text password" name="password" id="password"
                   data-password-min-length="<?= $block->escapeHtml($block->getMinimumPasswordLength()) ?>"
                   data-password-min-character-sets="<?= $block->escapeHtml($minCharacterSets) ?>"
                   data-input="new-password"
                   data-validate="{required:true, 'validate-customer-password':true}"
                   placeholder="<?= $block->escapeHtml(__('New Password')) ?>"
                   title="<?= $block->escapeHtml(__('New Password')) ?>"
                   autocomplete="off" />
               <div id="same-new-password"></div>   
               <div id="password-strength-meter-container" data-role="password-strength-meter" aria-live="polite">
                   <div id="password-strength-meter" class="password-strength-meter">
                       <?= $block->escapeHtml(__('Password Strength')) ?>:
                       <span id="password-strength-meter-label" data-role="password-strength-meter-label">
                           <?= $block->escapeHtml(__('No Password')) ?>
                       </span>
                   </div>
               </div>
           </div>
       </div>
       <div class="field confirmation password required" data-container="confirm-password">
           <label class="label" for="password-confirmation">
               <span><?= $block->escapeHtml(__('Confirm New Password')) ?></span>
           </label>
           <div class="control">
               <input type="password" class="input-text" name="password_confirmation" id="password-confirmation"
                   data-input="confirm-password"
                   placeholder="<?= $block->escapeHtml(__('Confirm New Password')) ?>"
                   title="<?= $block->escapeHtml(__('Confirm New Password')) ?>"
                   autocomplete="off" />
           </div>
       </div>
       <div class="field choice" data-bind="scope: 'showPassword'">
           <!-- ko template: getTemplate() --><!-- /ko -->
       </div>
   </fieldset>


   <fieldset class="fieldset additional_info">
       <?= $block->getChildHtml('form_additional_info') ?>
   </fieldset>


   <div class="actions-toolbar">
       <div class="primary">
           <button type="submit" class="action save primary" title="<?= $block->escapeHtmlAttr(__('Save')) ?>">
               <span><?= $block->escapeHtml(__('Save')) ?></span>
           </button>
       </div>
       <div class="secondary">
           <a class="action back" href="<?= $block->escapeUrl($block->getBackUrl()) ?>">
               <span><?= $block->escapeHtml(__('Go back')) ?></span>
           </a>
       </div>
   </div>
</form>
<?php $ignore = /* @noEscape */ $_dob->isEnabled() ? '\'input[id$="full"]\'' : 'null';
$scriptString = <<<script
   require([
       "jquery",
       "mage/mage"
   ], function($){
       var dataForm = $('#form-validate');
       var ignore = {$ignore};


       dataForm.mage('validation', {
script;
if ($_dob->isEnabled()):
   $scriptString .= <<<script
           errorPlacement: function(error, element) {
               if (element.prop('id').search('full') !== -1) {
                   var dobElement = $(element).parents('.customer-dob'),
                       errorClass = error.prop('class');
                   error.insertAfter(element.parent());
                   dobElement.find('.validate-custom').addClass(errorClass)
                       .after('<div class="' + errorClass + '"></div>');
               }
               else {
                   error.insertAfter(element);
               }
           },
           ignore: ':hidden:not(' + ignore + ')'
script;
else:
   $scriptString .= <<<script
           ignore: ignore ? ':hidden:not(' + ignore + ')' : ':hidden'
script;
endif;
$scriptString .= <<<script
       });


   });
script;
?>
<?= /* @noEscape */ $secureRenderer->renderTag('script', [], $scriptString, false) ?>
<?php $changeEmailAndPasswordTitle = $block->escapeHtml(__('Change Email and Password')) ?>
<script type="text/x-magento-init">
   {
       "[data-role=change-email], [data-role=change-password]": {
           "changeEmailPassword": {
               "titleChangeEmail": "<?= $block->escapeJs($block->escapeHtml(__('Change Email'))) ?>",
               "titleChangePassword": "<?= $block->escapeJs($block->escapeHtml(__('Change Password'))) ?>",
               "titleChangeEmailAndPassword": "<?= $block->escapeJs($changeEmailAndPasswordTitle) ?>"
           }
       },
       "[data-container=new-password]": {
           "passwordStrengthIndicator": {
               "formSelector": "form.form-edit-account"
           }
       },
       "*": {
           "Magento_Ui/js/core/app": {
               "components": {
                   "showPassword": {
                       "component": "Magento_Customer/js/show-password",
                       "passwordSelector": "#current-password,#password,#password-confirmation"
                   }
               }
           }
       }
   }
</script>




<script>
require([
   'jquery'
], function($) {
   $(document).ready(function () {
       var currentPasswordInput = $('#current-password');
       var newPasswordInput = $('#password');
       console.log(currentPasswordInput);
       console.log(newPasswordInput);
       var errorContainer = $('#same-new-password');
       var form = $('#form-validate');
       newPasswordInput.on('input', function () {
           var currentPassword = currentPasswordInput.val();
           var newPassword = newPasswordInput.val();


           errorContainer.html('').hide();
          
               if (newPassword == currentPassword) {
                   // Show error message
                   errorContainer.html("<div class='message-error-password'>Password already used try for new password.</div>").find('.message-error-password')
                   .css('color', 'red');
                   errorContainer.show();
                   $('.save').prop('disabled', true);
               }else if(newPassword != currentPassword){
                   // Show error message
                   errorContainer.html('').find('.message-error-password')
                   .css('color', 'red');
                   errorContainer.show();
                   $('.save').prop('disabled', false);
               }
          
       });


       form.on('submit', function (event) {
           var currentPassword = currentPasswordInput.val();
           var newPassword = newPasswordInput.val();


           if (newPassword == currentPassword) {
               // Show error message
               errorContainer.html("<div class='message-error-password'>Password already used try for new password.</div>").find('.message-error-password')
               .css('color', 'red');
               event.preventDefault();
               $('.save').prop('disabled', true);
           }else if(newPassword != currentPassword){
               // Show error message
               errorContainer.html('').find('.message-error-password')
               .css('color', 'red');
               errorContainer.show();
               $('.save').prop('disabled', false);
           }
       });
   });
});
</script>


<script>
   require(['jquery'], function ($) {
       $(document).ready(function () {
           $('#current-password').on('input', function () {
               var currentPassword = $('#current-password').val();
               var Url = '<?php echo $this->getUrl('customereditvalidate/account/currentpassword'); ?>'
               $.ajax({
                   url: Url,
                   data: { current_password: currentPassword },
                   type: 'POST',
                   dataType: 'json',
                   success: function (response) {
                       console.log(response);
               if( response != 'no results'){


                       if (response == false) {
                           $('#password-strength-meter-current').html('<span>Your current password is correct please enter same</span>');
                           $('#password-strength-meter-current').css('color', 'green');
                           $('.save').prop('disabled', false);


                       }
                       else if(response == true){


                           $('#password-strength-meter-current').html('<span>Your current password is not correct please enter different</span>');
                           $('#password-strength-meter-current').css('color', 'red');
                           $('.save').prop('disabled', true);


                       }
                   }


                   },
               });
           });
       });
   });


</script>

Recommended Read: Add Custom Validation for Forgotten Password in Magento 2 

Output for Current Password Validation

Output for New Password Validation

Implementing Robust Password Validation Mechanism in Magento 2 with Klizer 

By implementing robust password validation mechanisms in Magento 2, merchants can enhance the security of customer accounts and mitigate the risk of unauthorized access. 

Verifying the customer’s current password during the password change process helps prevent unauthorized changes while enforcing password complexity requirements strengthens overall account security. 

Checking for password reuse ensures that customers adopt unique and secure passwords, further fortifying the protection of their accounts and sensitive information. Magento 2 merchants can effectively safeguard their customers’ accounts and foster trust in their online store by following the steps outlined in this guide.

Get in touch with us to get further inquiries and explore how our solutions can benefit your business.

About The Author

We take the guesswork out of ecommerce.
Schedule a consultation call today