Como autenticar con JWT para REST y configurar CORS en WordPress

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

  1. Desde el escritorio de WordPress: Plugins → Añadir nuevo → buscar JWT Authentication for WP REST API (u otro similar) → Instalar y activar.
  2. Añadir la clave secreta en wp-config.php:
    
        
  3. 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

  1. Usa HTTPS en todo el dominio que sirva la REST API y el frontend.
  2. Define tokens con expiración corta y refresco controlado.
  3. 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).
  4. Valida claims (iss, aud, exp) en cada decodificación.
  5. Limita Access-Control-Allow-Origin a orígenes de confianza en producción, evita .
  6. 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 🙂



Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *