Error message
"kid" invalid, unable to lookup correct key
Also printed as
- "kid" empty, unable to lookup correct key
"\"kid\" empty, unable to lookup correct key" is thrown for tokens without a `kid` header when a key array is passed.
firebase/php-jwt (PHP) — UnexpectedValueException from JWT::decode with a key array
What it means
You passed `JWT::decode` an array of keys (typically from `JWK::parseKeySet`), and the array has no entry for the `kid` in the token header. php-jwt selects the key strictly by `kid`; it does not try other keys. The sibling "\"kid\" empty" means the token has no `kid` at all, which a key array cannot resolve.
Why it happens
The cached key set predates a rotation
commonApplications that store the JWKS in a file, a database or a long-lived cache keep the old key IDs after the provider rotates. Google and Firebase rotate their token-signing keys every few days.
Key set from another issuer or project
occasionalA JWKS URL of another tenant, or Google's OAuth certificates used for Firebase ID tokens (Firebase uses a separate key URL).
The issuer does not set kid
occasionalTokens without `kid` can only be verified with a single `Key` object, not with a key array.
How to fix it
- 1.
Use CachedKeySet
It caches the JWKS through PSR-6 and refetches when an unknown `kid` arrives, with optional rate limiting so random `kid` values cannot hammer the issuer.
php use Firebase\JWT\CachedKeySet; // Refetches the JWKS when a token names a kid the cache does not have (rate-limited). $keys = new CachedKeySet($jwksUri, $httpClient, $httpFactory, $cachePool, 3600, true); $claims = JWT::decode($token, $keys); - 2.
Pass a single Key for kid-less issuers
If the issuer has exactly one key and omits `kid`, pass that key directly.
php // Issuer without kid and with exactly one key: pass a Key object, not an array. $claims = JWT::decode($token, new Key($publicKeyPem, 'ES256'));