Redirigir al usuario usando el hook 'wp_login_failed' si el error es 'empty_username' o 'empty_password'
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
authenticateestá dentro dewp_authenticate() - El hook
wp_login_failedestá dentro dewp_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?
WordPress maneja los fallos de inicio de sesión de dos maneras:
- 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 - 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_failedcaptura 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
}
} );
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
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