feat: SPA-based password reset page
- Created ResetPassword.tsx with: - Password reset form with strength indicator - Key validation on load - Show/hide password toggle - Success/error states - Redirect to login on success - Updated EmailManager.php: - Changed reset_link from wp-login.php to SPA route - Format: /wp-admin/admin.php?page=woonoow#/reset-password?key=KEY&login=LOGIN - Added AuthController API methods: - validate_reset_key: Validates reset key before showing form - reset_password: Performs actual password reset - Registered new REST routes in Routes.php: - POST /auth/validate-reset-key - POST /auth/reset-password Password reset emails now link to the SPA instead of native WordPress.
This commit is contained in:
@@ -230,4 +230,100 @@ class AuthController {
|
||||
'message' => __( 'Password reset email sent! Please check your inbox.', 'woonoow' ),
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate password reset key
|
||||
*
|
||||
* @param WP_REST_Request $request Request object
|
||||
* @return WP_REST_Response Response object
|
||||
*/
|
||||
public static function validate_reset_key( WP_REST_Request $request ): WP_REST_Response {
|
||||
$key = sanitize_text_field( $request->get_param( 'key' ) );
|
||||
$login = sanitize_text_field( $request->get_param( 'login' ) );
|
||||
|
||||
if ( empty( $key ) || empty( $login ) ) {
|
||||
return new WP_REST_Response( [
|
||||
'valid' => false,
|
||||
'message' => __( 'Invalid password reset link', 'woonoow' ),
|
||||
], 400 );
|
||||
}
|
||||
|
||||
// Check the reset key
|
||||
$user = check_password_reset_key( $key, $login );
|
||||
|
||||
if ( is_wp_error( $user ) ) {
|
||||
$error_code = $user->get_error_code();
|
||||
$message = __( 'This password reset link has expired or is invalid.', 'woonoow' );
|
||||
|
||||
if ( $error_code === 'invalid_key' ) {
|
||||
$message = __( 'This password reset link is invalid.', 'woonoow' );
|
||||
} elseif ( $error_code === 'expired_key' ) {
|
||||
$message = __( 'This password reset link has expired. Please request a new one.', 'woonoow' );
|
||||
}
|
||||
|
||||
return new WP_REST_Response( [
|
||||
'valid' => false,
|
||||
'message' => $message,
|
||||
], 400 );
|
||||
}
|
||||
|
||||
return new WP_REST_Response( [
|
||||
'valid' => true,
|
||||
'user' => [
|
||||
'login' => $user->user_login,
|
||||
'email' => $user->user_email,
|
||||
],
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset password with key
|
||||
*
|
||||
* @param WP_REST_Request $request Request object
|
||||
* @return WP_REST_Response Response object
|
||||
*/
|
||||
public static function reset_password( WP_REST_Request $request ): WP_REST_Response {
|
||||
$key = sanitize_text_field( $request->get_param( 'key' ) );
|
||||
$login = sanitize_text_field( $request->get_param( 'login' ) );
|
||||
$password = $request->get_param( 'password' );
|
||||
|
||||
if ( empty( $key ) || empty( $login ) || empty( $password ) ) {
|
||||
return new WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => __( 'Missing required fields', 'woonoow' ),
|
||||
], 400 );
|
||||
}
|
||||
|
||||
// Validate password strength
|
||||
if ( strlen( $password ) < 8 ) {
|
||||
return new WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => __( 'Password must be at least 8 characters long', 'woonoow' ),
|
||||
], 400 );
|
||||
}
|
||||
|
||||
// Validate the reset key
|
||||
$user = check_password_reset_key( $key, $login );
|
||||
|
||||
if ( is_wp_error( $user ) ) {
|
||||
return new WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => __( 'This password reset link has expired or is invalid. Please request a new one.', 'woonoow' ),
|
||||
], 400 );
|
||||
}
|
||||
|
||||
// Reset the password
|
||||
reset_password( $user, $password );
|
||||
|
||||
// Delete the password reset key so it can't be reused
|
||||
delete_user_meta( $user->ID, 'default_password_nag' );
|
||||
|
||||
// Trigger password changed action
|
||||
do_action( 'password_reset', $user, $password );
|
||||
|
||||
return new WP_REST_Response( [
|
||||
'success' => true,
|
||||
'message' => __( 'Password reset successfully. You can now log in with your new password.', 'woonoow' ),
|
||||
], 200 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +79,20 @@ class Routes {
|
||||
'permission_callback' => '__return_true',
|
||||
] );
|
||||
|
||||
// Validate password reset key (public)
|
||||
register_rest_route( $namespace, '/auth/validate-reset-key', [
|
||||
'methods' => 'POST',
|
||||
'callback' => [ AuthController::class, 'validate_reset_key' ],
|
||||
'permission_callback' => '__return_true',
|
||||
] );
|
||||
|
||||
// Reset password with key (public)
|
||||
register_rest_route( $namespace, '/auth/reset-password', [
|
||||
'methods' => 'POST',
|
||||
'callback' => [ AuthController::class, 'reset_password' ],
|
||||
'permission_callback' => '__return_true',
|
||||
] );
|
||||
|
||||
// Defer to controllers to register their endpoints
|
||||
CheckoutController::register();
|
||||
OrdersController::register();
|
||||
|
||||
@@ -327,12 +327,12 @@ class EmailManager {
|
||||
return $message; // Use WordPress default
|
||||
}
|
||||
|
||||
// Build reset URL - use SPA route if available, otherwise WordPress default
|
||||
$site_url = get_site_url();
|
||||
// Build reset URL - use SPA route
|
||||
$admin_url = admin_url('admin.php?page=woonoow');
|
||||
|
||||
// Check if this is a WooCommerce customer - use SPA reset page
|
||||
// Otherwise fall back to WordPress default reset page
|
||||
$reset_link = network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login');
|
||||
// Build SPA reset password URL with hash router format
|
||||
// Format: /wp-admin/admin.php?page=woonoow#/reset-password?key=KEY&login=LOGIN
|
||||
$reset_link = $admin_url . '#/reset-password?key=' . $key . '&login=' . rawurlencode($user_login);
|
||||
|
||||
// Create a pseudo WC_Customer for template rendering
|
||||
$customer = null;
|
||||
|
||||
Reference in New Issue
Block a user