Ok so this has no convenient hook. I had to basically hijack the reset password page, and redirect it to my own implementation where I can be as foolish as I want with the rules.
function redirect_to_custom_password_reset() {
if ( 'GET' == $_SERVER['REQUEST_METHOD'] ) {
// Verify key / login combo
$user = check_password_reset_key( $_REQUEST['key'], $_REQUEST['login'] );
if ( ! $user || is_wp_error( $user ) ) {
//die($user->get_error_message());
if ( $user && $user->get_error_code() === 'expired_key' ) {
wp_redirect( home_url( 'member-login?login=expiredkey' ) );
} else {
wp_redirect( home_url( 'member-login?login=invalidkey' ) );
}
exit;
}
$redirect_url = get_permalink( 1234 ); //this is the ID of the page you make to handle this
$redirect_url = add_query_arg( 'login', esc_attr( $_REQUEST['login'] ), $redirect_url );
$redirect_url = add_query_arg( 'key', esc_attr( $_REQUEST['key'] ), $redirect_url );
wp_redirect( $redirect_url );
exit;
}
}
add_action( 'login_form_rp', 'redirect_to_custom_password_reset' );
add_action( 'login_form_resetpass', 'redirect_to_custom_password_reset' );
You'll need a custom handler for updating the password. Something simple like this would do:
if(isset($_POST['rp_key']) && isset($_POST['rp_login'])){
if($_POST['pass1'] == $_POST['pass2'] && strlen($_POST['pass1']) >= 10){
$verify = check_password_reset_key($_POST['rp_key'], $_POST['rp_login']);
if(!isset($verify->errors)){
$update = wp_set_password($_POST['pass1'], $verify->ID);
if($update){
//do something, set a variable, or whatever
$reset_pw_success = true;
}
}
}else{
$reset_pw_error = 'Invalid login keys';
}
}
You'll need some code for the page you set up. You can make a page template, or a gutenberg block. Here's some sample code for what it might contain:
<?php
$verify = check_password_reset_key($_REQUEST['key'], $_REQUEST['login']);
global $reset_pw_error;
global $reset_pw_success;
$reset_error = false;
if(isset($reset_pw_error)){
$reset_error = true;
}
?>
<section id="primary-content" class="section-default padded">
<div class="shell">
<div id="password-reset-form" class="widecolumn">
<?php if(isset($reset_pw_success) && $reset_pw_success === true): ?>
<h1>Success!</h1>
<p>
Your password has been updated. You can now <a href="/login-registration/">login</a> with your new password.
</p>
<?php else: ?>
<h1>Reset Your Password</h1>
<?php if(!isset($verify->errors)): ?>
<form name="resetpassform" id="resetpassform" action="" method="post" autocomplete="off">
<input type="hidden" id="user_login" name="rp_login" value="<?php echo esc_attr( $_GET['login'] ); ?>" autocomplete="off" />
<input type="hidden" name="rp_key" value="<?php echo esc_attr( $_GET['key'] ); ?>" />
<div class="form-row">
<label for="pass1">New Password</label>
<input type="password" name="pass1" id="pass1" class="input" size="20" value="" autocomplete="off" />
</div>
<div class="form-row">
<label for="pass2">Confirm New Password</label>
<input type="password" name="pass2" id="pass2" class="input" size="20" value="" />
</div>
<p class="description">Your password needs to be at least 10 characters long. Use special characters if you want.</p>
<div id="reset-pw-message" <?php if($reset_error): ?>class="error"<?php endif; ?>><?php echo $reset_error ? $reset_pw_error : ''; ?></div>
<p class="resetpass-submit">
<button type="submit" name="submit" id="resetpass-button">Reset Password</button>
</p>
</form>
<?php else: ?>
<p>The password reset link has expired.</p>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
</section>
And you'll probably want some basic JS validation:
$('#resetpass-button').click(function(e){
var pw1 = $('#pass1').val();
var pw2 = $('#pass2').val();
error = [];
if(pw1 == '' || pw2 == ''){
error.push('Please make sure you enter a new password, and make it match the confirm password field.');
}
else
{
if(pw1.length < 10){
error.push('Your new password has to be at least 10 characters long.');
}
if(pw1 !== pw2){
error.push('Please make sure both password fields match exactly.');
}
}
if(error.length > 0){
e.preventDefault();
$('#reset-pw-message').html(error.join('<br>')).slideDown().delay(8000).slideUp();
}
});
This code is functional but you might want to try a little harder than I did. It was a quick patch for me, but I would consider making this a class and actually trying to protect things. Globals and procedural approaches tend to be frowned upon by many if they have to maintain the code after you.