Error message
token signature is invalid: key is of invalid type: ECDSA verify expects *ecdsa.PublicKey
Also printed as
- token signature is invalid: key is of invalid type: HMAC verify expects []byte
- token signature is invalid: key is of invalid type: RSA verify expects *rsa.PublicKey
- key is of invalid type: ECDSA sign expects *ecdsa.PrivateKey
- key is of invalid type
v5 names the expected type ("ECDSA verify expects *ecdsa.PublicKey", "HMAC verify expects []byte"); v4 prints only "key is of invalid type". Because v5 reports it under `ErrTokenSignatureInvalid`, it looks like a signature failure at first glance.
golang-jwt/jwt (Go) — jwt.ErrInvalidKeyType
What it means
The value your `Keyfunc` returned (or the key passed to `SignedString`) has the wrong Go type for the token's algorithm. golang-jwt type-asserts the key: ES* verification needs `*ecdsa.PublicKey`, RS*/PS* need `*rsa.PublicKey`, EdDSA needs `ed25519.PublicKey`, HS* needs `[]byte`; signing needs the matching private key. No cryptography ran — the key never had a chance to be wrong, it was the wrong kind of object.
Why it happens
PEM bytes returned instead of a parsed key
commonReturning `[]byte` read from a `.pem` file works for HMAC but not for asymmetric algorithms. The bytes must be parsed into `*ecdsa.PublicKey` or `*rsa.PublicKey` first.
The private key passed for verification
common`*ecdsa.PrivateKey` is not accepted where `*ecdsa.PublicKey` is required. Return `&priv.PublicKey` — or better, keep private keys out of verifiers entirely.
An HMAC secret returned as string
commonFor HS256 the Keyfunc must return `[]byte`. A `string` (for example from `os.Getenv`) fails the type assertion.
A JWK struct or map instead of a crypto key
occasionalKeys decoded from a JWKS into a struct or `map[string]any` are not crypto keys. Convert them (for example with go-jose's `JSONWebKey.Key` or keyfunc's JWKS support).
How to fix it
- 1.
Parse PEM into a typed key once
golang-jwt ships helpers for each key type. Parse at startup and return the typed key from the Keyfunc.
go pub, err := jwt.ParseECPublicKeyFromPEM(pemBytes) // ES256/384/512 // rsaPub, err := jwt.ParseRSAPublicKeyFromPEM(pemBytes) // RS*/PS* // edPub, err := jwt.ParseEdPublicKeyFromPEM(pemBytes) // EdDSA if err != nil { log.Fatal(err) } token, err := jwt.Parse(raw, func(t *jwt.Token) (any, error) { return pub, nil }, jwt.WithValidMethods([]string{"ES256"})) - 2.
Return HMAC secrets as []byte
Convert the secret to bytes and pin `WithValidMethods` to the HMAC algorithm so tokens with an asymmetric `alg` cannot reach that code path.
go secret := []byte(os.Getenv("JWT_HMAC_SECRET")) // []byte, not string token, err := jwt.Parse(raw, func(t *jwt.Token) (any, error) { return secret, nil }, jwt.WithValidMethods([]string{"HS256"}))