php

Autenticar usuario con base de datos de WordPress

En este post te mostraré cómo autenticar un usuario existente de WordPress desde un lugar externo; es decir, desde otro sistema, software, código o como le quieras llamar.

Dicho con otras palabras, te mostraré cómo hacer un login externo usando el usuario y contraseña de los usuarios de WordPress.

Por ejemplo, yo tengo un sistema en el que quiero autenticar a los usuarios con los que ya tengo en mi WordPress, así que quiero reutilizar esa tabla de usuarios y verificar si la contraseña y usuario coinciden, sin usar las funciones de WordPress.

Requisitos para leer base de datos de WordPress

Autenticar usuario de WordPress con PHP

Esto lo vamos a hacer con PHP, pero técnicamente hablando se podría hacer desde cualquier lenguaje de programación que pueda conectarse a MySQL y usar el algoritmo de hasheo de WP.

Para que funcione con PHP, el programa debe estar en el mismo servidor que el sitio de WordPress, y si no, al menos la base de datos debe estar disponible de manera remota.

Comprobar contraseña de WordPress

Esto es como hacer un login simple, lo que va a cambiar será el modo de comprobar si la contraseña es correcta, pues vamos a usar PasswordHash: Portable PHP password hashing framework.

Puedes descargarlo desde la página oficial, e incluirlo como mejor lo prefieras.

Obteniendo base de datos

En este caso usaré PDO para obtener una conexión a la base de datos. Recuerda cambiar las credenciales como sea necesario.

<?php
function obtenerBaseDeDatosWordPress()
{
    $pass = "";
    $usuario = "root";
    $nombreBaseDeDatos = "wp";
    $baseDeDatos = new PDO('mysql:host=localhost;dbname=' . $nombreBaseDeDatos, $usuario, $pass);
    $baseDeDatos->query("set names utf8;");
    $baseDeDatos->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);
    $baseDeDatos->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $baseDeDatos->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
    return $baseDeDatos;
}

Como puedes ver es una simple conexión a la base de datos de MySQL de WordPress en donde están almacenados los usuarios.

Obtener usuario de WordPress

Ahora que tenemos la conexión, vamos a leer los datos. Los usuarios están en wp_users (me parece que puede cambiar si cambiaste el prefijo de las tablas, así que podría ser simplemente users) y sus roles (administrador, suscriptor, colaborador) están en wp_usermeta por lo que la consulta y la función quedan así:

<?php
function obtenerUsuarioDeWordPress($usuarioOCorreo)
{
    $bd = obtenerBaseDeDatosWordPress();
    $sentencia = $bd->prepare("SELECT * FROM wp_users 
INNER JOIN wp_usermeta ON wp_users.ID = wp_usermeta.user_id 
WHERE (wp_users.user_login = ? OR wp_users.user_email = ?) AND wp_usermeta.meta_key = ?");
    $sentencia->execute([$usuarioOCorreo, $usuarioOCorreo, "wp_capabilities"]);
    return $sentencia->fetchObject();
}

En este caso el usuario puede autenticarse con el correo o con el nombre de usuario. La función regresará un objeto de usuario que tendrá todos los datos del mismo.

Aquí hay algo muy importante y es que el rol está dentro de meta_value pero este valor está serializado así que debemos usar unserialize y hacer un montón de comprobaciones para saber el rol.

Autenticar usuario

Ahora que ya tenemos esa función, veamos cómo autenticar. Lo que hacemos es:

  • Obtener el usuario de la base de datos de WordPress
  • Incluir a PasswordHash para comprobar la contraseña
  • Si todo es correcto, volvemos a regresar al usuario, con el rol ya arreglado
  • Si algo falla (la contraseña no es correcta, no existe el usuario, etcétera) regresamos false

Entonces queda así:

<?php
function autenticarUsuario($username, $password)
{
    # Obtener usuario. Si no existe, regresamos false inmediatamente
    $usuario = obtenerUsuarioDeWordPress($username);
    if (!$usuario) return false;
    # Incluimos PasswordHash pues vamos a realizar algunas comprobaciones
    include_once "PasswordHash.php";
    # Esta es la contraseña almacenada en la base de datos:
    $storedHash = $usuario->user_pass;
    # Crear instancia de PasswordHash
    $passwordHash = new PasswordHash(8, false);
    $metaValue = unserialize($usuario->meta_value);
    # Nota: en mi caso solo permito usuarios administradores y colaboradores; ignoro a los demás.
    # Si tú quieres permitirlos, modifica el código
    if (!isset($metaValue["administrator"]) && !isset($metaValue["contributor"])) {
        return false;
    }
    if ((isset($metaValue["administrator"]) && !$metaValue["administrator"]) && (isset($metaValue["contributor"]) && !$metaValue["contributor"])) {
        return false;
    }
    # Ahora sí comprobamos la contraseña plana ($password) con la hasheada almacenada ($storedHash)
    if ($passwordHash->CheckPassword($password, $storedHash)) {
        # Si llegamos aquí, el usuario y contraseña son correctos
        # Lo único que hacemos es poner el rol del mismo
        if ((isset($metaValue["administrator"])) && $metaValue["administrator"]) {
            $usuario->role = "administrator";
        } else if (isset($metaValue["contributor"]) && $metaValue["contributor"]) {
            $usuario->role = "contributor";
        }
        # Y regresamos el usuario
        return $usuario;
    } else {
        # Caso contrario (la contraseña no coincide) regresamos igualmente false
        return false;
    }
}

La función recibe el nombre de usuario o correo, y la contraseña plana. Obtiene el usuario, comprueba los datos y en caso de que todo vaya bien, regresará el usuario.

Si la contraseña no coincide o el usuario no existe, regresa false. A partir de la llamada de esta función podríamos iniciar sesión, pues si nos regresa un usuario podemos iniciar y establecer datos en la sesión.

A continuación te mostraré un ejemplo de uso de la función.

Poniendo todo junto

También he grabado un vídeo explicando el código, te invito a verlo y a que te suscribas:

Veamos el ejemplo completo, a continuación el código. Recuerda que la clase PasswordHash.php debe estar en el mismo directorio que este código.

<?php
/*
  ____          _____               _ _           _       
 |  _ \        |  __ \             (_) |         | |      
 | |_) |_   _  | |__) |_ _ _ __ _____| |__  _   _| |_ ___ 
 |  _ <| | | | |  ___/ _` | '__|_  / | '_ \| | | | __/ _ \
 | |_) | |_| | | |  | (_| | |   / /| | |_) | |_| | ||  __/
 |____/ \__, | |_|   \__,_|_|  /___|_|_.__/ \__, |\__\___|
         __/ |                               __/ |        
        |___/                               |___/         
    
____________________________________
/ Si necesitas ayuda, contáctame en \
\ https://parzibyte.me               /
 ------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
Creado por Parzibyte (https://parzibyte.me). Este encabezado debe mantenerse intacto,
excepto si este es un proyecto de un estudiante.
*/function obtenerBaseDeDatosWordPress()
{
    $pass = "";
    $usuario = "root";
    $nombreBaseDeDatos = "wp";
    $baseDeDatos = new PDO('mysql:host=localhost;dbname=' . $nombreBaseDeDatos, $usuario, $pass);
    $baseDeDatos->query("set names utf8;");
    $baseDeDatos->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);
    $baseDeDatos->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $baseDeDatos->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
    return $baseDeDatos;
}


function obtenerUsuarioDeWordPress($usuarioOCorreo)
{
    $bd = obtenerBaseDeDatosWordPress();
    $sentencia = $bd->prepare("SELECT * FROM wp_users 
INNER JOIN wp_usermeta ON wp_users.ID = wp_usermeta.user_id 
WHERE (wp_users.user_login = ? OR wp_users.user_email = ?) AND wp_usermeta.meta_key = ?");
    $sentencia->execute([$usuarioOCorreo, $usuarioOCorreo, "wp_capabilities"]);
    return $sentencia->fetchObject();
}

function autenticarUsuario($username, $password)
{
    # Obtener usuario. Si no existe, regresamos false inmediatamente
    $usuario = obtenerUsuarioDeWordPress($username);
    if (!$usuario) return false;
    # Incluimos PasswordHash pues vamos a realizar algunas comprobaciones
    include_once "PasswordHash.php";
    # Esta es la contraseña almacenada en la base de datos:
    $storedHash = $usuario->user_pass;
    # Crear instancia de PasswordHash
    $passwordHash = new PasswordHash(8, false);
    $metaValue = unserialize($usuario->meta_value);
    # Nota: en mi caso solo permito usuarios administradores y colaboradores; ignoro a los demás.
    # Si tú quieres permitirlos, modifica el código
    if (!isset($metaValue["administrator"]) && !isset($metaValue["contributor"])) {
        return false;
    }
    if ((isset($metaValue["administrator"]) && !$metaValue["administrator"]) && (isset($metaValue["contributor"]) && !$metaValue["contributor"])) {
        return false;
    }
    # Ahora sí comprobamos la contraseña plana ($password) con la hasheada almacenada ($storedHash)
    if ($passwordHash->CheckPassword($password, $storedHash)) {
        # Si llegamos aquí, el usuario y contraseña son correctos
        # Lo único que hacemos es poner el rol del mismo
        if ((isset($metaValue["administrator"])) && $metaValue["administrator"]) {
            $usuario->role = "administrator";
        } else if (isset($metaValue["contributor"]) && $metaValue["contributor"]) {
            $usuario->role = "contributor";
        }
        # Y regresamos el usuario
        return $usuario;
    } else {
        # Caso contrario (la contraseña no coincide) regresamos igualmente false
        return false;
    }
}

# Momento de probar. Usaré <pre> para que se vea de mejor manera, pero recuerda que solo estoy depurando ;)
echo "<pre>";
echo "Debería mostrar los datos correctos: ";
$usuarioCorrecto = autenticarUsuario("parzibyte", "hunter2");
echo json_encode($usuarioCorrecto, JSON_PRETTY_PRINT);
echo "\n\nAhora debería mostrar false: ";
$usuarioIncorrecto = autenticarUsuario("inexistente", "blabla");
echo json_encode($usuarioIncorrecto, JSON_PRETTY_PRINT);
echo "</pre>";

Igualmente dejo el código completo en mi GitHub. No olvides cambiar las credenciales de la base de datos. Aquí puedes leer más artículos de WordPress.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Ver comentarios

  • Buen post! Estoy tratando de hacer login con un usuario de Wordpress en un php externo, como el que has puesto. El caso es que, he probado lo que mencionas y nos muestras en el post pero no me funciona (he cambiado el usuario + passwd evidentemente). La salida de la web es esta:

    Debería mostrar los datos correctos: false
    Ahora debería mostrar false: false

    El login me funciona correctamente en el Wordpress..se te ocurre que puede ocurrir?

    Saludos!

Entradas recientes

Creador de credenciales web – Aplicación gratuita

Hoy te voy a presentar un creador de credenciales que acabo de programar y que…

1 semana hace

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

2 semanas hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

2 semanas hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

2 semanas hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

2 semanas hace

Esperar promesa para inicializar Store de Pinia con Vue 3

En este artículo te voy a enseñar cómo usar un "top level await" esperando a…

2 semanas hace

Esta web usa cookies.