Redirigir al usuario usando el hook 'wp_login_failed' si el error es 'empty_username' o 'empty_password'

18 dic 2014, 12:20:18
Vistas: 16.7K
Votos: 7

Antecedentes

Estoy intentando hacer un login frontend sin problemas para un blog, manteniendo wp-login.php disponible para usuarios que saben cómo llegar allí. wp-login.php también es necesario para autenticar logins frontend, por lo que no puede eliminarse completamente.

Estoy cerca de lograrlo, pero tengo un problema con fallos de login, específicamente con un nombre de usuario vacío.

Pregunta

¿Alguien sabe cómo evitar que wp-login.php se muestre por errores de usuario/contraseña vacíos, sin mostrar errores cuando un usuario va directamente a wp-login.php?

Mi trabajo

Encontré el hook wp_login_failed y creé la función abajo. Básicamente añade parámetros de consulta para indicar que el login falló y qué usuario se ingresó antes de redirigir al login frontend. Con estos parámetros puedo determinar por qué falló y mostrar un mensaje.

add_action('wp_login_failed', 'djg_front_end_login_fail');
function djg_front_end_login_fail($username){

    $referrer = (isset($_SERVER['HTTP_REFERER'])) ? $_SERVER['HTTP_REFERER'] : $_SERVER['PHP_SELF'];
    $referrer = add_query_arg('result', 'failed', $referrer);
    $referrer = add_query_arg('username', $username, $referrer);

    if(!empty($referrer) && !strstr($referrer, 'wp-login') && !strstr($referrer, 'wp-admin')) :
        wp_redirect($referrer);
        exit;
    endif;

}

Sin embargo, noté que si el campo de usuario estaba vacío no se redirigía y seguía mostrándose wp-login.php.

Al investigar más, encontré la función wp_authenticate() donde noté que si el primer error era empty_username o empty_password, no se ejecutaba el hook wp_login_failed. Para solucionarlo, usé el filtro authenticate y modifiqué esos errores añadiendo djg_ para que no fueran ignorados.

add_filter('authenticate', 'djg_authenticate_login', 99, 3);
function djg_authenticate_login($user, $username, $password){;

    if(is_wp_error($user)) :

        $codes = $user->get_error_codes();
        $messages = $user->get_error_messages();

        $user = new WP_Error;

        for($i = 0; $i <= count($codes) - 1; $i++) :

            $code = $codes[$i];
            if(in_array($code, array('empty_username', 'empty_password'))) :
                $code = 'djg_' . $code;
            endif;

            $user->add($code, $messages[$i]);

        endfor;

    endif;

    return $user;

}

Esto funciona, pero ahora si el usuario va directamente a wp-login.php, que debe seguir disponible, se muestra un error:

ERROR: El campo de usuario está vacío.
ERROR: El campo de contraseña está vacío.

Finalmente, encontré en la función wp_signon() el código que causa esto, pero no veo filtros que permitan cambiar este comportamiento. Básicamente dice que si los errores son exactamente empty_username y empty_password, los ignore:

if ( is_wp_error($user) ) {
    if ( $user->get_error_codes() == array('empty_username', 'empty_password') ) {
        $user = new WP_Error('', '');
    }

    return $user;
}

Detalles

  • wp_authenticate() está en /wp-includes/pluggable.php
  • El filtro authenticate está dentro de wp_authenticate()
  • El hook wp_login_failed está dentro de wp_authenticate()
  • wp_signon() está en /wp-includes/user.php

Pregunta (de nuevo)

¿Alguien sabe cómo evitar que wp-login.php se muestre por errores de usuario vacío, sin mostrar errores cuando un usuario va directamente a wp-login.php?

0
Todas las respuestas a la pregunta 1
3

WordPress maneja los fallos de inicio de sesión de dos maneras:

  1. Si son credenciales incorrectas, y tanto el nombre de usuario como la contraseña tienen un valor, entonces esta acción puede ser capturada por wp_login_failed
  2. Si ambas, o una, de las opciones están vacías, entonces WordPress genera el objeto de error como el primer parámetro en el filtro authenticate; no se abre y la acción wp_login_failed captura esta causa/evento

Para lo que hemos hecho aquí, ver comentarios en el código:

add_filter( 'authenticate', function( $user, $username, $password ) {
    // capturar forzosamente el fallo de inicio de sesión para abrir forzosamente la acción wp_login_failed,
    // de modo que este evento pueda ser capturado
    if ( empty( $username ) || empty( $password ) ) {
        do_action( 'wp_login_failed', $user );
    }
    return $user;
}, 10, 3 );

// para manejar incluso puedes manejar el error así
add_action( 'wp_login_failed', function( $username ) {
    if ( is_wp_error( $username ) ) {
        // realizar operación en el objeto de error para error vacío
    }
} );
4 abr 2015 10:53:39
Comentarios

Votado como una buena respuesta, pero por favor corrige el problema con el hook authenticate. Sin pasar una prioridad y número de argumentos, esto siempre disparará una acción de inicio de sesión fallido debido a cómo funcionan los filtros y hooks (por defecto, solo permiten pasar un argumento al método).

Tom Dyer Tom Dyer
6 mar 2018 14:33:27

El parámetro $username en wp_login_failed es un string según la documentación para desarrolladores de wp_login_failed - ¿cómo obtenemos un objeto de error a partir de él ahora?

Cush Cush
15 sept 2018 04:19:47

@TomDyer tiene razón, deduciendo de la pregunta la declaración correcta debería ser add_filter( 'authenticate', function(...) {...}, 99, 3);

GigiSan GigiSan
17 oct 2018 12:58:25