nats-io/nats-server · error

auth callout signing key is unknown

Error message

auth callout signing key is unknown

What it means

This error occurs when the auth callout response is signed by a key (Issuer, or IssuerAccount if set) that is not the expected account and is not a known signing key of that account. The server verifies that whoever signed the authorization response is authorized (via account issuer keys) to do so; an unknown signer is a security violation.

Source

Thrown at server/auth_callout.go:148

		// check the audience to be the server ID
		if cr.Audience != s.info.ID {
			return nil, errors.New("auth callout violation: auth callout response is not for server")
		}

		// check if had an error message from the auth account
		if cr.Error != _EMPTY_ {
			return nil, fmt.Errorf("auth callout service returned an error: %v", cr.Error)
		}

		// if response is encrypted none of this is needed
		if isOperatorMode && !encrypted {
			pkStr := cr.Issuer
			if cr.IssuerAccount != _EMPTY_ {
				pkStr = cr.IssuerAccount
			}
			if pkStr != account {
				if _, ok := acc.hasIssuer(pkStr); !ok {
					return nil, errors.New("auth callout signing key is unknown")
				}
			}
		}

		return jwt.DecodeUserClaims(cr.Jwt)
	}

	// getIssuerAccount returns the issuer (as per JWT) - it also asserts that
	// only in operator mode we expect to receive `issuer_account`.
	getIssuerAccount := func(arc *jwt.UserClaims, account string) (string, error) {
		// Make sure correct issuer.
		var issuer string
		if opts.AuthCallout != nil {
			issuer = opts.AuthCallout.Issuer
		} else {
			// Operator mode is who we send the request on unless switching accounts.
			issuer = acc.Name
		}

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Add the callout service's signing public key to the account's trusted signing/issuer keys (nsc edit account --signing-key or equivalent)
  2. Ensure cr.IssuerAccount, when set, matches an account that is a valid issuer for the target account
  3. Re-sign the authorization response with a key already registered on the account
  4. Push the updated account JWT to the server after adding the issuer key

Example fix

// before
nsc add operator -n ops   // service signs with a key unknown to account
// after
nsc edit account AUTH --signing-key <callout-service-public-key>
nsc push -A
Defensive patterns

Strategy: validation

Validate before calling

// before deploying: verify the signing key is a registered account signing key
acctJWT, _ := fetchAccountJWT(accountPub)
claims, _ := jwt.DecodeAccountClaims(acctJWT)
found := false
for k := range claims.SigningKeys {
    if k == calloutSigningPubKey {
        found = true
    }
}
if !found {
    return fmt.Errorf("callout signing key %s not registered on account", calloutSigningPubKey)
}

Try / catch

user, err := s.lookupAccountAuthorization(...)
if err != nil && strings.Contains(err.Error(), "signing key is unknown") {
    log.Errorf("callout response signed by unregistered key %s", cr.Issuer)
    return nil, ErrAuthorization
}

Prevention

When it happens

Trigger: The AuthorizationResponse carries an Issuer (or IssuerAccount) public key that differs from the target account and is not present in the account's issuer keys (acc.hasIssuer fails).

Common situations: Rotating or adding auth callout signing keys without updating the account's issuer list, using a separate signing key not registered via nsc, or pointing the callout at the wrong account.

Related errors


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/15f35b034287aeae. Report an issue: GitHub.