Saltar al contenido
AldeaCode Logo
Decodificador JWT / PHP Desarrollador 100% local

Decodificar JWT en PHP: firebase/php-jwt, lcobucci/jwt y la vía manual

Decodificar un JWT en PHP son tres operaciones de string y dos json_decode. La vía con librería es una línea. La trampa que rompe la mitad de los decodificadores caseros es el alfabeto base64 URL-safe, que el base64_decode de PHP no entiende por defecto.

Decodificar no es verificar

Un JWT tiene tres partes unidas por puntos: header.payload.signature. El header y el payload son JSON codificado en base64url. La firma es un MAC o una firma asimétrica sobre header.payload. Decodificar solo lee las dos primeras partes. Te dice lo que el token afirma, no si la afirmación es genuina.

Si aceptas un JWT decodificado como prueba de identidad sin verificar la firma, un atacante cambia el payload, lo recodifica y entra como cualquiera. El error clásico es loggear el claim sub decodificado y tratar a ese usuario como autenticado. Decodifica para inspección, depuración y herramientas CLI. Verifica en producción siempre.

Las librerías de abajo tienen vía de verificación. Úsala. El snippet manual al final de la página es solo para inspección y lo deja claro en el comentario.

firebase/php-jwt es la opción por defecto

firebase/php-jwt es la librería más extendida y la que recomienda el registro de Composer. Instala con composer require firebase/php-jwt. La llamada de decode recibe el token, la clave y el algoritmo:

```php use Firebase\JWT\JWT; use Firebase\JWT\Key;

$payload = JWT::decode($token, new Key($secret, 'HS256')); ```

El resultado es un stdClass con los claims del payload. La librería valida la firma, los claims exp, nbf e iat, y lanza una excepción ante cualquier fallo. Si solo quieres inspeccionar el token sin clave, la librería no expone una llamada pública sin verificar; entonces toca splittear a mano.

lcobucci/jwt para parsing más rico

lcobucci/jwt es la segunda opción mainstream, con una API más orientada a objetos y mejor tipado en el conjunto de claims. Es la opción cuando necesitas inspeccionar la estructura del token, iterar claims, o construir un token con un builder fluido.

El paso de parse no verifica; devuelve un objeto Token que puedes inspeccionar. La verificación va por un Validator aparte. La separación te permite loggear el contenido del token para depurar sin mezclar las dos operaciones. La librería pesa más que firebase/php-jwt pero compensa en bases de código grandes.

El detalle base64 URL-safe en PHP

Los JWT usan base64url, la variante URL-safe definida en RFC 4648 sección 5. Reemplaza + por - y / por _, y descarta el padding final =. El base64_decode de PHP espera el alfabeto estándar. Si le pasas un segmento de JWT directo, devuelve basura o false en modo estricto.

La solución es un str_replace de dos pasos más restauración del padding antes de base64_decode:

function base64UrlDecode(string $data): string {
    $remainder = strlen($data) % 4;
    if ($remainder) {
        $data .= str_repeat('=', 4 - $remainder);
    }
    return base64_decode(strtr($data, '-_', '+/'));
}

Todas las librerías envuelven esto internamente. Los decodificadores caseros olvidan el padding y producen string vacío para tokens cuyo payload no es múltiplo de cuatro. El paso de padding no es opcional.

Ejemplo completo

php
<?php
// Decode manual solo para inspección. NUNCA confíes en el resultado sin verificar.
function base64UrlDecode(string $data): string {
    $remainder = strlen($data) % 4;
    if ($remainder) {
        $data .= str_repeat('=', 4 - $remainder);
    }
    return base64_decode(strtr($data, '-_', '+/'));
}

function jwtInspect(string $token): array {
    $parts = explode('.', $token);
    if (count($parts) !== 3) {
        throw new InvalidArgumentException('No es un JWT');
    }
    return [
        'header'  => json_decode(base64UrlDecode($parts[0]), true),
        'payload' => json_decode(base64UrlDecode($parts[1]), true),
        'sig_b64' => $parts[2],
    ];
}

// Vía de verificación en producción con firebase/php-jwt
// composer require firebase/php-jwt
use Firebase\JWT\JWT;
use Firebase\JWT\Key;

$verified = JWT::decode($token, new Key($secret, 'HS256'));
echo $verified->sub;

¿Solo necesitas el resultado?

Cuando tienes un token en una pestaña de Postman o en un log y solo quieres leer los claims sin escribir PHP, pégalo en el decodificador JWT de aldeacode.com. La página parsea header y payload en tu navegador, nunca envía el token a ningún sitio, y muestra el claim exp en tu zona horaria local.

Abrir Decodificador JWT →

Preguntas frecuentes

¿Puedo decodificar un JWT en PHP sin librería?

Sí para inspección. Splittea por los puntos, base64url-decode las dos primeras partes, json_decode. No uses el resultado para autorización. La firma queda sin verificar y es trivialmente falsificable.

¿Por qué base64_decode devuelve false en segmentos JWT?

Los JWT usan el alfabeto base64 URL-safe sin padding. El base64_decode de PHP espera base64 estándar. Traduce guion a más, guion bajo a barra, y rellena con signos de igual hasta múltiplo de cuatro antes de decodificar.

¿firebase/php-jwt o lcobucci/jwt?

firebase/php-jwt es más pequeña, más simple, y lo que asume cualquier tutorial. lcobucci/jwt tiene un modelo de objetos más rico y mejores primitivas de validación. En proyecto nuevo, firebase/php-jwt salvo que necesites la estructura extra.