ory/hydra · critical
secret for signing HMAC-SHA512/256 is expected to be 32 byte
Error message
secret for signing HMAC-SHA512/256 is expected to be 32 byte long, got %d byte
What it means
HMACStrategy.Generate refuses to sign tokens when the configured global secret is shorter than the 32-byte minimum required for HMAC-SHA512/256 (the signing key is a 32-byte array). This is a server configuration problem: the secret in the environment/config is too short (or empty).
Source
Thrown at fosite/token/hmac/hmacsha.go:57
minimumEntropy = 32
minimumSecretLength = 32
)
var b64 = base64.URLEncoding.WithPadding(base64.NoPadding)
// Generate generates a token and a matching signature or returns an error.
// This method implements rfc6819 Section 5.1.4.2.2: Use High Entropy for Secrets.
func (c *HMACStrategy) Generate(ctx context.Context) (string, string, error) {
c.Lock()
defer c.Unlock()
globalSecret, err := c.Config.GetGlobalSecret(ctx)
if err != nil {
return "", "", err
}
if len(globalSecret) < minimumSecretLength {
return "", "", errors.Errorf("secret for signing HMAC-SHA512/256 is expected to be 32 byte long, got %d byte", len(globalSecret))
}
var signingKey [32]byte
copy(signingKey[:], globalSecret)
entropy := c.Config.GetTokenEntropy(ctx)
if entropy < minimumEntropy {
entropy = minimumEntropy
}
// When creating tokens not intended for usage by human users (e.g.,
// client secrets or token handles), the authorization server should
// include a reasonable level of entropy in order to mitigate the risk
// of guessing attacks. The token value should be >=128 bits long and
// constructed from a cryptographically strong random or pseudo-random
// number sequence (see [RFC4086] for best current practice) generated
// by the authorization server.
tokenKey, err := RandomBytes(entropy)View on GitHub (pinned to 4174065ffb)
Solutions
- Set a global secret of at least 32 bytes (e.g. generate with: export OAUTH2_SHARED_SECRET=$(openssl rand -hex 32) or head -c 32 /dev/urandom | base64 depending on encoding)
- Restart/redeploy the service so the new secret is loaded
- Check for encoding pitfalls: if the value is hex/base64 encoded, decode before measuring or supply 32 raw bytes
- Ensure all replicas share the same long secret, otherwise token validation will break next
Example fix
// before export OAUTH2_SHARED_SECRET="supersecret" // after export OAUTH2_SHARED_SECRET="$(openssl rand -hex 32)" # 64 hex chars = 32 bytes
Defensive patterns
Strategy: validation
Validate before calling
secret, _ := cfg.GetGlobalSecret(context.Background())
if len(secret) < 32 {
log.Fatalf("global secret too short: %d bytes (need >= 32)", len(secret))
} Try / catch
_, err := tokens.GenerateAccessToken(ctx, req)
if err != nil && strings.Contains(err.Error(), "expected to be 32 byte long") {
log.Fatalf("misconfigured global secret: %v", err)
} Prevention
- Generate secrets with openssl rand -hex 32 at deploy time, never hardcode short dev secrets
- Add a startup assertion that the shared secret is >= 32 bytes
- Keep the same secret (or a rotation list of >=32-byte secrets) across all replicas and generate/validate paths
- Document the 32-byte requirement in your deployment checklist
When it happens
Trigger: Generating any HMAC-based artifact (access token, refresh token, authorize code, device code) via GenerateAccessToken/GenerateRefreshToken/GenerateAuthorizeCode/GenerateDeviceCode when GetGlobalSecret(ctx) returns fewer than 32 bytes.
Common situations: Dev/test secrets like 'some-secret' left in production config; missing or empty OAUTH2_SHARED_SECRET / COOKIE_SECRET env var; docker-compose examples with short secrets not replaced during deployment.
Related errors
- a secret for signing HMAC-SHA512/256 is expected to be defin
- failed to set token lifespans due to failed client type asse
- Session must be of type JWTSessionContainer but got type: %T
- GetTokenClaims() must not be nil
- unsupported auth type %q
AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03).
Data as JSON: /api/errors/b87e9da3209994d5.
Report an issue: GitHub.