{"record":{"id":"2ac685b9e5cc68da","repo":"hashicorp/nomad","slug":"errinvalidkeyidheader","errorCode":"ErrInvalidKeyIDHeader","errorMessage":"%w; must be one of: \"x5t\", \"x5t#S256\"","messagePattern":"%w; must be one of: \"x5t\", \"x5t#S256\"","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/auth/oidc/client_assertion.go","lineNumber":205,"sourceCode":"}\n\n// hashKeyID derives a \"certificate thumbprint\" that the OIDC provider uses\n// to find the certificate to verify the private key JWT signature.\n// https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.7\nfunc hashKeyID(cert *x509.Certificate, header structs.OIDCClientAssertionKeyIDHeader) (string, error) {\n\tvar hasher hash.Hash\n\tswitch header {\n\tcase structs.OIDCClientAssertionHeaderX5t:\n\t\tif fips140.Enabled() {\n\t\t\treturn \"\", errors.New(\"x5t assertion headers use SHA-1, which is forbidden in FIPS-140 mode\")\n\t\t}\n\n\t\thasher = sha1.New()\n\tcase structs.OIDCClientAssertionHeaderX5tS256:\n\t\thasher = sha256.New()\n\tdefault:\n\t\t// this should be validated long before here, at upsert\n\t\treturn \"\", fmt.Errorf(`%w; must be one of: \"x5t\", \"x5t#S256\"`, structs.ErrInvalidKeyIDHeader)\n\t}\n\thasher.Write(cert.Raw)\n\thashed := hasher.Sum(nil)\n\treturn base64.RawURLEncoding.EncodeToString(hashed), nil\n}\n\n// newlineHeaders allows flexible copy-paste of a one-line key/cert PEM\n// by adding newlines around \"----BEGIN.*-----\" and\n// \"-----END.*(KEY|CERTIFICATE)-----\"\n// it's okay to have extra whitespace, but it's imperative that there be\n// at least one newline between the header/footer and the content.\nfunc newlineHeaders(bts []byte) []byte {\n\tcp := bytes.Clone(bts)\n\tcp = bytes.TrimSpace(cp)\n\tcp = bytes.ReplaceAll(cp, []byte(\"-----BEGIN\"), []byte(\"\\n-----BEGIN\"))\n\tcp = bytes.ReplaceAll(cp, []byte(\"-----END\"), []byte(\"\\n-----END\"))\n\t// key may be \"PRIVATE KEY\" or \"RSA PRIVATE KEY\", so just look for \"KEY\"\n\tcp = bytes.ReplaceAll(cp, []byte(\"KEY-----\"), []byte(\"KEY-----\\n\"))","sourceCodeStart":187,"sourceCodeEnd":223,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/lib/auth/oidc/client_assertion.go#L187-L223","documentation":"hashKeyID computes the key-ID header (x5t or x5t#S256) for the client-assertion JWT. It returns ErrInvalidKeyIDHeader wrapped when the configured header type is neither \"x5t\" nor \"x5t#S256\". The comment notes this is normally validated at upsert, so hitting it means an unvalidated value reached the builder.","triggerScenarios":"BuildClientAssertionJWT calls hashKeyID with an OIDC client assertion key-ID header value that is not \"x5t\" or \"x5t#S256\" — typically a value written directly to state/store bypassing upsert validation, or an API client typo.","commonSituations":"Hand-edited Nomad ACL/OIDC objects in state; older clients or tooling writing unsupported header names; schema/version drift after an upgrade where validation rules changed; typo like \"x5t-S256\".","solutions":["Set the key-ID header to exactly \"x5t\" or \"x5t#S256\" (re-upsert the OIDC client assertion config)","If stored state has a bad value, update it via the API/CLI so upsert validation runs again","Check for tooling or scripts writing the field directly to the store and route them through the validated API"],"exampleFix":"// before\n\"key_id_header\": \"x5t-S256\"\n// after\n\"key_id_header\": \"x5t#S256\"","handlingStrategy":"validation","validationCode":"var validKeyIDHeaders = map[string]bool{\"x5t\": true, \"x5t#S256\": true}\nif !validKeyIDHeaders[keyIDHeader] {\n\treturn fmt.Errorf(\"key_id_header %q invalid; must be x5t or x5t#S256\", keyIDHeader)\n}","typeGuard":"func isValidKeyIDHeader(h string) bool {\n\treturn h == \"x5t\" || h == \"x5t#S256\"\n}","tryCatchPattern":"// wrap and inspect via errors.Is\nif err != nil {\n\tif errors.Is(err, structs.ErrInvalidKeyIDHeader) {\n\t\t// fix config and re-upsert the OIDC client\n\t}\n\treturn err\n}","preventionTips":["Always set key-ID headers through the validated upsert API, not direct state writes","Whitelist \"x5t\" and \"x5t#S256\" in any tooling that writes this field","Watch for typo variants like \"x5t-S256\" in config templates","Add a config lint/CI check asserting allowed header values"],"tags":["oidc","jwt","validation","config"],"backgroundTag":"invalid-jwt-keyid-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"}