Repository: nats-io/nats-server
Pull request #2699 (2021-11-18 12:49:21)

expose the nonce to custom authentication
Signed-off-by: R.I.Pienaar <rip@devco.net>

/cc @nats-io/core


ripienaar: > LGTM, but we have a test called `TestCustomClientAuthentication` that you could extend/copy form to actually verify that a nonce is specified?

OK, added a bit to that test

Patch:
diff --git a/scripts/cov.sh b/scripts/cov.sh
index 48451972..8a0af1b1 100755
--- a/scripts/cov.sh
+++ b/scripts/cov.sh
@@ -8,10 +8,10 @@ go get github.com/wadey/gocovmerge
 
 rm -rf ./cov
 mkdir cov
-go test -v -failfast -covermode=atomic -coverprofile=./cov/conf.out -run='Test[^NoRace]' ./conf -timeout=20m
-go test -v -failfast -covermode=atomic -coverprofile=./cov/log.out -run='Test[^NoRace]' ./logger -timeout=20m
-go test -v -failfast -covermode=atomic -coverprofile=./cov/server.out -run='Test[^NoRace]' ./server -timeout=20m
-go test -v -failfast -covermode=atomic -coverprofile=./cov/test.out -coverpkg=./server -run='Test[^NoRace]' ./test -timeout=20m
+go test -v -failfast -covermode=atomic -coverprofile=./cov/conf.out ./conf -timeout=20m
+go test -v -failfast -covermode=atomic -coverprofile=./cov/log.out ./logger -timeout=20m
+go test -v -failfast -covermode=atomic -coverprofile=./cov/server.out ./server -timeout=20m
+go test -v -failfast -covermode=atomic -coverprofile=./cov/test.out -coverpkg=./server ./test -timeout=20m
 gocovmerge ./cov/*.out > acc.out
 rm -rf ./cov
 
diff --git a/server/auth.go b/server/auth.go
index 3ee51576..5f5c9298 100644
--- a/server/auth.go
+++ b/server/auth.go
@@ -42,14 +42,16 @@ type Authentication interface {
 
 // ClientAuthentication is an interface for client authentication
 type ClientAuthentication interface {
-	// Get options associated with a client
+	// GetOpts gets options associated with a client
 	GetOpts() *ClientOpts
-	// If TLS is enabled, TLS ConnectionState, nil otherwise
+	// GetTLSConnectionState if TLS is enabled, TLS ConnectionState, nil otherwise
 	GetTLSConnectionState() *tls.ConnectionState
-	// Optionally map a user after auth.
+	// RegisterUser optionally map a user after auth.
 	RegisterUser(*User)
 	// RemoteAddress expose the connection information of the client
 	RemoteAddress() net.Addr
+	// GetNonce is the nonce presented to the user in the INFO line
+	GetNonce() []byte
 	// Kind indicates what type of connection this is matching defined constants like CLIENT, ROUTER, GATEWAY, LEAF etc
 	Kind() int
 }
diff --git a/server/client.go b/server/client.go
index 68a26a9a..11fe8759 100644
--- a/server/client.go
+++ b/server/client.go
@@ -427,6 +427,14 @@ func (c *client) String() (id string) {
 	return _EMPTY_
 }
 
+// GetNonce returns the nonce that was presented to the user on connection
+func (c *client) GetNonce() []byte {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+
+	return c.nonce
+}
+
 // GetName returns the application supplied name for the connection.
 func (c *client) GetName() string {
 	c.mu.Lock()


Test patch:
diff --git a/server/server_test.go b/server/server_test.go
index 3ed4b2ad..70756e82 100644
--- a/server/server_test.go
+++ b/server/server_test.go
@@ -603,32 +603,46 @@ func TestNilMonitoringPort(t *testing.T) {
 	}
 }
 
-type DummyAuth struct{}
+type DummyAuth struct {
+	t         *testing.T
+	needNonce bool
+}
 
 func (d *DummyAuth) Check(c ClientAuthentication) bool {
+	if d.needNonce && len(c.GetNonce()) == 0 {
+		d.t.Fatalf("Expected a nonce but received none")
+	} else if !d.needNonce && len(c.GetNonce()) > 0 {
+		d.t.Fatalf("Received a nonce when none was expected")
+	}
+
 	return c.GetOpts().Username == "valid"
 }
 
 func TestCustomClientAuthentication(t *testing.T) {
-	var clientAuth DummyAuth
-
-	opts := DefaultOptions()
-	opts.CustomClientAuthentication = &clientAuth
+	testAuth := func(t *testing.T, nonce bool) {
+		clientAuth := &DummyAuth{t, nonce}
 
-	s := RunServer(opts)
+		opts := DefaultOptions()
+		opts.CustomClientAuthentication = clientAuth
+		opts.AlwaysEnableNonce = nonce
 
-	defer s.Shutdown()
+		s := RunServer(opts)
+		defer s.Shutdown()
 
-	addr := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port)
+		addr := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port)
 
-	nc, err := nats.Connect(addr, nats.UserInfo("valid", ""))
-	if err != nil {
-		t.Fatalf("Expected client to connect, got: %s", err)
-	}
-	nc.Close()
-	if _, err := nats.Connect(addr, nats.UserInfo("invalid", "")); err == nil {
-		t.Fatal("Expected client to fail to connect")
+		nc, err := nats.Connect(addr, nats.UserInfo("valid", ""))
+		if err != nil {
+			t.Fatalf("Expected client to connect, got: %s", err)
+		}
+		nc.Close()
+		if _, err := nats.Connect(addr, nats.UserInfo("invalid", "")); err == nil {
+			t.Fatal("Expected client to fail to connect")
+		}
 	}
+
+	t.Run("with nonce", func(t *testing.T) { testAuth(t, true) })
+	t.Run("without nonce", func(t *testing.T) { testAuth(t, false) })
 }
 
 func TestCustomRouterAuthentication(t *testing.T) {