| @@ -24,8 +24,68 @@ | ||
| 24 | 24 | add_action('wp_ajax_yatra_ajax_login', [$this, 'handleAjaxLogin']); |
| 25 | 25 | |
| 26 | 26 | // Register AJAX action for non-logged-in users |
| 27 | 27 | add_action('wp_ajax_nopriv_yatra_ajax_login', [$this, 'handleAjaxLogin']); |
| 28 | + | |
| 29 | + // Password reset request (delegates to WordPress core's reset flow). | |
| 30 | + add_action('wp_ajax_yatra_ajax_lost_password', [$this, 'handleAjaxLostPassword']); | |
| 31 | + add_action('wp_ajax_nopriv_yatra_ajax_lost_password', [$this, 'handleAjaxLostPassword']); | |
| 32 | + } | |
| 33 | + | |
| 34 | + /** | |
| 35 | + * Handle AJAX password-reset request. | |
| 36 | + * | |
| 37 | + * This is a thin, on-site entry point that delegates to WordPress core's | |
| 38 | + * retrieve_password(): core generates the reset key and sends its standard | |
| 39 | + * reset email. We deliberately do NOT reimplement reset-token crypto. A | |
| 40 | + * generic success message is always returned to avoid account enumeration. | |
| 41 | + */ | |
| 42 | + public function handleAjaxLostPassword(): void | |
| 43 | + { | |
| 44 | + // Rate limiting (shared with login attempts). | |
| 45 | + $this->checkRateLimit(); | |
| 46 | + | |
| 47 | + // Verify nonce (same nonces the login form issues). | |
| 48 | + $nonce = $_POST['yatra_login_nonce'] ?? $_POST['nonce'] ?? ''; | |
| 49 | + if (!wp_verify_nonce($nonce, 'yatra_login_action') && !wp_verify_nonce($nonce, 'yatra_login_nonce')) { | |
| 50 | + wp_send_json_error([ | |
| 51 | + 'success' => false, | |
| 52 | + 'code' => 'security_check_failed', | |
| 53 | + 'message' => __('Security check failed. Please refresh the page and try again.', 'yatra'), | |
| 54 | + ]); | |
| 55 | + } | |
| 56 | + | |
| 57 | + if ($_SERVER['REQUEST_METHOD'] !== 'POST') { | |
| 58 | + wp_send_json_error([ | |
| 59 | + 'success' => false, | |
| 60 | + 'code' => 'invalid_method', | |
| 61 | + 'message' => __('Invalid request method.', 'yatra'), | |
| 62 | + ]); | |
| 63 | + } | |
| 64 | + | |
| 65 | + $user_login = sanitize_text_field(wp_unslash($_POST['user_login'] ?? $_POST['log'] ?? '')); | |
| 66 | + | |
| 67 | + if ($user_login === '') { | |
| 68 | + wp_send_json_error([ | |
| 69 | + 'success' => false, | |
| 70 | + 'code' => 'empty_user_login', | |
| 71 | + 'message' => __('Please enter your email address or username.', 'yatra'), | |
| 72 | + ]); | |
| 73 | + } | |
| 74 | + | |
| 75 | + // Populate $_POST['user_login'] for cross-version compatibility: | |
| 76 | + // retrieve_password() reads it directly on WordPress older than 5.7, | |
| 77 | + // and the explicit argument is used on 5.7+ (extra args are ignored on | |
| 78 | + // older cores, so this is safe either way). | |
| 79 | + $_POST['user_login'] = $user_login; | |
| 80 | + retrieve_password($user_login); | |
| 81 | + | |
| 82 | + // Always report success regardless of whether the account exists — | |
| 83 | + // prevents user/email enumeration (mirrors WordPress core behavior). | |
| 84 | + wp_send_json_success([ | |
| 85 | + 'success' => true, | |
| 86 | + 'message' => __('If an account exists for that email, a password reset link has been sent. Please check your inbox.', 'yatra'), | |
| 87 | + ]); | |
| 28 | 88 | } |
| 29 | 89 | |
| 30 | 90 | /** |
| 31 | 91 | * Handle AJAX login request |