# JsonWebTokenError: invalid signature

## TL;DR
The key or algorithm used to verify the token doesn't match what signed it. Compare the signing key on the issuer with the verification key on the verifier, and confirm both sides agree on the algorithm. Nine times out of ten, someone rotated a key on one side only.

```text
JsonWebTokenError: invalid signature
```

## Use this when
- JWT verification fails immediately after a deploy or a key rotation
- Tokens minted by one service fail verification in another
- Verification works in staging but fails in production (or vice versa)
- An identity provider's tokens fail against your manually configured key

## Not for this skill when
- The error is TokenExpiredError (expired, not mis-signed)
- The token is malformed and won't decode at all
- You control neither the issuer nor the verifier (that's a vendor support ticket)

## Steps
1. Decode the header to see the algorithm and key id:
```
node -e "const h='PASTE_VALUE_HERE'.split('.')[0]; console.log(JSON.parse(Buffer.from(h,'base64').toString()))"
```
Expected: something like {"alg":"HS256","typ":"JWT"}, plus a kid field if the issuer rotates keys.

2. Verify the verifier is using the exact same key material as the signer. Print a fingerprint on both sides (never the key itself):
```
node -e "const c=require('crypto'); console.log(c.createHash('sha256').update(process.env.SIGNING_KEY).digest('hex').slice(0,16))"
```
Expected: the fingerprints match. If they don't, you found it; sync the key and re-test.

3. Check for algorithm confusion: if the verifier accepts the none algorithm, or the issuer switched between HS256 and RS256, pin the expected algorithm:
```js
jwt.verify(value, key, { algorithms: ["RS256"] });
```
Expected: tokens with a different alg are rejected cleanly instead of mis-verified.

4. If the issuer rotates keys (kid header present), make the verifier fetch the matching public key by kid instead of using one static key. Most provider SDKs do this for you once pointed at the JWKS endpoint.
Expected: the next rotation stops breaking verification.

5. Re-test with a freshly minted token after each change, not a token minted before the fix.
Expected: verification succeeds consistently.

### Variant: invalid signature with Auth0, Clerk, or another provider
You probably pointed at the wrong tenant or pasted the wrong public key. Re-copy the JWKS URL or public key from the provider dashboard for the exact tenant and environment, and confirm the issuer claim matches.

### Variant: signature fails only in one environment
An env var is missing there and the code fell back to a default key. Search for fallback defaults in the config and make the key required at boot so a missing value fails loudly.

### Variant: invalid algorithm sibling error
Same family, same fix: pin the algorithms list on verify and make the issuer's alg match it.

## Why this happens
A JWT signature is computed over the header and payload with a specific key and algorithm. Any difference, a rotated key on one side, a stray whitespace in the env var, an algorithm switch, breaks verification. The error is deliberately vague so it doesn't leak which side is wrong.

## Edge cases and pitfalls
- Trailing newlines in env-provided keys are a classic; trim the value on load.
- Don't log the key while debugging; log the fingerprint from step 2.
- HS256 verified with an RSA public key pasted into the symmetric setting is the algorithm-confusion bug; keep symmetric and asymmetric keys in separate settings.
- If tokens are minted by a third party, you can't fix their signing; the fix is always on your verification config.

## Provenance

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