{"record":{"id":"545b6504a103461c","repo":"hashicorp/nomad","slug":"unable-to-get-validation-keys-from-oidc-provider","errorCode":null,"errorMessage":"unable to get validation keys from OIDC provider: %v","messagePattern":"unable to get validation keys from OIDC provider: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/auth/jwt/validator.go","lineNumber":124,"sourceCode":"\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}\n\treturn keySet, nil\n}\n","sourceCodeStart":106,"sourceCodeEnd":128,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/lib/auth/jwt/validator.go#L106-L128","documentation":"usingOIDC fails when jwt.NewOIDCDiscoveryKeySet cannot complete OIDC discovery — fetching `<oidc_discovery_url>/.well-known/openid-configuration` and then the JWKS it references. This is typically a network/TLS problem or an endpoint that is not a valid OIDC provider.","triggerScenarios":"Validate → usingOIDC when the auth method uses OidcDiscoveryURL and discovery document or key fetch fails, including when OidcDiscoveryCACert cannot validate the provider's TLS chain.","commonSituations":"Internal OIDC provider with private CA and no OidcDiscoveryCACert set; discovery URL missing the realm/tenant path; provider temporarily down; egress blocked from Nomad servers; provider does not publish openid-configuration at the expected path.","solutions":["Verify `curl <oidc_discovery_url>/.well-known/openid-configuration` returns valid discovery JSON from a Nomad server.","Set OidcDiscoveryCACert to the PEM of the CA that signed the provider's certificate if using a private/self-signed CA.","Fix the OidcDiscoveryURL (include issuer path, e.g. `https://idp.example.com/realms/prod`).","Check provider availability and Nomad server egress/firewall rules."],"exampleFix":"// before\ncfg := &structs.ACLAuthMethodConfig{\n  OidcDiscoveryURL: \"https://idp.internal:8443\",\n}\n// after: CA pinned for private endpoint\ncfg := &structs.ACLAuthMethodConfig{\n  OidcDiscoveryURL:     \"https://idp.internal:8443/realms/main\",\n  OidcDiscoveryCACert:  \"-----BEGIN CERTIFICATE-----\\n...\\n-----END CERTIFICATE-----\",\n}","handlingStrategy":"validation","validationCode":"discURL := strings.TrimSuffix(oidcURL, \"/\") + \"/.well-known/openid-configuration\"\nresp, err := http.Get(discURL)\nif err != nil { return fmt.Errorf(\"discovery unreachable: %w\", err) }\nif resp.StatusCode != 200 { return fmt.Errorf(\"discovery returned %d\", resp.StatusCode) }\nvar doc struct{ Issuer string `json:\"issuer\"`; JwksURI string `json:\"jwks_uri\"` }\njson.NewDecoder(resp.Body).Decode(&doc)\nif doc.Issuer == \"\" || doc.JwksURI == \"\" { return fmt.Errorf(\"not a valid OIDC discovery doc\") }","typeGuard":null,"tryCatchPattern":"keySet, err := usingOIDC(ctx, oidcURL, caPEMs)\nif err != nil {\n  return fmt.Errorf(\"verify OidcDiscoveryURL and OidcDiscoveryCACert: %w\", err)\n}","preventionTips":["Validate the discovery URL returns openid-configuration JSON before configuring the auth method.","Set OidcDiscoveryCACert when the provider uses a private CA.","Include the full issuer path (realm/tenant) in OidcDiscoveryURL."],"tags":["oidc","jwt","discovery","network","tls"],"backgroundTag":"oidc-discovery-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"}