I'm currently trying to build a custom reset password form, which seems to work fine up until it needs to validate the key for the user:
//Redirect away from default wordpress to reset password
function redirect_to_reset_password() {
if ( 'GET' == $_SERVER['REQUEST_METHOD'] ) {
// Verify key / login combo
$user = check_password_reset_key( $_REQUEST['key'], $_REQUEST['login'] );
if ( ! $user || is_wp_error( $user ) ) {
if ( $user && $user->get_error_code() === 'expired_key' ) {
wp_redirect( home_url( '/login?login=expiredkey/' ) );
} else {
wp_redirect( home_url( '/login?login=invalidkey/' ) );
}
exit;
}
$redirect_url = home_url( '/reset-password/' );
$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_reset_password');
add_action('login_form_resetpass', 'redirect_to_reset_password');
//Make new password
function do_password_reset() {
if ( 'POST' == $_SERVER['REQUEST_METHOD'] ) {
$rp_key = $_REQUEST['rp_key'];
$rp_login = $_REQUEST['rp_login'];
$user = check_password_reset_key( $rp_key, $rp_login );
if ( ! $user || is_wp_error( $user ) ) {
if ( $user && $user->get_error_code() === 'expired_key' ) {
wp_redirect( home_url( '/login?login=expiredkey/' ) );
} else {
wp_redirect( home_url( '/login?login=invalidkey/' ) );
}
exit;
}
if ( isset( $_POST['pass1'] ) ) {
if ( $_POST['pass1'] != $_POST['pass2'] ) {
// Passwords don't match
$redirect_url = home_url( '/reset-password/' );
$redirect_url = add_query_arg( 'key', $rp_key, $redirect_url );
$redirect_url = add_query_arg( 'login', $rp_login, $redirect_url );
$redirect_url = add_query_arg( 'error', 'password_reset_mismatch', $redirect_url );
wp_redirect( $redirect_url );
exit;
}
if ( empty( $_POST['pass1'] ) ) {
// Password is empty
$redirect_url = home_url( '/reset-password/' );
$redirect_url = add_query_arg( 'key', $rp_key, $redirect_url );
$redirect_url = add_query_arg( 'login', $rp_login, $redirect_url );
$redirect_url = add_query_arg( 'error', 'password_reset_empty', $redirect_url );
wp_redirect( $redirect_url );
exit;
}
// Parameter checks OK, reset password
reset_password( $user, $_POST['pass1'] );
wp_redirect( home_url( '/login?password=changed/' ) );
} else {
echo "Invalid request.";
}
exit;
}
}
add_action( 'login_form_rp', 'do_password_reset' );
add_action( 'login_form_resetpass', 'do_password_reset' );
I've been snooping around trying to figure out what the cause of the issue can be, and I've found that the key under user_activation_key is not the same as the one I get in my URL when trying to reset my password. E.g:
DB: 1553346836:$P$BYEbftAGRfnhlBTeuNL4ylhsxRyhS3/
URL: key=dx1GjoJnaD6Dytc5zpNq
I assume this is just wordpress' way of encrypting the key so it shouldn't cause this issue, but it is the only inconsistency I've been able to find.
EDIT: If I try and check what value $user is set to, I get an undefined index error regarding $_REQUEST['rp_key']
and $_REQUEST['rp_login']
. I then tried to var_dump $_REQUEST which gave me the following: array(2) { ["login"]=> string(20) "[email protected]" ["key"]=> string(20) "nWrRCsRD4OvaROeMFErV" }
. Following this I changed $_REQUEST['rp_key'] to $_REQUEST['key'] and the same for login, but I still get the same undefined index error. But the value is in the $_REQUEST array, why can't it find it?
Could the fact that I'm using check_password_reset_key
twice have an effect on the key being set to invalid?
EDIT2: Running the code through a environment running https resulted in a "Bad Request" return.
I'm currently trying to build a custom reset password form, which seems to work fine up until it needs to validate the key for the user:
//Redirect away from default wordpress to reset password
function redirect_to_reset_password() {
if ( 'GET' == $_SERVER['REQUEST_METHOD'] ) {
// Verify key / login combo
$user = check_password_reset_key( $_REQUEST['key'], $_REQUEST['login'] );
if ( ! $user || is_wp_error( $user ) ) {
if ( $user && $user->get_error_code() === 'expired_key' ) {
wp_redirect( home_url( '/login?login=expiredkey/' ) );
} else {
wp_redirect( home_url( '/login?login=invalidkey/' ) );
}
exit;
}
$redirect_url = home_url( '/reset-password/' );
$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_reset_password');
add_action('login_form_resetpass', 'redirect_to_reset_password');
//Make new password
function do_password_reset() {
if ( 'POST' == $_SERVER['REQUEST_METHOD'] ) {
$rp_key = $_REQUEST['rp_key'];
$rp_login = $_REQUEST['rp_login'];
$user = check_password_reset_key( $rp_key, $rp_login );
if ( ! $user || is_wp_error( $user ) ) {
if ( $user && $user->get_error_code() === 'expired_key' ) {
wp_redirect( home_url( '/login?login=expiredkey/' ) );
} else {
wp_redirect( home_url( '/login?login=invalidkey/' ) );
}
exit;
}
if ( isset( $_POST['pass1'] ) ) {
if ( $_POST['pass1'] != $_POST['pass2'] ) {
// Passwords don't match
$redirect_url = home_url( '/reset-password/' );
$redirect_url = add_query_arg( 'key', $rp_key, $redirect_url );
$redirect_url = add_query_arg( 'login', $rp_login, $redirect_url );
$redirect_url = add_query_arg( 'error', 'password_reset_mismatch', $redirect_url );
wp_redirect( $redirect_url );
exit;
}
if ( empty( $_POST['pass1'] ) ) {
// Password is empty
$redirect_url = home_url( '/reset-password/' );
$redirect_url = add_query_arg( 'key', $rp_key, $redirect_url );
$redirect_url = add_query_arg( 'login', $rp_login, $redirect_url );
$redirect_url = add_query_arg( 'error', 'password_reset_empty', $redirect_url );
wp_redirect( $redirect_url );
exit;
}
// Parameter checks OK, reset password
reset_password( $user, $_POST['pass1'] );
wp_redirect( home_url( '/login?password=changed/' ) );
} else {
echo "Invalid request.";
}
exit;
}
}
add_action( 'login_form_rp', 'do_password_reset' );
add_action( 'login_form_resetpass', 'do_password_reset' );
I've been snooping around trying to figure out what the cause of the issue can be, and I've found that the key under user_activation_key is not the same as the one I get in my URL when trying to reset my password. E.g:
DB: 1553346836:$P$BYEbftAGRfnhlBTeuNL4ylhsxRyhS3/
URL: key=dx1GjoJnaD6Dytc5zpNq
I assume this is just wordpress' way of encrypting the key so it shouldn't cause this issue, but it is the only inconsistency I've been able to find.
EDIT: If I try and check what value $user is set to, I get an undefined index error regarding $_REQUEST['rp_key']
and $_REQUEST['rp_login']
. I then tried to var_dump $_REQUEST which gave me the following: array(2) { ["login"]=> string(20) "[email protected]" ["key"]=> string(20) "nWrRCsRD4OvaROeMFErV" }
. Following this I changed $_REQUEST['rp_key'] to $_REQUEST['key'] and the same for login, but I still get the same undefined index error. But the value is in the $_REQUEST array, why can't it find it?
Could the fact that I'm using check_password_reset_key
twice have an effect on the key being set to invalid?
EDIT2: Running the code through a environment running https resulted in a "Bad Request" return.
Share Improve this question edited Apr 12, 2019 at 11:30 ok34332 asked Mar 27, 2019 at 15:34 ok34332ok34332 211 silver badge3 bronze badges 7 | Show 2 more comments2 Answers
Reset to default 2I had the same issue with this, I assume you're working from this guide - https://code.tutsplus/tutorials/build-a-custom-wordpress-user-flow-part-3-password-reset--cms-23811
Have a look at the actual reset password form where it shows pass1 and pass2. The hidden fields values are automatically set to $attribute['key']
and $attribute['login']
.
Changing this to $_REQUEST['key']
and $_REQUEST['login']
should solve the issue.
Hope this helps.
I think the problem here is the fact that you're using $_REQUEST
instead of explicitly looking for GET
or POST
variables.
I think you already solved the first issue which is that the first step of the process (where you request a reset) passes key
and login
via $_GET
variables. This allows redirect_to_reset_password()
to redirect appropriately.
The next part is a bit different. The reset password form passes the following:
Array (
[action] => resetpass
[pass1] => [text]
[pass1-text] => [text]
[pass2] => [text]
[rp_key] => [text]
[wp-submit] => Reset Password
)
Unless you're doing something to change these fields on a form from your /reset-password/
page, rp_login
will never be set. wp-login.php
shows a different process to retrieving the necessary pieces to run check_password_reset_key()
if ( isset( $_COOKIE[ $rp_cookie ] ) && 0 < strpos( $_COOKIE[ $rp_cookie ], ':' ) ) {
list( $rp_login, $rp_key ) = explode( ':', wp_unslash( $_COOKIE[ $rp_cookie ] ), 2 );
$user = check_password_reset_key( $rp_key, $rp_login );
if ( isset( $_POST['pass1'] ) && ! hash_equals( $rp_key, $_POST['rp_key'] ) ) {
$user = false;
}
}
You can see it's checking the rp_cookie
that was set earlier in the process. From here it's retrieving the login and key then checking the cookie's key against the key passed via a POST
variable.
You will need to do something similar to make this work.
php -a
require_once("class-phpass.php");
echo ((new PasswordHash(8,true))->CheckPassword('dx1GjoJnaD6Dytc5zpNq', '$P$BYEbftAGRfnhlBTeuNL4ylhsxRyhS3/')) ? "true" : "false";
gives me "true". – Rup Commented Mar 27, 2019 at 16:15