Error message
token signature is invalid: signing method ES256 is invalid
Also printed as
- signing method ES256 is invalid
- token signature is invalid: signing method HS256 is invalid
- token is unverifiable: signing method (alg) is unavailable
v4 prints "signing method ES256 is invalid". An `alg` value golang-jwt does not know at all (or `none` without the special constant) gives "token is unverifiable: signing method (alg) is unavailable" instead.
golang-jwt/jwt (Go) — WithValidMethods rejected the token's alg
What it means
The token's `alg` header is not in the list you passed with `jwt.WithValidMethods`, so golang-jwt refused it before calling your Keyfunc's key. The check exists because the header is attacker-controlled: the verifier, not the token, decides which algorithm is acceptable. v5 reports it under `ErrTokenSignatureInvalid`, which makes it look like a bad signature — it is an algorithm-policy rejection.
Why it happens
The issuer changed algorithm
commonIdentity providers move from RS256 to ES256 or add EdDSA keys. Verifiers that pinned the old algorithm reject every new token from the moment of the switch.
The allow-list was copied from another service
occasionalA shared middleware configured for one issuer (RS256) is reused for another that signs with ES256. The allow-list must describe the issuer behind this particular verifier.
A token from elsewhere, or a probe
occasionalTokens with `alg: HS256` or `none` reaching an RS256/ES256 API are typical of algorithm-confusion probes. Keep rejecting them.
How to fix it
- 1.
Allow exactly the issuer's algorithm
Read the issuer's JWKS (`alg` of each key) or its discovery document (`id_token_signing_alg_values_supported`) and pin those values. During a migration, list both and select keys by `kid`.
go token, err := jwt.Parse(raw, keyfunc, jwt.WithValidMethods([]string{"ES256"}), // what the issuer signs with today jwt.WithIssuer("https://issuer.example.com/"), ) - 2.
Assert the method type in the Keyfunc
Checking `t.Method`'s concrete type in the Keyfunc guarantees that an ECDSA key is only ever used with an ECDSA algorithm, even if the allow-list is later widened.
go keyfunc := func(t *jwt.Token) (any, error) { // Belt and braces: the key type must fit the algorithm family. if _, ok := t.Method.(*jwt.SigningMethodECDSA); !ok { return nil, fmt.Errorf("unexpected signing method %v", t.Header["alg"]) } return lookupKey(t.Header["kid"]) }