Contents
Introducción
Este tutorial explica, paso a paso y con ejemplos prácticos, cómo implementar autenticación con JWT (JSON Web Tokens) en la REST API de WordPress y cómo configurar CORS para permitir llamadas desde aplicaciones externas (SPA, aplicaciones móviles, etc.). Se incluyen dos aproximaciones: usar el plugin más conocido y una implementación personalizada con firebase/php-jwt, además de la configuración del servidor (Apache/Nginx) y ejemplos de cliente (curl y fetch).
Conceptos clave
- JWT: token compacto y firmado que se puede usar para autenticar peticiones sin estado.
- REST API de WordPress: expone rutas bajo /wp-json podemos proteger rutas y validar tokens antes de servir la respuesta.
- CORS: Cross-Origin Resource Sharing hay que exponer cabeceras que permitan peticiones desde otro origen y aceptar la cabecera Authorization.
- Requisitos mínimos: WordPress 5.x , HTTPS (fuertemente recomendado), acceso a editar tema/plugin o servidor para añadir cabeceras.
Opción A — Usar un plugin existente (rápido)
Instalación
- Desde el escritorio de WordPress: Plugins → Añadir nuevo → buscar JWT Authentication for WP REST API (u otro similar) → Instalar y activar.
- Añadir la clave secreta en wp-config.php:
- Comprobar en la documentación del plugin si requiere alguna constante adicional para CORS algunos plugins exponen una ruta /wp-json/jwt-auth/v1/token.
Obtener un token (ejemplo con curl)
curl -X POST https://tudominio.com/wp-json/jwt-auth/v1/token -H Content-Type: application/json -d {username:usuario,password:contraseña}
La respuesta incluirá el token en la propiedad token (o access_token según el plugin).
Consumir la API con el token
curl -X GET https://tudominio.com/wp-json/wp/v2/posts -H Authorization: Bearer TU.JWT.AQUI
Opción B — Implementación personalizada con firebase/php-jwt
Por qué implementarlo tú
- Control total sobre claims, expiración, refresh tokens y revocación.
- Integración con permisos y roles de WordPress (current_user_can, WP_User).
Instalación de dependencias
Si tienes composer en el entorno del servidor (o en un plugin personalizado):
composer require firebase/php-jwt
Crear endpoint para emitir tokens
Ejemplo mínimo para un plugin o functions.php (protege en plugin real):
POST, callback => cj_get_jwt_token, permission_callback => __return_true, ]) }) function cj_get_jwt_token(WP_REST_Request request) { params = json_decode(request->get_body(), true) if (empty(params[username]) empty(params[password])) { return new WP_Error(invalid_request, Faltan credenciales, [status => 400]) } user = wp_authenticate(params[username], params[password]) if (is_wp_error(user)) { return new WP_Error(invalid_credentials, Usuario o contraseña incorrectos, [status => 401]) } issuedAt = time() expire = issuedAt (60 60) // 1 hora payload = [ iss => get_site_url(), iat => issuedAt, exp => expire, sub => (int) user->ID, roles => user->roles, ] secret_key = defined(JWT_AUTH_SECRET_KEY) ? JWT_AUTH_SECRET_KEY : pon-una-clave-secreta jwt = JWT::encode(payload, secret_key, HS256) return [ token => jwt, expires => expire, user_id => user->ID ] } ?>
Validar JWT en cada petición REST
Usar el filtro rest_authentication_errors para autenticar al usuario WordPress según el token en la cabecera Authorization:
403]) } try { secret_key = defined(JWT_AUTH_SECRET_KEY) ? JWT_AUTH_SECRET_KEY : pon-una-clave-secreta decoded = FirebaseJWTJWT::decode(token, new Key(secret_key, HS256)) } catch (Exception e) { return new WP_Error(invalid_token, Token inválido: . e->getMessage(), [status => 401]) } // Recuperar el usuario por sub if (empty(decoded->sub)) { return new WP_Error(invalid_token_payload, Payload inválido, [status => 401]) } user = get_user_by(ID, intval(decoded->sub)) if (!user) { return new WP_Error(invalid_user, Usuario no encontrado, [status => 401]) } // Loguea el usuario para que current_user_ funcione wp_set_current_user(user->ID) return result } ?>
Configurar CORS para la REST API
Las peticiones desde otro origen necesitan que el servidor exponga cabeceras CORS. Además, hay que permitir la cabecera Authorization para enviar tokens.
Solución WordPress (recomendada — añadir cabeceras desde WP)
Si usas Apache (.htaccess)
Agregar cabeceras y asegurarse de pasar Authorization al entorno PHP:
# .htaccess (dentro del directorio raíz de WP)Header set Access-Control-Allow-Origin https://tudominiofront.com Header set Access-Control-Allow-Methods GET,POST,PUT,PATCH,DELETE,OPTIONS Header set Access-Control-Allow-Headers Authorization,Content-Type Header set Access-Control-Allow-Credentials true # Pasar Authorization a PHP (importante en algunos hosts) SetEnvIf Authorization (.) HTTP_AUTHORIZATION=1
Si usas Nginx
# Dentro del server block add_header Access-Control-Allow-Origin https://tudominiofront.com add_header Access-Control-Allow-Methods GET, POST, OPTIONS, PUT, DELETE add_header Access-Control-Allow-Headers Authorization,Content-Type add_header Access-Control-Allow-Credentials true # Manejar preflight (opciones) if (request_method = OPTIONS ) { add_header Access-Control-Allow-Origin https://tudominiofront.com add_header Access-Control-Allow-Methods GET, POST, OPTIONS, PUT, DELETE add_header Access-Control-Allow-Headers Authorization,Content-Type add_header Access-Control-Allow-Credentials true return 204 }
Proteger rutas y permisos
Al registrar rutas REST personalizadas, usar permission_callback para comprobar capacidad o validar JWT:
GET, callback => mi_callback, permission_callback => function() { // current_user_can funciona si el filtro de JWT ha seteado current user return current_user_can(read) } ]) ?>
Refresh tokens y revocación
- Implementación recomendada: tokens de acceso cortos (p. ej. 15-60 minutos) y refresh tokens largos almacenados en la base de datos con relación al user_id y fecha de expiración.
- Al revocar un refresh token, el servidor marca como inválido y obligará al usuario a re-identificarse.
- Alternativa: implementar una lista negra de JWT (jti) almacenada en DB. Cada vez que se quiera revocar un token se añade el jti a la blacklist y en la validación se comprueba.
Ejemplos de cliente
Obtener token con fetch (JS)
fetch(https://tudominio.com/wp-json/custom-jwt/v1/token, { method: POST, headers: {Content-Type: application/json}, body: JSON.stringify({username: usuario, password: contraseña}) }) .then(r => r.json()) .then(data => { // data.token contiene el JWT localStorage.setItem(jwt, data.token) })
Consumir API con token
const token = localStorage.getItem(jwt) fetch(https://tudominio.com/wp-json/wp/v2/posts, { headers: { Authorization: Bearer token, Content-Type: application/json } }) .then(r => r.json()) .then(posts => console.log(posts))
Comandos útiles de prueba (curl)
# Obtener token curl -X POST https://tudominio.com/wp-json/custom-jwt/v1/token -H Content-Type: application/json -d {username:usuario,password:contraseña} # Consumir API con token curl -X GET https://tudominio.com/wp-json/wp/v2/posts -H Authorization: Bearer TU.JWT.AQUI
Problemas comunes y soluciones
- Cabecera Authorization no llega a PHP: En Apache añade SetEnvIf Authorization (.) HTTP_AUTHORIZATION=1 o configura fastcgi_param HTTP_AUTHORIZATION http_authorization en Nginx.
- Preflight (OPTIONS) falla: Asegúrate de responder a OPTIONS con cabeceras CORS y código 200/204 sin requerir autenticación.
- Token caducado: Implementa refresh tokens o fuerza re-login. Revisa la hora del servidor (desincronización puede invalidar tokens).
- Seguridad: Usar HTTPS siempre mantener la clave secreta fuera de repositorios rotar claves si se sospecha compromiso.
Buenas prácticas y recomendaciones
- Usa HTTPS en todo el dominio que sirva la REST API y el frontend.
- Define tokens con expiración corta y refresco controlado.
- Almacena refresh tokens con hashing o en tabla segura no expongas refresh tokens en localStorage sin evaluar riesgos (usar cookies seguras y httpOnly si es posible).
- Valida claims (iss, aud, exp) en cada decodificación.
- Limita Access-Control-Allow-Origin a orígenes de confianza en producción, evita .
- Registra eventos de inicio de sesión y uso de refresh tokens para auditoría y detección de abuso.
Resumen práctico
1) Para una integración rápida, instala el plugin JWT. 2) Si necesitas control, crea tu endpoint con firebase/php-jwt, genera y valida tokens en rest_authentication_errors y usa permission_callback para proteger rutas. 3) Asegura CORS permitiendo la cabecera Authorization y respondiendo a OPTIONS. 4) Usa HTTPS, expira tokens y ofrece un mecanismo de refresh y revocación.
Enlaces útiles
|
Acepto donaciones de BAT's mediante el navegador Brave 🙂 |