nats-io/nats-server · error

auth callout violation: auth callout response is not for exp

Error message

auth callout violation: auth callout response is not for expected user

What it means

This error is raised during authorization-callout processing when the auth callout service returns an authorization response whose Subject does not match the user id (public key) that initiated the request. The server treats a mismatched subject as a protocol/security violation and rejects the authentication. It ensures the callout service cannot authorize a different user than the one being checked.

Source

Thrown at server/auth_callout.go:127

			if err != nil {
				return nil, fmt.Errorf("error decrypting auth callout response on account %q: %v", account, err)
			}
			encrypted = true
		}

		cr, err := jwt.DecodeAuthorizationResponseClaims(string(msg))
		if err != nil {
			return nil, err
		}
		vr := jwt.CreateValidationResults()
		cr.Validate(vr)
		if len(vr.Issues) > 0 {
			return nil, fmt.Errorf("authorization response had validation errors: %v", vr.Issues[0])
		}

		// the subject is the user id
		if cr.Subject != pub {
			return nil, errors.New("auth callout violation: auth callout response is not for expected user")
		}

		// 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
			}

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Fix the auth callout service to set Subject in the AuthorizationResponse to the exact public key (user id) from the AuthorizationRequest
  2. Ensure the callout service does not cache or replay responses across different users
  3. Log the expected pub and returned cr.Subject on the callout service side to find the mismatch
  4. Verify the service decodes the request JWT with the correct key so it reads the right user id

Example fix

// before (callout service)
resp.Subject = requestUserAccount // wrong field
// after
resp.Subject = req.UserNonceClaim / decodedReq.Subject // the connecting user's public key
Defensive patterns

Strategy: try-catch

Validate before calling

// client side: ensure the callout service sets Subject to your public key
// server side: nothing to pre-validate; fix in callout service
if resp.Subject != expectedPubKey {
    return fmt.Errorf("callout subject mismatch: got %s want %s", resp.Subject, expectedPubKey)
}

Try / catch

user, err := s.lookupAccountAuthorization(...) // callout path
if err != nil && strings.Contains(err.Error(), "not for expected user") {
    log.Errorf("callout service returned wrong subject; check callout service config")
    return nil, ErrAuthorization
}

Prevention

When it happens

Trigger: A client connects and triggers the auth callout; the authorization response (AuthorizationResponse claims) contains a Subject field different from the connecting user's public key passed to the check.

Common situations: A misconfigured auth callout service echoing the wrong subject, reusing cached responses across users, signing responses for a different account/user, or a bug where the service derives Subject from the wrong request field.

Related errors


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