{"record":{"id":"41c2f49f7d0a8cfd","repo":"hashicorp/nomad","slug":"unable-to-verify-signature-of-jwt-token-v","errorCode":null,"errorMessage":"unable to verify signature of JWT token: %v","messagePattern":"unable to verify signature of JWT token: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/auth/jwt/validator.go","lineNumber":69,"sourceCode":"\ttoAlgFn := func(m string) jwt.Alg { return jwt.Alg(m) }\n\talgorithms := helper.ConvertSlice(methodConf.SigningAlgs, toAlgFn)\n\n\texpected := jwt.Expected{\n\t\tAudiences:         methodConf.BoundAudiences,\n\t\tSigningAlgorithms: algorithms,\n\t\tNotBeforeLeeway:   methodConf.NotBeforeLeeway,\n\t\tExpirationLeeway:  methodConf.ExpirationLeeway,\n\t\tClockSkewLeeway:   methodConf.ClockSkewLeeway,\n\t}\n\n\tvalidator, err := jwt.NewValidator(keySet)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tclaims, err := validator.Validate(ctx, token, expected)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to verify signature of JWT token: %v\", err)\n\t}\n\n\t// validate issuer manually, because we allow users to specify an array\n\tif len(methodConf.BoundIssuer) > 0 {\n\t\tif _, ok := claims[\"iss\"]; !ok {\n\t\t\treturn nil, fmt.Errorf(\n\t\t\t\t\"auth method specifies BoundIssuers but the provided token does not contain issuer information\",\n\t\t\t)\n\t\t}\n\t\tif iss, ok := claims[\"iss\"].(string); !ok {\n\t\t\treturn nil, fmt.Errorf(\"unable to read iss property of provided token\")\n\t\t} else if !slices.Contains(methodConf.BoundIssuer, iss) {\n\t\t\treturn nil, fmt.Errorf(\"invalid JWT issuer: %v\", claims[\"iss\"])\n\t\t}\n\t}\n\n\treturn claims, nil\n}","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/lib/auth/jwt/validator.go#L51-L87","documentation":"Validate wraps any failure from the underlying JWT validator (bad signature, expired token, malformed token, wrong audience) with this message. It means the token could not be cryptographically/structurally verified against the auth method's configuration.","triggerScenarios":"Login with a JWT whose signature doesn't verify against the configured JWKS/public keys, an expired token, wrong aud claim, or a token signed by an unexpected key.","commonSituations":"IdP rotated signing keys but cached JWKS is stale; testing a dev token against prod config; clock skew causing expiry; wrong key source (JWKS URL, PEM, or OIDC discovery URL) configured on the auth method.","solutions":["Read the wrapped %v cause to distinguish expired vs bad-signature vs malformed","Re-obtain a fresh token and retry immediately","Verify the auth method's JWKSURL/KeySource/OIDCDiscoveryURL and BoundAudiences match your IdP","Check clock skew between client and server; ensure IdP keys haven't rotated without cache refresh"],"exampleFix":"// before: expired token reused from cache\ntoken := cachedToken\n// after: fetch/refresh token before login\ntoken := idp.GetFreshToken()","handlingStrategy":"try-catch","validationCode":"// verify token structurally and check expiry before calling Validate\ntoken, _, err := new(jwt.Parser).ParseUnverified(rawToken)\nif err != nil { return ErrMalformedToken }\nvar claims jwt.MapClaims\ntoken.Claims = &claims\nif exp, err := claims.GetExpirationTime(); err == nil && exp != nil && exp.Time.Before(time.Now()) {\n    return ErrTokenExpired\n}","typeGuard":null,"tryCatchPattern":"claims, err := validator.Validate(ctx, token, expected)\nif err != nil {\n    if strings.Contains(err.Error(), \"token is expired\") {\n        return refreshTokenAndRetry()\n    }\n    return fmt.Errorf(\"jwt validation failed: %w\", err)\n}","preventionTips":["Refresh tokens proactively before expiry instead of caching indefinitely","Keep the auth method's JWKS URL / OIDC discovery config in sync with your IdP","Set BoundAudiences to the exact aud value your IdP emits","Monitor NTP/clock skew on servers validating exp/iat"],"tags":["auth","jwt","signature-verification"],"backgroundTag":"jwt-signature-verification-failed","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}