Mensaje de error
Firebase\JWT\SignatureInvalidException: Signature verification failed
También se muestra como
- PHP Fatal error: Uncaught Firebase\JWT\SignatureInvalidException: Signature verification failed
Si no se captura, PHP imprime «PHP Fatal error: Uncaught Firebase\JWT\SignatureInvalidException: Signature verification failed in …/JWT.php».
firebase/php-jwt (PHP) — Firebase\JWT\SignatureInvalidException from JWT::decode
Qué significa
php-jwt analizó el token, aceptó el algoritmo para la clave que le pasas y la comprobación de la firma devolvió false: la clave y el token no se corresponden, o el token se modificó después de firmarse. `SignatureInvalidException` es una subclase de `UnexpectedValueException`, así que un bloque catch genérico puede ocultarla entre los errores de análisis.
Por qué ocurre
La clave configurada no es la del firmante
frecuenteUna clave pública copiada de otro entorno, o una clave que el emisor ya rotó. Firebase Auth y Google firman los tokens de ID con claves que cambian cada pocos días.
Una clave tomada de un conjunto de claves sin usar kid
frecuenteEl código que toma la primera clave de un JWKS (por ejemplo, `reset(JWK::parseKeySet($jwks))`) funciona hasta que el emisor firma con otra clave del conjunto.
El secreto HMAC difiere entre servicios
ocasionalCon HS256, cualquier diferencia en la cadena del secreto —espacios en blanco, un salto de línea final que viene de un archivo, base64 en un servicio y bytes en bruto en el otro— produce un MAC distinto.
El token cambió después de firmarse
ocasionalEncabezado o carga útil recodificados, o un token compuesto con partes de dos tokens.
Cómo solucionarlo
- 1.
Pasar el conjunto de claves completo y dejar que php-jwt elija por kid
`JWT::decode` acepta un array de objetos `Key` indexado por `kid` (de `JWK::parseKeySet`) o un `CachedKeySet`, que además se actualiza cuando hay una rotación.
php use Firebase\JWT\CachedKeySet; use Firebase\JWT\JWT; use GuzzleHttp\Client; use GuzzleHttp\Psr7\HttpFactory; // Keys selected by the token's kid, fetched from the issuer's JWKS and cached (PSR-6). $keys = new CachedKeySet( 'https://issuer.example.com/.well-known/jwks.json', new Client(), new HttpFactory(), $cache, // any PSR-6 cache pool 3600, // cache lifetime in seconds true // rate-limit refetches on unknown kids ); $claims = JWT::decode($token, $keys); - 2.
Comparar las mitades del par de claves
Si la clave pública derivada de la clave privada del emisor difiere de la que tienes configurada, la firma no puede verificarse.
bash # Do the issuer's private key and your public key belong together? openssl pkey -in issuer-private.pem -pubout | sha256sum openssl pkey -pubin -in configured-public.pem | sha256sum