package busauth import ( "encoding/base64" server "github.com/nats-io/nats-server/v2/server" "github.com/nats-io/nkeys" ) // nkeyAuthenticator is a NATS server.Authentication that authorizes a client by // verifying the nkey signature over the server-presented nonce and then // consulting the bus user allowlist. Authorization is checked on every new // connection via the injected predicate (not a static Options.Nkeys map), so // revoking a user denies its next connection without restarting the server. type nkeyAuthenticator struct { // isAuthorized reports whether the lowercase-hex Ed25519 public key behind an // nkey belongs to an active bus user. Injected (membership.Store.IsAuthorized) // so this package stays free of the store dependency. isAuthorized func(signPubHex string) bool } // NewNkeyAuthenticator builds a NATS custom authenticator backed by isAuthorized. // Pass it to embeddednats so the data plane only accepts registered identities. func NewNkeyAuthenticator(isAuthorized func(signPubHex string) bool) server.Authentication { return &nkeyAuthenticator{isAuthorized: isAuthorized} } // Check verifies the client's nkey signature against the nonce the server // presented, then maps the nkey to its allowlist key and checks authorization. // Any malformed input or failed verification yields false (fail closed). The // signature decoding mirrors nats-server's own (raw-url base64, then std base64 // fallback) so genuine clients using nats.Nkey are accepted unchanged. func (a *nkeyAuthenticator) Check(c server.ClientAuthentication) bool { opts := c.GetOpts() if opts.Nkey == "" { return false } sig, err := base64.RawURLEncoding.DecodeString(opts.Sig) if err != nil { sig, err = base64.StdEncoding.DecodeString(opts.Sig) if err != nil { return false } } pub, err := nkeys.FromPublicKey(opts.Nkey) if err != nil { return false } if err := pub.Verify(c.GetNonce(), sig); err != nil { return false } signPubHex, err := SignPubHexFromNkey(opts.Nkey) if err != nil { return false } return a.isAuthorized(signPubHex) }