Skip to content

Add HTTP endpoint authentication for username/password#7918

Open
emamihe wants to merge 2 commits intonats-io:mainfrom
emamihe:feat/auth-http
Open

Add HTTP endpoint authentication for username/password#7918
emamihe wants to merge 2 commits intonats-io:mainfrom
emamihe:feat/auth-http

Conversation

@emamihe
Copy link
Copy Markdown

@emamihe emamihe commented Mar 7, 2026

Summary

Adds support for delegating username/password authentication to an external HTTP endpoint via the new auth_http configuration option. When clients connect with credentials, the server validates them by POSTing to the configured URL instead of checking against locally configured users.

The auth endpoint can optionally return permissions in its response to restrict which subjects each user can publish to and subscribe to. If omitted, the user gets full access.

This enables integration with existing identity providers, LDAP, OAuth backends, or custom auth services without requiring a NATS-aware auth service (unlike auth_callout which uses NATS subjects).

Configuration

Add the auth_http block inside your authorization section:

authorization {
  auth_http {
    url: "http://your-auth-service:8080/verify"
    timeout: 5
  }
}
  • url (required): HTTP endpoint to validate credentials (POST request)
  • timeout (optional): Request timeout in seconds (default: 5)

Request/Response Protocol

Request: The server POSTs JSON to the endpoint:

{"username": "...", "password": "..."}

Response:

  • 2xx = authentication success. The response body may optionally include permissions:

    {
    "permissions": {
    "publish": { "allow": ["foo.", "bar.>"], "deny": ["secret.>"] },
    "subscribe": { "allow": ["foo.
    ", "bar.>"], "deny": ["secret.>"] }
    }
    }

    Omit permissions or leave the body empty for full access.

  • 4xx/5xx = authentication failure.

Changes

  • Config: New auth_http block in authorization with url and optional timeout (default 5s)
  • Protocol: Server POSTs credentials; on 2xx, optionally parses permissions from response body
  • Auth flow: When auth_http is configured and a client sends username/password, credentials are validated via HTTP; on success the client is registered with the returned permissions (or full access if none)
  • Validation: auth_http and auth_callout are mutually exclusive; auth_http is not supported for cluster/gateway authorization

Files changed

  • server/opts.go – AuthHTTP struct, config parsing
  • server/auth.go – HTTP auth check logic, permissions parsing, configureAuthorization
  • server/reload.go – Reload support for AuthHTTP
  • server/opts_test.go – TestAuthHTTPConfig
  • server/auth_test.go – TestAuthHTTPWithPermissions (integration test)
  • README.md – Documentation and examples

Testing

  • TestAuthHTTPConfig – verifies config parsing
  • TestAuthHTTPWithPermissions – integration test with mock auth endpoint returning permissions; verifies publish/subscribe to allowed subjects succeeds and to denied subjects fails

Signed-off-by: Hamid emami.he@gmail.com

@emamihe emamihe requested review from a team as code owners March 7, 2026 14:13
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7d0982e97d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

server/auth.go Outdated
Comment on lines +1650 to +1651
// 2xx status codes indicate successful authentication
return resp.StatusCode >= 200 && resp.StatusCode < 300
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject redirected auth_http responses

checkAuthHTTP treats any final 2xx as success, but http.Client follows redirects by default, so a failed auth flow that returns 302 (common for login endpoints) and lands on a 200 page will be accepted as valid credentials. This can let invalid usernames/passwords authenticate whenever the configured endpoint signals failure via redirects instead of direct 4xx/5xx responses.

Useful? React with 👍 / 👎.

Comment on lines +4721 to +4722
if _, err := url.Parse(ah.URL); err != nil {
return nil, &configErr{tk, fmt.Sprintf("auth_http url is not valid: %v", err)}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require an absolute URL for auth_http

The parser only checks url.Parse, which accepts relative or hostless values (for example /verify or auth-service:8080/verify) as “valid”. Those configs load successfully, but runtime auth calls then fail with request errors (unsupported protocol scheme / missing host), causing all username/password logins to fail until reconfigured.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3bba00a9d7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +287 to +288
} else if opts.AuthHTTP != nil {
s.info.AuthRequired = true
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Clear stale local credentials when auth_http is configured

When configureAuthorization takes the new opts.AuthHTTP != nil branch, it marks auth as required but does not reset s.users/s.nkeys. After a config reload from static users/nkeys to auth_http-only, those old in-memory credentials remain populated and are evaluated before checkAuthHTTP in processClientOrLeafAuthentication, so previously valid local usernames can still authenticate and bypass the external HTTP verifier until restart.

Useful? React with 👍 / 👎.

@MauriceVanVeen
Copy link
Copy Markdown
Member

Delegating auth to an external auth provider is already supported through auth callout, and that is intended as the extension point. Wouldn't it be possible to write an auth callout service that interfaces with the auth service you need? This seems like reimplementing auth callout, but then making it HTTP specific too.

@emamihe
Copy link
Copy Markdown
Author

emamihe commented Mar 7, 2026

Thanks for your attention @MauriceVanVeen .
I have actually used the auth_callout feature before in one of my projects where NATS was acting as an edge service and clients were connecting to it with least-privileged roles while authentication was delegated to an external service.
While this worked well functionally, I ran into an operational issue caused by the dependency chain. The auth extension itself depended on NATS, so when NATS went down, the extension would enter a retry backoff while trying to reconnect. Even after NATS recovered, clients were temporarily unable to authenticate because the extension was still in its backoff window. I eventually mitigated this behavior in the extension service, but the experience highlighted the tight coupling between authentication and the NATS transport.
The motivation behind proposing native HTTP-based authentication is to provide a simpler and more decoupled integration model. An HTTP auth service can operate completely independently from NATS and avoids circular dependencies in failure scenarios.
The idea was also inspired by similar capabilities in some MQTT brokers where HTTP-based authentication is commonly used as a straightforward extension point.
My intention with this proposal is not to replace auth_callout, but rather to offer HTTP as an additional option for environments where a lightweight and transport-independent integration might be preferable.

@MauriceVanVeen
Copy link
Copy Markdown
Member

In that case it would be better to open an issue and discuss the problem there first, instead of raising a PR with a specific solution without knowing it can or will be accepted. See also: https://github.com/nats-io/nats-server/blob/main/CONTRIBUTING.md#contributing-changes

For example, why did your NATS setup go down? That sounds like the primary problem here that needs to be looked into, not whether auth callout is used or not. And if the backoff was too long, couldn't that be decreased? Etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants