ory/hydra · critical

a secret for signing HMAC-SHA512/256 is expected to be defin

Error message

a secret for signing HMAC-SHA512/256 is expected to be defined, but none were

What it means

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.

Source

Thrown at fosite/token/hmac/hmacsha.go:108

	globalSecret, err := c.Config.GetGlobalSecret(ctx)
	if err != nil {
		return err
	}

	if len(globalSecret) > 0 {
		keys = append(keys, globalSecret)
	}

	rotatedSecrets, err := c.Config.GetRotatedGlobalSecrets(ctx)
	if err != nil {
		return err
	}

	keys = append(keys, rotatedSecrets...)

	if len(keys) == 0 {
		return errors.New("a secret for signing HMAC-SHA512/256 is expected to be defined, but none were")
	}

	for _, key := range keys {
		if err = c.validate(ctx, key, token); err == nil {
			return nil
		} else if errors.Is(err, fosite.ErrTokenSignatureMismatch) {
			// Continue to the next key. The error will be returned if it is the last key.
		} else {
			return err
		}
	}

	return err
}

func (c *HMACStrategy) validate(ctx context.Context, secret []byte, token string) error {
	if len(secret) < minimumSecretLength {
		return errors.Errorf("secret for signing HMAC-SHA512/256 is expected to be 32 byte long, got %d byte", len(secret))

View on GitHub (pinned to 4174065ffb)

Solutions

  1. Configure a non-empty global secret (Hydra: secrets.system with 16+ chars) before constructing the HMAC strategy
  2. Check your GlobalSecretProvider/GetGlobalSecret implementation returns at least one non-empty secret
  3. Add rotated secrets via RotatedGlobalSecretsProvider if the primary secret is intentionally absent

Example fix

// before
strategy := hmacsha.NewHMACStrategy(&fosite.Config{GlobalSecret: []byte{}})
// after
strategy := hmacsha.NewHMACStrategy(&fosite.Config{GlobalSecret: []byte("at-least-16-characters!")})
Defensive patterns

Strategy: validation

Validate before calling

if len(c.GlobalSecret) == 0 {
    return errors.New("HMAC strategy requires a non-empty global secret (>=16 bytes)")
}

Try / catch

if err := strategy.ValidateAccessToken(ctx, token); err != nil {
    if strings.Contains(err.Error(), "secret for signing HMAC") {
        // configuration error: abort startup, do not retry request
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03). Data as JSON: /api/errors/8a231253d6f67d2d. Report an issue: GitHub.