# Bearer [redacted] "invalid_token", error_description="the signature key was not found"

## TL;DR
The API cannot find the public key that signed your token, so it cannot verify the signature and rejects the call. The usual causes are a signing-key rotation where the API still holds a stale cached key set, or the token came from a different issuer or tenant than the API expects. Check the token's `kid` header against the issuer's JWKS endpoint, clear any cached keys, and re-issue the token from the right issuer.

```text
Bearer [redacted] "invalid_token", error_description="the signature key was not found"
```

Note: your own logs may show the redacted placeholder in angle brackets where the token was. The error text after it is what matters.

## Use this when
- An API call returns invalid_token with "the signature key was not found"
- A working integration breaks right after the identity provider rotated keys
- Tokens minted in staging fail against production, or vice versa
- A multi-tenant setup starts rejecting tokens for one tenant only

## Not for this skill when
- The error is about expiry ("token has been expired or revoked")
- The token itself is malformed or fails to decode
- The failure happens during the OAuth code exchange rather than on API calls

## Steps
1. Decode the token header offline. You only need the header, never the signature, and you do not need any library:
   ```bash
   python3 -c "
   import base64, json
   h = '[paste the token]'.split('.')[0]
   h += '=' * (-len(h) % 4)
   print(json.dumps(json.loads(base64.urlsafe_b64decode(h)), indent=2))
   "
   ```
   Expected: JSON showing `alg` (usually RS256), and a `kid` value identifying the signing key.

2. Fetch the issuer's current key set and compare. The JWKS endpoint is published at the issuer's well-known URL:
   ```bash
   curl -s https://example.com/.well-known/jwks.json | python3 -c "import json,sys; print([k['kid'] for k in json.load(sys.stdin)['keys']])"
   ```
   Expected: the token's `kid` appears in the list. If it does not, the keys rotated or the token came from a different issuer.

3. If the kid is missing, flush the API's cached key set. Many gateways and SDKs cache JWKS for minutes to hours. Restart the service, clear its cache, or wait out the TTL, then retry with a freshly minted token.
   Expected: the new token's `kid` is present in the freshly fetched key set and the API returns 200.

4. Rule out a wrong-issuer token. Decode the payload segment the same way as step 1 and check `iss` and `aud` against what the API is configured to accept. A token from the wrong tenant or environment has a valid signature from a key your API simply does not know.
   Expected: `iss` matches the API's configured issuer exactly. If not, point the client at the right issuer and re-authenticate.

### Variant: "the signature key was not found" after key rotation
Identity providers rotate signing keys on a schedule. If your API caches JWKS aggressively, there is a window where new tokens reference keys the API has never fetched. Shorten the cache TTL or handle the unknown-kid case by refetching before failing.

### Variant: kid mismatch in Auth0, Okta, or Keycloak
Same diagnosis, provider-specific JWKS URL. Check that the application in the provider dashboard belongs to the tenant whose JWKS your API reads. Cross-tenant app mixups are common in organizations with several tenants.

### Variant: invalid_token with a valid kid
Then the problem is not the key lookup. Move on to signature verification (was the token tampered with?), `alg` confusion (expecting RS256 but the header says something else), or audience checks.

### Variant: works with a new token, fails with an old one
Old tokens signed by a retired key keep failing forever. That is by design. Re-issue rather than trying to resurrect the old key.

## Why this happens
RS256-signed tokens are verified with the matching public key, which the API looks up by the `kid` in the token header from the issuer's published key set. Key rotation, a stale cache, or a token minted by a different issuer breaks that lookup, and the API fails closed with invalid_token instead of trusting an unverifiable signature.

## Edge cases and pitfalls
- Some providers publish several keys during a rotation overlap. Your API must try the right one by `kid`, not just grab the first.
- A long JWKS cache TTL turns every rotation into an outage. Keep it short or make unknown-kid trigger a refetch.
- Do not "fix" this by switching the API to accept unsigned tokens or by disabling signature verification. That trades a lookup failure for no authentication at all.
- Logging full tokens to debug this leaks credentials into your logs. Log the `kid`, `iss`, and `alg` only.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_CPov164tvD0maPtHIWk03Q
