{"record":{"id":"6e37db08e12f4bbb","repo":"hashicorp/nomad","slug":"unable-to-get-validation-keys-from-jwks-v","errorCode":null,"errorMessage":"unable to get validation keys from JWKS: %v","messagePattern":"unable to get validation keys from JWKS: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/auth/jwt/validator.go","lineNumber":107,"sourceCode":"func usingStaticKeys(keys []string) (jwt.KeySet, error) {\n\tvar parsedKeys []crypto.PublicKey\n\tfor _, v := range keys {\n\t\tkey, err := jwt.ParsePublicKeyPEM([]byte(v))\n\t\tparsedKeys = append(parsedKeys, key)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unable to parse public key for JWT auth: %v\", err)\n\t\t}\n\t}\n\treturn jwt.NewStaticKeySet(parsedKeys)\n}\n\nfunc usingJWKS(ctx context.Context, jwksurl, jwkscapem string) (jwt.KeySet, error) {\n\t// Measure the JWKS endpoint performance.\n\tdefer metrics.MeasureSince([]string{\"nomad\", \"acl\", \"jwt\", \"jwks\"}, time.Now())\n\n\tkeySet, err := jwt.NewJSONWebKeySet(ctx, jwksurl, jwkscapem)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to get validation keys from JWKS: %v\", err)\n\t}\n\treturn keySet, nil\n}\n\nfunc usingOIDC(ctx context.Context, oidcurl string, oidccapem []string) (jwt.KeySet, error) {\n\t// Measure the OIDC endpoint performance.\n\tdefer metrics.MeasureSince([]string{\"nomad\", \"acl\", \"jwt\", \"oidc_jwt\"}, time.Now())\n\n\t// TODO why do we have DiscoverCaPem as an array but JWKSCaPem as a single string?\n\tpem := \"\"\n\tif len(oidccapem) > 0 {\n\t\tpem = oidccapem[0]\n\t}\n\n\tkeySet, err := jwt.NewOIDCDiscoveryKeySet(ctx, oidcurl, pem)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to get validation keys from OIDC provider: %v\", err)\n\t}","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/lib/auth/jwt/validator.go#L89-L125","documentation":"usingJWKS fails when jwt.NewJSONWebKeySet cannot fetch or parse the JWKS document from the configured jwks_url. Nomad fetches signing keys from the provider's JWKS endpoint at login time; any network, TLS, or format problem is reported here.","triggerScenarios":"Validate → usingJWKS when the auth method has JwksURL set and the HTTP GET of the JWKS URL fails or returns unparseable JSON, or the optional JWKSCACert does not validate the endpoint's TLS chain.","commonSituations":"JWKS URL unreachable from Nomad servers (firewall, DNS); private/internal IdP endpoint with a self-signed or private CA cert not supplied via JWKSCACert; provider returns non-200/HTML error page; typo'd URL.","solutions":["From a Nomad server node, test reachability: `curl -v <jwks_url>` and confirm valid JWKS JSON (keys array) is returned.","If the endpoint uses a private CA, set JWKSCACert on the auth method to the CA's PEM.","Correct the jwks_url in the auth method config (usually `<issuer>/.well-known/jwks.json` or the provider's documented JWKS path).","Check proxy/firewall rules allowing egress from all Nomad servers."],"exampleFix":"// before\ncfg := &structs.ACLAuthMethodConfig{\n  JwksURL: \"https://keycloak.internal/realms/prod/protocol/openid-connect/certs\",\n}\n// after: trust the internal CA\ncfg := &structs.ACLAuthMethodConfig{\n  JwksURL:     \"https://keycloak.internal/realms/prod/protocol/openid-connect/certs\",\n  JWKSCACert:  \"-----BEGIN CERTIFICATE-----\\n...\\n-----END CERTIFICATE-----\",\n}","handlingStrategy":"validation","validationCode":"resp, err := http.Get(jwksURL)\nif err != nil { return fmt.Errorf(\"JWKS unreachable: %w\", err) }\nif resp.StatusCode != 200 { return fmt.Errorf(\"JWKS returned %d\", resp.StatusCode) }\nvar doc struct{ Keys []map[string]any `json:\"keys\"` }\nif err := json.NewDecoder(resp.Body).Decode(&doc); err != nil || len(doc.Keys) == 0 {\n  return fmt.Errorf(\"JWKS body invalid\")\n}","typeGuard":null,"tryCatchPattern":"keySet, err := usingJWKS(ctx, jwksURL, caPEM)\nif err != nil {\n  return fmt.Errorf(\"check JwksURL reachability from Nomad servers and JWKSCACert: %w\", err)\n}","preventionTips":["curl the JWKS URL from every Nomad server node during setup.","Always set JWKSCACert for internal IdPs with private CAs.","Monitor the nomad.acl.jwt.jwks metric for latency/failure spikes."],"tags":["jwt","jwks","network","oidc","tls"],"backgroundTag":"jwks-fetch-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"}