{"record":{"id":"8a231253d6f67d2d","repo":"ory/hydra","slug":"a-secret-for-signing-hmac-sha512-256-is-expected-t","errorCode":null,"errorMessage":"a secret for signing HMAC-SHA512/256 is expected to be defined, but none were","messagePattern":"a secret for signing HMAC-SHA512/256 is expected to be defined, but none were","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"fosite/token/hmac/hmacsha.go","lineNumber":108,"sourceCode":"\n\tglobalSecret, err := c.Config.GetGlobalSecret(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif len(globalSecret) > 0 {\n\t\tkeys = append(keys, globalSecret)\n\t}\n\n\trotatedSecrets, err := c.Config.GetRotatedGlobalSecrets(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tkeys = append(keys, rotatedSecrets...)\n\n\tif len(keys) == 0 {\n\t\treturn errors.New(\"a secret for signing HMAC-SHA512/256 is expected to be defined, but none were\")\n\t}\n\n\tfor _, key := range keys {\n\t\tif err = c.validate(ctx, key, token); err == nil {\n\t\t\treturn nil\n\t\t} else if errors.Is(err, fosite.ErrTokenSignatureMismatch) {\n\t\t\t// Continue to the next key. The error will be returned if it is the last key.\n\t\t} else {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn err\n}\n\nfunc (c *HMACStrategy) validate(ctx context.Context, secret []byte, token string) error {\n\tif len(secret) < minimumSecretLength {\n\t\treturn errors.Errorf(\"secret for signing HMAC-SHA512/256 is expected to be 32 byte long, got %d byte\", len(secret))","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/ory/hydra/blob/4174065ffb052799890f7480f5360a877a67ffc1/fosite/token/hmac/hmacsha.go#L90-L126","documentation":"HMACSHA strategy Validate collects the global secret plus rotated secrets; if the resulting key list is empty there is no secret to verify HMAC-SHA512/256 signatures with, so it fails before attempting validation. This guards fosite's access token, refresh token, authorize code, and device code validation paths.","triggerScenarios":"Calling ValidateAccessToken / ValidateRefreshToken / ValidateAuthorizeCode / ValidateDeviceCode when the HMAC strategy's GlobalSecret is nil/empty AND no rotated secrets are provided — e.g. GlobalSecretProvider returns an empty byte slice.","commonSituations":"Fosite configured with an empty GlobalSecret (nil passed to hmacsha.NewHMACStrategy config); a custom GlobalSecretProvider that returns nil, nil; misconfigured secret provider returning empty slice.","solutions":["Configure a non-empty global secret (Hydra: secrets.system with 16+ chars) before constructing the HMAC strategy","Check your GlobalSecretProvider/GetGlobalSecret implementation returns at least one non-empty secret","Add rotated secrets via RotatedGlobalSecretsProvider if the primary secret is intentionally absent"],"exampleFix":"// before\nstrategy := hmacsha.NewHMACStrategy(&fosite.Config{GlobalSecret: []byte{}})\n// after\nstrategy := hmacsha.NewHMACStrategy(&fosite.Config{GlobalSecret: []byte(\"at-least-16-characters!\")})","handlingStrategy":"validation","validationCode":"if len(c.GlobalSecret) == 0 {\n    return errors.New(\"HMAC strategy requires a non-empty global secret (>=16 bytes)\")\n}","typeGuard":null,"tryCatchPattern":"if err := strategy.ValidateAccessToken(ctx, token); err != nil {\n    if strings.Contains(err.Error(), \"secret for signing HMAC\") {\n        // configuration error: abort startup, do not retry request\n    }\n}","preventionTips":["Wire a GetGlobalSecret that always returns 16+ random bytes","Fail fast at boot if secrets are missing rather than per-request","Generate secrets with `openssl rand -base64 32`"],"tags":["fosite","hmac","secrets","oauth2"],"backgroundTag":"missing-signing-secret","analyzedSha":"4174065ffb052799890f7480f5360a877a67ffc1","analyzedAt":"2026-09-03T14:52:41.581Z","contentChangedAt":"2026-09-03T14:52:41.581Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}