Plugin Directory

Changeset 3390737


Ignore:
Timestamp:
11/05/2025 10:41:15 PM (4 months ago)
Author:
chrmrtns
Message:

Release version 3.2.2 - Fix login error display on custom login pages

  • Fixed blank error display when wrong password/username entered
  • Added wp_login_failed hook to catch authentication failures
  • Error parameters now properly preserved during redirect
  • Compatible with User Enumeration Prevention feature
Location:
keyless-auth
Files:
71 added
3 edited

Legend:

Unmodified
Added
Removed
  • keyless-auth/trunk/includes/Core/Core.php

    r3386610 r3390737  
    4848        // Hook early to catch wp-login.php requests for redirect
    4949        add_action('init', array($this, 'chrmrtns_kla_maybe_redirect_wp_login'), 1);
     50
     51        // Hook into failed login to redirect with error parameters
     52        add_action('wp_login_failed', array($this, 'handle_failed_login'), 10, 2);
    5053
    5154        // Disable XML-RPC if option is enabled
     
    129132                echo '<p class="chrmrtns-box chrmrtns-error" role="alert" aria-live="assertive">' . wp_kses_post(apply_filters('chrmrtns_kla_admin_approval_error', __('Your account needs to be approved by an admin before you can log-in.', 'keyless-auth'))) . '</p>';
    130133            }
    131            
     134
     135            // Show WordPress native login errors (from wp-login.php redirects)
     136            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- URL parameters for error display
     137            $login_error = isset($_GET['login_error']) ? sanitize_text_field(wp_unslash($_GET['login_error'])) : '';
     138            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- URL parameters for error display
     139            $login_status = isset($_GET['login']) ? sanitize_text_field(wp_unslash($_GET['login'])) : '';
     140
     141            if ($login_error || $login_status) {
     142                $error_message = $this->get_login_error_message($login_error, $login_status);
     143                if ($error_message) {
     144                    echo '<p class="chrmrtns-box chrmrtns-error" role="alert">' . wp_kses_post($error_message) . '</p>';
     145                }
     146            }
     147
    132148            // Render the login form
    133149            $this->render_login_form_html($atts);
     
    230246            } elseif (is_wp_error($sent_link)) {
    231247                echo '<p class="chrmrtns-box chrmrtns-error">' . wp_kses_post(apply_filters('chrmrtns_kla_error_send_link_msg', __('Email could not be sent. Please try again.', 'keyless-auth'))) . '</p>';
     248            }
     249
     250            // Show WordPress native login errors (from wp-login.php redirects)
     251            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- URL parameters for error display
     252            $login_error = isset($_GET['login_error']) ? sanitize_text_field(wp_unslash($_GET['login_error'])) : '';
     253            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- URL parameters for error display
     254            $login_status = isset($_GET['login']) ? sanitize_text_field(wp_unslash($_GET['login'])) : '';
     255
     256            if ($login_error || $login_status) {
     257                $error_message = $this->get_login_error_message($login_error, $login_status);
     258                if ($error_message) {
     259                    echo '<p class="chrmrtns-box chrmrtns-error" role="alert">' . wp_kses_post($error_message) . '</p>';
     260                }
     261            }
     262
     263            // Show success messages
     264            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- URL parameters for success messages
     265            if (isset($_GET['loggedout']) && $_GET['loggedout'] === 'true') {
     266                echo '<p class="chrmrtns-box chrmrtns-success" role="status">' . esc_html__('You have successfully logged out.', 'keyless-auth') . '</p>';
     267            }
     268
     269            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- URL parameters for success messages
     270            if (isset($_GET['registered']) && $_GET['registered'] === 'true') {
     271                echo '<p class="chrmrtns-box chrmrtns-success" role="status">' . esc_html__('Registration complete. Please check your email.', 'keyless-auth') . '</p>';
     272            }
     273
     274            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- URL parameters for success messages
     275            $checkemail = isset($_GET['checkemail']) ? sanitize_text_field(wp_unslash($_GET['checkemail'])) : '';
     276            if ($checkemail === 'confirm') {
     277                echo '<p class="chrmrtns-box chrmrtns-success" role="status">' . esc_html__('Check your email for the confirmation link.', 'keyless-auth') . '</p>';
     278            } elseif ($checkemail === 'newpass') {
     279                echo '<p class="chrmrtns-box chrmrtns-success" role="status">' . esc_html__('Check your email for your new password.', 'keyless-auth') . '</p>';
    232280            }
    233281
     
    391439
    392440    /**
     441     * Get user-friendly error message from WordPress login error codes
     442     *
     443     * @param string $error_code Error code from login redirect.
     444     * @param string $login_status Login status parameter.
     445     * @return string Human-readable error message.
     446     */
     447    private function get_login_error_message($error_code, $login_status) {
     448        // Handle common WordPress login error codes
     449        $error_messages = array(
     450            // Standard wp-login.php error codes
     451            'invalid_username' => __('Invalid username or email address.', 'keyless-auth'),
     452            'incorrect_password' => __('The password you entered is incorrect.', 'keyless-auth'),
     453            'invalidcombo' => __('Invalid username or password.', 'keyless-auth'),
     454            'empty_username' => __('Please enter your username or email address.', 'keyless-auth'),
     455            'empty_password' => __('Please enter your password.', 'keyless-auth'),
     456            'invalid_email' => __('Invalid email address.', 'keyless-auth'),
     457            'invalidkey' => __('Your password reset link is invalid or has expired.', 'keyless-auth'),
     458            'expiredkey' => __('Your password reset link has expired. Please request a new one.', 'keyless-auth'),
     459
     460            // Login status messages (from ?login= parameter)
     461            'failed' => __('Login failed. Please try again.', 'keyless-auth'),
     462        );
     463
     464        // Check error code first
     465        if (!empty($error_code) && isset($error_messages[$error_code])) {
     466            return $error_messages[$error_code];
     467        }
     468
     469        // Check login status
     470        if (!empty($login_status) && isset($error_messages[$login_status])) {
     471            return $error_messages[$login_status];
     472        }
     473
     474        // Generic fallback for unknown errors
     475        if (!empty($error_code) || !empty($login_status)) {
     476            return __('Login failed. Please try again.', 'keyless-auth');
     477        }
     478
     479        return '';
     480    }
     481
     482    /**
    393483     * Handle form submission
    394484     */
     
    585675        }
    586676
     677        // Preserve error parameters when redirecting
     678        $redirect_url = $custom_login_url;
     679        $params_to_preserve = array();
     680
     681        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- GET parameters for error display, no security impact
     682        if (isset($_GET['login'])) {
     683            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- GET parameters from WordPress redirect
     684            $params_to_preserve['login'] = sanitize_text_field(wp_unslash($_GET['login']));
     685        }
     686
     687        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- GET parameters for error display, no security impact
     688        if (isset($_GET['error'])) {
     689            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- GET parameters from WordPress redirect
     690            $params_to_preserve['login_error'] = sanitize_text_field(wp_unslash($_GET['error']));
     691        }
     692
     693        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- GET parameters for success messages, no security impact
     694        if (isset($_GET['loggedout'])) {
     695            $params_to_preserve['loggedout'] = 'true';
     696        }
     697
     698        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- GET parameters for success messages, no security impact
     699        if (isset($_GET['registered'])) {
     700            $params_to_preserve['registered'] = 'true';
     701        }
     702
     703        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- GET parameters for success messages, no security impact
     704        if (isset($_GET['checkemail'])) {
     705            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- GET parameters from WordPress redirect
     706            $params_to_preserve['checkemail'] = sanitize_text_field(wp_unslash($_GET['checkemail']));
     707        }
     708
     709        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- GET parameters for redirect URL, no security impact
     710        if (isset($_GET['redirect_to'])) {
     711            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- GET parameters from WordPress redirect
     712            $params_to_preserve['redirect_to'] = esc_url_raw(wp_unslash($_GET['redirect_to']));
     713        }
     714
     715        // Add preserved parameters to redirect URL
     716        if (!empty($params_to_preserve)) {
     717            $redirect_url = add_query_arg($params_to_preserve, $custom_login_url);
     718        }
     719
    587720        // Perform the redirect
    588         wp_redirect($custom_login_url);
     721        wp_redirect($redirect_url);
     722        exit;
     723    }
     724
     725    /**
     726     * Handle failed login attempts
     727     *
     728     * Redirects to custom login page with error parameters when authentication fails
     729     *
     730     * @param string $username Username or email used in login attempt
     731     * @param WP_Error $error WP_Error object containing error details
     732     */
     733    public function handle_failed_login($username, $error) {
     734        // Check if custom login redirect is enabled
     735        $redirect_enabled = get_option('chrmrtns_kla_redirect_wp_login', '0');
     736        if ($redirect_enabled !== '1') {
     737            return;
     738        }
     739
     740        // Check if custom login URL is configured
     741        $custom_login_url = get_option('chrmrtns_kla_custom_login_url', '');
     742        if (empty($custom_login_url)) {
     743            return;
     744        }
     745
     746        // Get the error code from WP_Error object
     747        $error_code = $error->get_error_code();
     748
     749        // Build redirect URL with error parameter
     750        $redirect_url = add_query_arg('login_error', $error_code, $custom_login_url);
     751
     752        // Also preserve the username for better UX (optional)
     753        if (!empty($username)) {
     754            $redirect_url = add_query_arg('login', sanitize_text_field($username), $redirect_url);
     755        }
     756
     757        // Perform the redirect
     758        wp_redirect($redirect_url);
    589759        exit;
    590760    }
  • keyless-auth/trunk/keyless-auth.php

    r3389048 r3390737  
    44* Plugin URI: https://github.com/chrmrtns/keyless-auth
    55* Description: Enhanced passwordless authentication with magic email links, two-factor authentication, SMTP integration, WooCommerce integration, and comprehensive security features for WordPress.
    6 * Version: 3.2.1
     6* Version: 3.2.2
    77* Author: Chris Martens
    88* Author URI: https://github.com/chrmrtns
  • keyless-auth/trunk/readme.txt

    r3389048 r3390737  
    66Requires at least: 3.9
    77Tested up to: 6.8
    8 Stable tag: 3.2.1
     8Stable tag: 3.2.2
    99License: GPLv2 or later
    1010License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    315315
    316316== Changelog ==
     317
     318= 3.2.2 =
     319* FIX: Login error display on custom login pages - Wrong password/username errors now display properly instead of blank error
     320* FIX: wp_login_failed hook integration - Failed login attempts now redirect to custom login page with error parameters
     321* IMPROVEMENT: Error messages preserved during wp-login.php to custom page redirect flow
     322* IMPROVEMENT: Better error handling for standard WordPress password forms on custom login pages
     323* TECHNICAL: Added handle_failed_login() method to catch authentication failures and redirect with error codes
     324* TECHNICAL: Error parameters (login_error, login) now properly preserved and displayed via shortcodes
     325* COMPATIBILITY: Works harmoniously with User Enumeration Prevention feature - no conflicts
    317326
    318327= 3.2.1 =
Note: See TracChangeset for help on using the changeset viewer.