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.
ON THIS PAGE
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.