{"record":{"id":"6efcd22d83d33030","repo":"hashicorp/nomad","slug":"w-key-header-for-key-id-must-be-q","errorCode":null,"errorMessage":"%w; key header for key ID must be %q","messagePattern":"%w; key header for key ID must be %q","errorType":"validation","errorClass":"ErrInvalidKeyIDHeader","httpStatus":null,"severity":"error","filePath":"nomad/structs/acl.go","lineNumber":1891,"sourceCode":"\tif k.PemCert != \"\" && (k.PemCertFile != \"\" || k.KeyID != \"\") {\n\t\treturn ErrAmbiguousClientAssertionKeyID\n\t}\n\tif k.PemCertFile != \"\" && (k.PemCert != \"\" || k.KeyID != \"\") {\n\t\treturn ErrAmbiguousClientAssertionKeyID\n\t}\n\tif k.KeyID != \"\" && (k.PemCert != \"\" || k.PemCertFile != \"\") {\n\t\treturn ErrAmbiguousClientAssertionKeyID\n\t}\n\tif k.PemCertFile != \"\" {\n\t\tif !path.IsAbs(k.PemCertFile) {\n\t\t\treturn fmt.Errorf(\"%w: must be absolute; got: %s\", ErrInvalidClientAssertionCertPath, k.PemCertFile)\n\t\t}\n\t}\n\n\t// only allow certain key id headers\n\t// only \"kid\" for KeyID\n\tif k.KeyID != \"\" && k.KeyIDHeader != OIDCClientAssertionHeaderKid {\n\t\treturn fmt.Errorf(\"%w; key header for key ID must be %q\",\n\t\t\tErrInvalidKeyIDHeader, OIDCClientAssertionHeaderKid)\n\t}\n\t// only \"x5t*\" for certs\n\tif k.PemCert != \"\" || k.PemCertFile != \"\" {\n\t\tif k.KeyIDHeader != OIDCClientAssertionHeaderX5t && k.KeyIDHeader != OIDCClientAssertionHeaderX5tS256 {\n\t\t\treturn fmt.Errorf(\"%w; certificate-derived key header must be one of: %q, %q\",\n\t\t\t\tErrInvalidKeyIDHeader, OIDCClientAssertionHeaderX5tS256, OIDCClientAssertionHeaderX5t)\n\t\t}\n\t\tif fips140.Enabled() && k.KeyIDHeader == OIDCClientAssertionHeaderX5t {\n\t\t\treturn errors.New(\"x5t assertion headers use SHA-1, which is forbidden in FIPS-140 mode\")\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// ACLAuthClaims is the claim mapping of the OIDC auth method in a format that\n// can be used with go-bexpr. This structure is used during rule binding","sourceCodeStart":1873,"sourceCodeEnd":1909,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/acl.go#L1873-L1909","documentation":"When an OIDC client assertion uses a KeyID (key_id), the kid JWT header is the only permitted KeyIDHeader. Any other header value (x5t, x5tS256, custom) fails validation with ErrInvalidKeyIDHeader. This enforces the JWT spec mapping: a raw key id maps to the `kid` header.","triggerScenarios":"Configuring an OIDC client assertion with key_id set while key_id_header is \"x5t\", \"x5tS256\", or any non-\"kid\" value.","commonSituations":"Copy-pasting a cert-based assertion config (which uses x5t) and only swapping in a key_id; misunderstanding the header/key correspondence in the IdP (e.g. Azure AD expects x5t for certs).","solutions":["Set key_id_header to \"kid\" (OIDCClientAssertionHeaderKid).","If your IdP requires x5t, provide cert material (pem_cert/pem_cert_file) instead of key_id.","Re-validate the provider config."],"exampleFix":"// before\nkey_id = \"my-key-id\"\nkey_id_header = \"x5t\"\n\n// after\nkey_id = \"my-key-id\"\nkey_id_header = \"kid\"","handlingStrategy":"validation","validationCode":"if k.KeyID != \"\" && k.KeyIDHeader != \"kid\" {\n    return fmt.Errorf(\"key_id requires key_id_header=kid, got %q\", k.KeyIDHeader)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pair key_id exclusively with the kid header.","Use cert material (pem_cert) when your IdP requires x5t.","Document header/key pairing in your config templates."],"tags":["nomad","acl","oidc","jwt","validation"],"backgroundTag":"invalid-jwt-header","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"}