Repository: nats-io/nats-server
Pull request #1845 (2021-01-26 18:41:58)

[added] support for StrictSigningKeyUsage and updated jwt library
This will cause the server to not trust accounts/user signed by an
identity key

Signed-off-by: Matthias Hanel <mh@synadia.com>

To be sure I changed using len( s.trustedKeys) > 0 to a check for not nil.
To support this I moved all keys trusted into signingKeys.



Patch:
diff --git a/go.mod b/go.mod
index 20a9eee8..ba5dcfad 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.15
 require (
 	github.com/klauspost/compress v1.11.7
 	github.com/minio/highwayhash v1.0.0
-	github.com/nats-io/jwt/v2 v2.0.0-20210107222814-18c5cc45d263
+	github.com/nats-io/jwt/v2 v2.0.0-20210125223648-1c24d462becc
 	github.com/nats-io/nats.go v1.10.1-0.20210122204956-b8ea7fc17ea6
 	github.com/nats-io/nkeys v0.2.0
 	github.com/nats-io/nuid v1.0.1
diff --git a/go.sum b/go.sum
index 496a8bcc..c9554443 100644
--- a/go.sum
+++ b/go.sum
@@ -3,49 +3,30 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x
 github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
 github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
 github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
-github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/klauspost/compress v1.11.4 h1:kz40R/YWls3iqT9zX9AHN3WoVsrAWVyui5sxuLqiXqU=
-github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 github.com/klauspost/compress v1.11.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg=
 github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 github.com/minio/highwayhash v1.0.0 h1:iMSDhgUILCr0TNm8LWlSjF8N0ZIj2qbO8WHp6Q/J2BA=
 github.com/minio/highwayhash v1.0.0/go.mod h1:xQboMTeM9nY9v/LlAOxFctujiv5+Aq2hR5dxBpaMbdc=
 github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
-github.com/nats-io/jwt v0.3.3-0.20200519195258-f2bf5ce574c7 h1:RnGotxlghqR5D2KDAu4TyuLqyjuylOsJiAFhXvMvQIc=
 github.com/nats-io/jwt v0.3.3-0.20200519195258-f2bf5ce574c7/go.mod h1:n3cvmLfBfnpV4JJRN7lRYCyZnw48ksGsbThGXEk4w9M=
 github.com/nats-io/jwt v1.1.0 h1:+vOlgtM0ZsF46GbmUoadq0/2rChNS45gtxHEa3H1gqM=
 github.com/nats-io/jwt v1.1.0/go.mod h1:n3cvmLfBfnpV4JJRN7lRYCyZnw48ksGsbThGXEk4w9M=
-github.com/nats-io/jwt v1.2.3-0.20210107222814-18c5cc45d263 h1:x3J+0KMQhbQE8iHxChJdJHNk7rJnCwXFX+WI0hfvYtE=
-github.com/nats-io/jwt v1.2.3-0.20210107222814-18c5cc45d263/go.mod h1:/xX356yQA6LuXI9xWW7mZNpxgF2mBmGecH+Fj34sP5Q=
 github.com/nats-io/jwt/v2 v2.0.0-20200916203241-1f8ce17dff02/go.mod h1:vs+ZEjP+XKy8szkBmQwCB7RjYdIlMaPsFPs4VdS4bTQ=
 github.com/nats-io/jwt/v2 v2.0.0-20201015190852-e11ce317263c/go.mod h1:vs+ZEjP+XKy8szkBmQwCB7RjYdIlMaPsFPs4VdS4bTQ=
-github.com/nats-io/jwt/v2 v2.0.0-20210107222814-18c5cc45d263 h1:M2bkT/arzYFYPtRqQWN2m+LSLfcqFmPxlxvTxdB4aVE=
-github.com/nats-io/jwt/v2 v2.0.0-20210107222814-18c5cc45d263/go.mod h1:PuO5FToRL31ecdFqVjc794vK0Bj0CwzveQEDvkb7MoQ=
+github.com/nats-io/jwt/v2 v2.0.0-20210125223648-1c24d462becc h1:pu+s4XC+bYnI0iD2vDtOl83zjCYUau/q6c83pEvsGZc=
+github.com/nats-io/jwt/v2 v2.0.0-20210125223648-1c24d462becc/go.mod h1:PuO5FToRL31ecdFqVjc794vK0Bj0CwzveQEDvkb7MoQ=
 github.com/nats-io/nats-server/v2 v2.1.8-0.20200524125952-51ebd92a9093/go.mod h1:rQnBf2Rv4P9adtAs/Ti6LfFmVtFG6HLhl/H7cVshcJU=
 github.com/nats-io/nats-server/v2 v2.1.8-0.20200601203034-f8d6dd992b71/go.mod h1:Nan/1L5Sa1JRW+Thm4HNYcIDcVRFc5zK9OpSZeI2kk4=
 github.com/nats-io/nats-server/v2 v2.1.8-0.20200929001935-7f44d075f7ad/go.mod h1:TkHpUIDETmTI7mrHN40D1pzxfzHZuGmtMbtb83TGVQw=
 github.com/nats-io/nats-server/v2 v2.1.8-0.20201129161730-ebe63db3e3ed/go.mod h1:XD0zHR/jTXdZvWaQfS5mQgsXj6x12kMjKLyAk/cOGgY=
 github.com/nats-io/nats.go v1.10.0/go.mod h1:AjGArbfyR50+afOUotNX2Xs5SYHf+CoOa5HH1eEl2HE=
 github.com/nats-io/nats.go v1.10.1-0.20200531124210-96f2130e4d55/go.mod h1:ARiFsjW9DVxk48WJbO3OSZ2DG8fjkMi7ecLmXoY/n9I=
-github.com/nats-io/nats.go v1.10.1-0.20200606002146-fc6fed82929a h1:gzSKZOBlu/DpbuPbt34paXCOvA6+E+lVfU2BmomQ9HA=
 github.com/nats-io/nats.go v1.10.1-0.20200606002146-fc6fed82929a/go.mod h1:8eAIv96Mo9QW6Or40jUHejS7e4VwZ3VRYD6Sf0BTDp4=
-github.com/nats-io/nats.go v1.10.1-0.20201021145452-94be476ad6e0 h1:s//xxvMgNRVJNTRXTRNvLlES0zvJoDLXfcmbK0YgN0E=
 github.com/nats-io/nats.go v1.10.1-0.20201021145452-94be476ad6e0/go.mod h1:VU2zERjp8xmF+Lw2NH4u2t5qWZxwc7jB3+7HVMWQXPI=
-github.com/nats-io/nats.go v1.10.1-0.20201218032324-b4450fb04511 h1:VMkjinILzhvMPbG+MwRC9q5IElHdsOaB5aHIiCT8vd4=
-github.com/nats-io/nats.go v1.10.1-0.20201218032324-b4450fb04511/go.mod h1:Sa3kLIonafChP5IF0b55i9uvGR10I3hPETFbi4+9kOI=
-github.com/nats-io/nats.go v1.10.1-0.20210113000055-be60b8378d4f h1:HjROEVuamG+e0lCwjKs3yfTzDbbaogriW4HQQ2OQcr0=
-github.com/nats-io/nats.go v1.10.1-0.20210113000055-be60b8378d4f/go.mod h1:Sa3kLIonafChP5IF0b55i9uvGR10I3hPETFbi4+9kOI=
-github.com/nats-io/nats.go v1.10.1-0.20210113025029-acca6d69b4e3 h1:M7a1IwTIUnZ/ucEz+/oyOoQ11CJ/sDfybZYjdGMGjdE=
-github.com/nats-io/nats.go v1.10.1-0.20210113025029-acca6d69b4e3/go.mod h1:Sa3kLIonafChP5IF0b55i9uvGR10I3hPETFbi4+9kOI=
-github.com/nats-io/nats.go v1.10.1-0.20210114001154-0a6b5f686ab3 h1:2uYi4zZJ6zni7jwix8l1bA/srT3hjFwGN2iUvx0CWpA=
-github.com/nats-io/nats.go v1.10.1-0.20210114001154-0a6b5f686ab3/go.mod h1:Sa3kLIonafChP5IF0b55i9uvGR10I3hPETFbi4+9kOI=
-github.com/nats-io/nats.go v1.10.1-0.20210115180731-7fb8bacca613 h1:Zio4IMHHsFjtTeksjF4PySxFNcKwSH9urNIiIW7A/FQ=
-github.com/nats-io/nats.go v1.10.1-0.20210115180731-7fb8bacca613/go.mod h1:Sa3kLIonafChP5IF0b55i9uvGR10I3hPETFbi4+9kOI=
 github.com/nats-io/nats.go v1.10.1-0.20210122204956-b8ea7fc17ea6 h1:cpS+9uyfHXvRG/Q+WcDd3KXRgPa9fo9tDbIeDHCxYAg=
 github.com/nats-io/nats.go v1.10.1-0.20210122204956-b8ea7fc17ea6/go.mod h1:Sa3kLIonafChP5IF0b55i9uvGR10I3hPETFbi4+9kOI=
 github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
@@ -56,7 +37,6 @@ github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
 github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM=
 golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
 golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -64,19 +44,16 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
 golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU=
 golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
 golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
 google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
 google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
diff --git a/server/accounts.go b/server/accounts.go
index 388580b9..73251227 100644
--- a/server/accounts.go
+++ b/server/accounts.go
@@ -2746,10 +2746,6 @@ func (a *Account) hasIssuer(issuer string) (jwt.Scope, bool) {
 
 // hasIssuerNoLock is the unlocked version of hasIssuer
 func (a *Account) hasIssuerNoLock(issuer string) (jwt.Scope, bool) {
-	// same issuer -- keep this for safety on the calling code
-	if a.Name == issuer {
-		return nil, true
-	}
 	scope, ok := a.signingKeys[issuer]
 	return scope, ok
 }
@@ -2844,13 +2840,17 @@ func (s *Server) updateAccountClaimsWithRefresh(a *Account, ac *jwt.AccountClaim
 
 	// update account signing keys
 	a.signingKeys = nil
-	if len(ac.SigningKeys) > 0 {
-		a.signingKeys = make(map[string]jwt.Scope, len(ac.SigningKeys))
+	_, strict := s.strictSigningKeyUsage[a.Issuer]
+	if len(ac.SigningKeys) > 0 || !strict {
+		a.signingKeys = make(map[string]jwt.Scope)
 	}
 	signersChanged := false
 	for k, scope := range ac.SigningKeys {
 		a.signingKeys[k] = scope
 	}
+	if !strict {
+		a.signingKeys[a.Name] = nil
+	}
 	if len(a.signingKeys) != len(old.signingKeys) {
 		signersChanged = true
 	}
@@ -3150,6 +3150,10 @@ func (s *Server) updateAccountClaimsWithRefresh(a *Account, ac *jwt.AccountClaim
 	if signersChanged {
 		for _, c := range clients {
 			c.mu.Lock()
+			if c.user == nil {
+				c.mu.Unlock()
+				continue
+			}
 			sk := c.user.SigningKey
 			c.mu.Unlock()
 			if sk == _EMPTY_ {
@@ -3506,19 +3510,21 @@ func handleDeleteRequest(store *DirJWTStore, s *Server, msg []byte, reply string
 	}
 }
 
-func getOperator(s *Server) (string, error) {
+func getOperator(s *Server) (string, bool, error) {
 	var op string
+	var strict bool
 	if opts := s.getOpts(); opts != nil && len(opts.TrustedOperators) > 0 {
 		op = opts.TrustedOperators[0].Subject
+		strict = opts.TrustedOperators[0].StrictSigningKeyUsage
 	}
 	if op == "" {
-		return "", fmt.Errorf("no operator found")
+		return "", false, fmt.Errorf("no operator found")
 	}
-	return op, nil
+	return op, strict, nil
 }
 
 func (dr *DirAccResolver) Start(s *Server) error {
-	op, err := getOperator(s)
+	op, strict, err := getOperator(s)
 	if err != nil {
 		return err
 	}
@@ -3553,6 +3559,9 @@ func (dr *DirAccResolver) Start(s *Server) error {
 			} else if claim.Subject != pubKey {
 				err := errors.New("subject does not match jwt content")
 				respondToUpdate(s, resp, pubKey, "jwt update resulted in error", err)
+			} else if claim.Issuer == op && strict {
+				err := errors.New("operator requires issuer to be a signing key")
+				respondToUpdate(s, resp, pubKey, "jwt update resulted in error", err)
 			} else if err := dr.save(pubKey, string(msg)); err != nil {
 				respondToUpdate(s, resp, pubKey, "jwt update resulted in error", err)
 			} else {
@@ -3565,6 +3574,9 @@ func (dr *DirAccResolver) Start(s *Server) error {
 	if _, err := s.sysSubscribe(accClaimsReqSubj, func(_ *subscription, _ *client, subj, resp string, msg []byte) {
 		if claim, err := jwt.DecodeAccountClaims(string(msg)); err != nil {
 			respondToUpdate(s, resp, "n/a", "jwt update resulted in error", err)
+		} else if claim.Issuer == op && strict {
+			err := errors.New("operator requires issuer to be a signing key")
+			respondToUpdate(s, resp, claim.Subject, "jwt update resulted in error", err)
 		} else if err := dr.save(claim.Subject, string(msg)); err != nil {
 			respondToUpdate(s, resp, claim.Subject, "jwt update resulted in error", err)
 		} else {
@@ -3760,7 +3772,7 @@ func NewCacheDirAccResolver(path string, limit int64, ttl time.Duration, _ ...di
 }
 
 func (dr *CacheDirAccResolver) Start(s *Server) error {
-	op, err := getOperator(s)
+	op, strict, err := getOperator(s)
 	if err != nil {
 		return err
 	}
@@ -3794,6 +3806,9 @@ func (dr *CacheDirAccResolver) Start(s *Server) error {
 			} else if claim.Subject != pubKey {
 				err := errors.New("subject does not match jwt content")
 				respondToUpdate(s, resp, pubKey, "jwt update cache resulted in error", err)
+			} else if claim.Issuer == op && strict {
+				err := errors.New("operator requires issuer to be a signing key")
+				respondToUpdate(s, resp, pubKey, "jwt update cache resulted in error", err)
 			} else if _, ok := s.accounts.Load(pubKey); !ok {
 				respondToUpdate(s, resp, pubKey, "jwt update cache skipped", nil)
 			} else if err := dr.save(pubKey, string(msg)); err != nil {
@@ -3808,6 +3823,9 @@ func (dr *CacheDirAccResolver) Start(s *Server) error {
 	if _, err := s.sysSubscribe(accClaimsReqSubj, func(_ *subscription, _ *client, subj, resp string, msg []byte) {
 		if claim, err := jwt.DecodeAccountClaims(string(msg)); err != nil {
 			respondToUpdate(s, resp, "n/a", "jwt update cache resulted in error", err)
+		} else if claim.Issuer == op && strict {
+			err := errors.New("operator requires issuer to be a signing key")
+			respondToUpdate(s, resp, claim.Subject, "jwt update cache resulted in error", err)
 		} else if _, ok := s.accounts.Load(claim.Subject); !ok {
 			respondToUpdate(s, resp, claim.Subject, "jwt update cache skipped", nil)
 		} else if err := dr.save(claim.Subject, string(msg)); err != nil {
diff --git a/server/auth.go b/server/auth.go
index 6265dfcd..8c520051 100644
--- a/server/auth.go
+++ b/server/auth.go
@@ -238,7 +238,7 @@ func (s *Server) configureAuthorization() {
 	// This just checks and sets up the user map if we have multiple users.
 	if opts.CustomClientAuthentication != nil {
 		s.info.AuthRequired = true
-	} else if len(s.trustedKeys) > 0 {
+	} else if s.trustedKeys != nil {
 		s.info.AuthRequired = true
 	} else if opts.Nkeys != nil || opts.Users != nil {
 		s.nkeys, s.users = s.buildNkeysAndUsersFromOptions(opts.Nkeys, opts.Users)
@@ -560,21 +560,18 @@ func (s *Server) processClientOrLeafAuthentication(c *client, opts *Options) boo
 			c.Debugf("Account JWT not signed by trusted operator")
 			return false
 		}
-		// this only executes IF there's an issuer on the Juc - otherwise the account is already vetted
-		if juc.IssuerAccount != _EMPTY_ {
-			if scope, ok := acc.hasIssuer(juc.Issuer); !ok {
-				c.Debugf("User JWT issuer is not known")
+		if scope, ok := acc.hasIssuer(juc.Issuer); !ok {
+			c.Debugf("User JWT issuer is not known")
+			return false
+		} else if scope != nil {
+			if err := scope.ValidateScopedSigner(juc); err != nil {
+				c.Debugf("User JWT is not valid: %v", err)
 				return false
-			} else if scope != nil {
-				if err := scope.ValidateScopedSigner(juc); err != nil {
-					c.Debugf("User JWT is not valid: %v", err)
-					return false
-				} else if uSc, ok := scope.(*jwt.UserScope); !ok {
-					c.Debugf("User JWT is not valid")
-					return false
-				} else {
-					juc.UserPermissionLimits = uSc.Template
-				}
+			} else if uSc, ok := scope.(*jwt.UserScope); !ok {
+				c.Debugf("User JWT is not valid")
+				return false
+			} else {
+				juc.UserPermissionLimits = uSc.Template
 			}
 		}
 		if acc.IsExpired() {
diff --git a/server/client.go b/server/client.go
index 2c09f55b..39527779 100644
--- a/server/client.go
+++ b/server/client.go
@@ -1787,7 +1787,7 @@ func (c *client) authViolation() {
 	var hasTrustedNkeys, hasNkeys, hasUsers bool
 	if s = c.srv; s != nil {
 		s.mu.Lock()
-		hasTrustedNkeys = len(s.trustedKeys) > 0
+		hasTrustedNkeys = s.trustedKeys != nil
 		hasNkeys = s.nkeys != nil
 		hasUsers = s.users != nil
 		s.mu.Unlock()
diff --git a/server/jwt.go b/server/jwt.go
index f7e76ba3..49efd4ec 100644
--- a/server/jwt.go
+++ b/server/jwt.go
@@ -132,7 +132,9 @@ func validateTrustedOperators(o *Options) error {
 		if o.TrustedKeys == nil {
 			o.TrustedKeys = make([]string, 0, 4)
 		}
-		o.TrustedKeys = append(o.TrustedKeys, opc.Issuer)
+		if !opc.StrictSigningKeyUsage {
+			o.TrustedKeys = append(o.TrustedKeys, opc.Subject)
+		}
 		o.TrustedKeys = append(o.TrustedKeys, opc.SigningKeys...)
 	}
 	for _, key := range o.TrustedKeys {
diff --git a/server/leafnode.go b/server/leafnode.go
index 9ccbf4a9..87a54b3a 100644
--- a/server/leafnode.go
+++ b/server/leafnode.go
@@ -407,9 +407,14 @@ func (s *Server) connectToRemoteLeafNode(remote *leafNodeCfg, firstConnect bool)
 
 // Save off the tlsName for when we use TLS and mix hostnames and IPs. IPs usually
 // come from the server we connect to.
+//
+// We used to save the name only if there was a TLSConfig or scheme equal to "tls".
+// However, this was causing failures for users that did not set the scheme (and
+// their remote connections did not have a tls{} block).
+// We now save the host name regardless in case the remote returns an INFO indicating
+// that TLS is required.
 func (cfg *leafNodeCfg) saveTLSHostname(u *url.URL) {
-	isTLS := cfg.TLSConfig != nil || u.Scheme == "tls"
-	if isTLS && cfg.tlsName == "" && net.ParseIP(u.Hostname()) == nil {
+	if cfg.tlsName == _EMPTY_ && net.ParseIP(u.Hostname()) == nil {
 		cfg.tlsName = u.Hostname()
 	}
 }
diff --git a/server/mqtt.go b/server/mqtt.go
index 65fef7f7..c2daaa12 100644
--- a/server/mqtt.go
+++ b/server/mqtt.go
@@ -1436,6 +1436,7 @@ func (c *client) mqttParseConnect(r *mqttReader, pl int) (byte, *mqttConnectProt
 			return 0, nil, err
 		}
 		c.opts.Token = c.opts.Password
+		c.opts.JWT = c.opts.Password
 	}
 	return 0, cp, nil
 }
diff --git a/server/nkey.go b/server/nkey.go
index a792c6e0..87b20a4f 100644
--- a/server/nkey.go
+++ b/server/nkey.go
@@ -33,7 +33,7 @@ func (s *Server) NonceRequired() bool {
 // nonceRequired tells us if we should send a nonce.
 // Lock should be held on entry.
 func (s *Server) nonceRequired() bool {
-	return len(s.nkeys) > 0 || len(s.trustedKeys) > 0
+	return len(s.nkeys) > 0 || s.trustedKeys != nil
 }
 
 // Generate a nonce for INFO challenge.
diff --git a/server/opts.go b/server/opts.go
index 9330a21a..17f6a74f 100644
--- a/server/opts.go
+++ b/server/opts.go
@@ -4036,6 +4036,7 @@ func ConfigureOptions(fs *flag.FlagSet, args []string, printVersion, printHelp,
 	fs.IntVar(&opts.Port, "p", 0, "Port to listen on.")
 	fs.StringVar(&opts.ServerName, "n", "", "Server name.")
 	fs.StringVar(&opts.ServerName, "name", "", "Server name.")
+	fs.StringVar(&opts.ServerName, "server_name", "", "Server name.")
 	fs.StringVar(&opts.Host, "addr", "", "Network host to listen on.")
 	fs.StringVar(&opts.Host, "a", "", "Network host to listen on.")
 	fs.StringVar(&opts.Host, "net", "", "Network host to listen on.")
diff --git a/server/raft.go b/server/raft.go
index d2ffd7e5..e05eb7f9 100644
--- a/server/raft.go
+++ b/server/raft.go
@@ -559,6 +559,11 @@ func (n *raft) Applied(index uint64) {
 	n.Lock()
 	defer n.Unlock()
 
+	// Ignore if already applied.
+	if index <= n.applied {
+		return
+	}
+
 	// FIXME(dlc) - Check spec on error conditions, storage
 	n.applied = index
 	// FIXME(dlc) - Can be more efficient here.
@@ -1319,6 +1324,10 @@ func (n *raft) loadEntry(index uint64) (*appendEntry, error) {
 // applyCommit will update our commit index and apply the entry to the apply chan.
 // lock should be held.
 func (n *raft) applyCommit(index uint64) {
+	if index <= n.commit {
+		n.debug("Ignoring apply commit for %d, already processed", index)
+		return
+	}
 	original := n.commit
 	n.commit = index
 
diff --git a/server/server.go b/server/server.go
index 5f2c7b2e..60962674 100644
--- a/server/server.go
+++ b/server/server.go
@@ -194,6 +194,8 @@ type Server struct {
 
 	// Trusted public operator keys.
 	trustedKeys []string
+	// map of trusted keys to operator setting StrictSigningKeyUsage
+	strictSigningKeyUsage map[string]struct{}
 
 	// We use this to minimize mem copies for requests to monitoring
 	// endpoint /varz (when it comes from http).
@@ -419,6 +421,8 @@ func NewServer(opts *Options) (*Server, error) {
 			if a == nil {
 				sac := NewAccount(s.opts.SystemAccount)
 				sac.Issuer = opts.TrustedOperators[0].Issuer
+				sac.signingKeys = map[string]jwt.Scope{}
+				sac.signingKeys[s.opts.SystemAccount] = nil
 				s.registerAccountNoLock(sac)
 			}
 		}
@@ -748,7 +752,7 @@ func (s *Server) generateRouteInfoJSON() {
 func (s *Server) globalAccountOnly() bool {
 	var hasOthers bool
 
-	if len(s.trustedKeys) > 0 {
+	if s.trustedKeys != nil {
 		return false
 	}
 
@@ -798,7 +802,7 @@ func (s *Server) isTrustedIssuer(issuer string) bool {
 	s.mu.Lock()
 	defer s.mu.Unlock()
 	// If we are not running in trusted mode and there is no issuer, that is ok.
-	if len(s.trustedKeys) == 0 && issuer == "" {
+	if s.trustedKeys == nil && issuer == "" {
 		return true
 	}
 	for _, tk := range s.trustedKeys {
@@ -812,6 +816,7 @@ func (s *Server) isTrustedIssuer(issuer string) bool {
 // processTrustedKeys will process binary stamped and
 // options-based trusted nkeys. Returns success.
 func (s *Server) processTrustedKeys() bool {
+	s.strictSigningKeyUsage = map[string]struct{}{}
 	if trustedKeys != "" && !s.initStampedTrustedKeys() {
 		return false
 	} else if s.opts.TrustedKeys != nil {
@@ -820,7 +825,15 @@ func (s *Server) processTrustedKeys() bool {
 				return false
 			}
 		}
-		s.trustedKeys = s.opts.TrustedKeys
+		s.trustedKeys = append([]string(nil), s.opts.TrustedKeys...)
+		for _, claim := range s.opts.TrustedOperators {
+			if !claim.StrictSigningKeyUsage {
+				continue
+			}
+			for _, key := range claim.SigningKeys {
+				s.strictSigningKeyUsage[key] = struct{}{}
+			}
+		}
 	}
 	return true
 }
@@ -1378,13 +1391,23 @@ func (s *Server) fetchAccount(name string) (*Account, error) {
 // Start up the server, this will block.
 // Start via a Go routine if needed.
 func (s *Server) Start() {
-	s.Noticef("Starting nats-server version %s", VERSION)
-	s.Debugf("Go build version %s", s.info.GoVersion)
+	s.Noticef("Starting nats-server")
+
 	gc := gitCommit
 	if gc == "" {
 		gc = "not set"
 	}
-	s.Noticef("Git commit [%s]", gc)
+
+	s.Noticef("  Version:  %s", VERSION)
+	s.Noticef("  Git:      [%s]", gc)
+	s.Debugf("  Go build: %s", s.info.GoVersion)
+	s.Noticef("  Name:     %s", s.info.Name)
+	if s.sys != nil {
+		s.Noticef("  Node:     %s", s.sys.shash)
+	}
+	s.Noticef("  ID:       %s", s.info.ID)
+
+	defer s.Noticef("Server is ready")
 
 	// Check for insecure configurations.
 	s.checkAuthforWarnings()
@@ -1769,13 +1792,6 @@ func (s *Server) AcceptLoop(clr chan struct{}) {
 		return
 	}
 
-	s.Noticef("Server name: %s", s.info.Name)
-	if s.sys != nil {
-		s.Noticef("Server node: %s", s.sys.shash)
-	}
-	s.Noticef("Server ID:   %s", s.info.ID)
-	s.Noticef("Server is ready")
-
 	hp := net.JoinHostPort(opts.Host, strconv.Itoa(opts.Port))
 	l, e := natsListen("tcp", hp)
 	if e != nil {
diff --git a/vendor/github.com/nats-io/jwt/v2/account_claims.go b/vendor/github.com/nats-io/jwt/v2/account_claims.go
index 2037c111..67169483 100644
--- a/vendor/github.com/nats-io/jwt/v2/account_claims.go
+++ b/vendor/github.com/nats-io/jwt/v2/account_claims.go
@@ -148,7 +148,7 @@ func NewAccountClaims(subject string) *AccountClaims {
 	c.Limits = OperatorLimits{
 		NatsLimits{NoLimit, NoLimit, NoLimit},
 		AccountLimits{NoLimit, NoLimit, true, NoLimit, NoLimit},
-		JetStreamLimits{NoLimit, NoLimit, NoLimit, NoLimit}}
+		JetStreamLimits{0, 0, 0, 0}}
 	c.Subject = subject
 	return c
 }
diff --git a/vendor/github.com/nats-io/jwt/v2/operator_claims.go b/vendor/github.com/nats-io/jwt/v2/operator_claims.go
index a41d9c04..61d474e5 100644
--- a/vendor/github.com/nats-io/jwt/v2/operator_claims.go
+++ b/vendor/github.com/nats-io/jwt/v2/operator_claims.go
@@ -43,6 +43,8 @@ type Operator struct {
 	SystemAccount string `json:"system_account,omitempty"`
 	// Min Server version
 	AssertServerVersion string `json:"assert_server_version,omitempty"`
+	// Signing of subordinate objects will require signing keys
+	StrictSigningKeyUsage bool `json:"strict_signing_key_usage,omitempty"`
 	GenericFields
 }
 
@@ -174,7 +176,7 @@ func (oc *OperatorClaims) DidSign(op Claims) bool {
 	}
 	issuer := op.Claims().Issuer
 	if issuer == oc.Subject {
-		return true
+		return !oc.StrictSigningKeyUsage
 	}
 	return oc.SigningKeys.Contains(issuer)
 }
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 60277f2c..c4e2429a 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -4,7 +4,7 @@ github.com/klauspost/compress/s2
 # github.com/minio/highwayhash v1.0.0
 ## explicit
 github.com/minio/highwayhash
-# github.com/nats-io/jwt/v2 v2.0.0-20210107222814-18c5cc45d263
+# github.com/nats-io/jwt/v2 v2.0.0-20210125223648-1c24d462becc
 ## explicit
 github.com/nats-io/jwt/v2
 # github.com/nats-io/nats.go v1.10.1-0.20210122204956-b8ea7fc17ea6


Test patch:
diff --git a/server/jwt_test.go b/server/jwt_test.go
index 739fbcc0..8df61f0a 100644
--- a/server/jwt_test.go
+++ b/server/jwt_test.go
@@ -5144,3 +5144,121 @@ func TestJWScopedSigningKeys(t *testing.T) {
 	})
 	require_Len(t, len(errChan), 0)
 }
+
+func TestJWTStrictSigningKeys(t *testing.T) {
+	newAccount := func(opKp nkeys.KeyPair) (nkeys.KeyPair, nkeys.KeyPair, string, *jwt.AccountClaims, string) {
+		accId, err := nkeys.CreateAccount()
+		require_NoError(t, err)
+		accIdPub, err := accId.PublicKey()
+		require_NoError(t, err)
+
+		accSig, err := nkeys.CreateAccount()
+		require_NoError(t, err)
+		accSigPub, err := accSig.PublicKey()
+		require_NoError(t, err)
+
+		aClaim := jwt.NewAccountClaims(accIdPub)
+		aClaim.SigningKeys.Add(accSigPub)
+		theJwt, err := aClaim.Encode(opKp)
+		require_NoError(t, err)
+		return accId, accSig, accIdPub, aClaim, theJwt
+	}
+
+	opId, err := nkeys.CreateOperator()
+	require_NoError(t, err)
+	opIdPub, err := opId.PublicKey()
+	require_NoError(t, err)
+
+	opSig, err := nkeys.CreateOperator()
+	require_NoError(t, err)
+	opSigPub, err := opSig.PublicKey()
+	require_NoError(t, err)
+
+	aBadBadKp, aBadGoodKp, aBadPub, _, aBadJwt := newAccount(opId)
+	aGoodBadKp, aGoodGoodKp, aGoodPub, _, aGoodJwt := newAccount(opSig)
+	_, aSysKp, aSysPub, _, aSysJwt := newAccount(opSig)
+
+	oClaim := jwt.NewOperatorClaims(opIdPub)
+	oClaim.StrictSigningKeyUsage = true
+	oClaim.SigningKeys.Add(opSigPub)
+	oClaim.SystemAccount = aSysPub
+	oJwt, err := oClaim.Encode(opId)
+	require_NoError(t, err)
+
+	uBadBadCreds := newUserEx(t, aBadBadKp, false, aBadPub)
+	defer os.Remove(uBadBadCreds)
+	uBadGoodCreds := newUserEx(t, aBadGoodKp, false, aBadPub)
+	defer os.Remove(uBadGoodCreds)
+	uGoodBadCreds := newUserEx(t, aGoodBadKp, false, aGoodPub)
+	defer os.Remove(uGoodBadCreds)
+	uGoodGoodCreds := newUserEx(t, aGoodGoodKp, false, aGoodPub)
+	defer os.Remove(uGoodGoodCreds)
+	uSysCreds := newUserEx(t, aSysKp, false, aSysPub)
+	defer os.Remove(uSysCreds)
+
+	connectTest := func(url string) {
+		for _, test := range []struct {
+			creds string
+			fail  bool
+		}{
+			{uBadBadCreds, true},
+			{uBadGoodCreds, true},
+			{uGoodBadCreds, true},
+			{uGoodGoodCreds, false},
+		} {
+			nc, err := nats.Connect(url, nats.UserCredentials(test.creds))
+			nc.Close()
+			if test.fail {
+				require_Error(t, err)
+			} else {
+				require_NoError(t, err)
+			}
+		}
+	}
+
+	t.Run("resolver", func(t *testing.T) {
+		dirSrv := createDir(t, "srv")
+		defer os.RemoveAll(dirSrv)
+		cf := createConfFile(t, []byte(fmt.Sprintf(`
+		port: -1
+		operator = %s
+		resolver: {
+			type: full
+			dir: %s
+		}
+		resolver_preload = {
+			%s : "%s"
+		}
+		`, oJwt, dirSrv, aSysPub, aSysJwt)))
+		defer os.Remove(cf)
+		s, _ := RunServerWithConfig(cf)
+		defer s.Shutdown()
+		url := s.ClientURL()
+		if updateJwt(t, url, uSysCreds, aBadJwt, 1) != 0 {
+			t.Fatal("Expected negative response")
+		}
+		if updateJwt(t, url, uSysCreds, aGoodJwt, 1) != 1 {
+			t.Fatal("Expected positive response")
+		}
+		connectTest(url)
+	})
+
+	t.Run("mem-resolver", func(t *testing.T) {
+		dirSrv := createDir(t, "srv")
+		defer os.RemoveAll(dirSrv)
+		cf := createConfFile(t, []byte(fmt.Sprintf(`
+		port: -1
+		operator = %s
+		resolver: MEMORY
+		resolver_preload = {
+			%s : "%s"
+			%s : "%s"
+			%s : "%s"
+		}
+		`, oJwt, aSysPub, aSysJwt, aBadPub, aBadJwt, aGoodPub, aGoodJwt)))
+		defer os.Remove(cf)
+		s, _ := RunServerWithConfig(cf)
+		defer s.Shutdown()
+		connectTest(s.ClientURL())
+	})
+}
diff --git a/server/leafnode_test.go b/server/leafnode_test.go
index dae790bc..9c389d70 100644
--- a/server/leafnode_test.go
+++ b/server/leafnode_test.go
@@ -953,6 +953,51 @@ func TestLeafCloseTLSConnection(t *testing.T) {
 	ch <- true
 }
 
+func TestLeafCloseTLSSaveName(t *testing.T) {
+	opts := DefaultOptions()
+	opts.LeafNode.Host = "127.0.0.1"
+	opts.LeafNode.Port = -1
+	tc := &TLSConfigOpts{
+		CertFile: "../test/configs/certs/server-noip.pem",
+		KeyFile:  "../test/configs/certs/server-key-noip.pem",
+		Insecure: true,
+	}
+	tlsConf, err := GenTLSConfig(tc)
+	if err != nil {
+		t.Fatalf("Error generating tls config: %v", err)
+	}
+	opts.LeafNode.TLSConfig = tlsConf
+	s := RunServer(opts)
+	defer s.Shutdown()
+
+	lo := DefaultOptions()
+	u, _ := url.Parse(fmt.Sprintf("nats://localhost:%d", opts.LeafNode.Port))
+	lo.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{u}}}
+	lo.LeafNode.ReconnectInterval = 15 * time.Millisecond
+	ln := RunServer(lo)
+	defer ln.Shutdown()
+
+	// We know connection will fail, but it should not fail because of error such as:
+	// "cannot validate certificate for 127.0.0.1 because it doesn't contain any IP SANs"
+	// This would mean that we are not saving the hostname to use during the TLS handshake.
+
+	le := &captureErrorLogger{errCh: make(chan string, 100)}
+	ln.SetLogger(le, false, false)
+
+	tm := time.NewTimer(time.Second)
+	var done bool
+	for !done {
+		select {
+		case err := <-le.errCh:
+			if strings.Contains(err, "doesn't contain any IP SANs") {
+				t.Fatalf("Got this error: %q", err)
+			}
+		case <-tm.C:
+			done = true
+		}
+	}
+}
+
 func TestLeafNodeRemoteWrongPort(t *testing.T) {
 	for _, test1 := range []struct {
 		name              string
@@ -1880,6 +1925,7 @@ func TestLeafNodeTwoRemotesBindToSameAccount(t *testing.T) {
 func TestLeafNodeNoDuplicateWithinCluster(t *testing.T) {
 	// This set the cluster name to "abc"
 	oSrv1 := DefaultOptions()
+	oSrv1.ServerName = "srv1"
 	oSrv1.LeafNode.Host = "127.0.0.1"
 	oSrv1.LeafNode.Port = -1
 	srv1 := RunServer(oSrv1)
@@ -1891,6 +1937,7 @@ func TestLeafNodeNoDuplicateWithinCluster(t *testing.T) {
 	}
 
 	oLeaf1 := DefaultOptions()
+	oLeaf1.ServerName = "leaf1"
 	oLeaf1.LeafNode.Remotes = []*RemoteLeafOpts{&RemoteLeafOpts{URLs: []*url.URL{u}}}
 	leaf1 := RunServer(oLeaf1)
 	defer leaf1.Shutdown()
@@ -1898,6 +1945,7 @@ func TestLeafNodeNoDuplicateWithinCluster(t *testing.T) {
 	leaf1ClusterURL := fmt.Sprintf("nats://127.0.0.1:%d", oLeaf1.Cluster.Port)
 
 	oLeaf2 := DefaultOptions()
+	oLeaf2.ServerName = "leaf2"
 	oLeaf2.LeafNode.Remotes = []*RemoteLeafOpts{&RemoteLeafOpts{URLs: []*url.URL{u}}}
 	oLeaf2.Routes = RoutesFromStr(leaf1ClusterURL)
 	leaf2 := RunServer(oLeaf2)
@@ -1925,9 +1973,26 @@ func TestLeafNodeNoDuplicateWithinCluster(t *testing.T) {
 	defer ncLeaf2.Close()
 
 	// Check that "foo" interest is available everywhere.
-	checkSubInterest(t, srv1, globalAccountName, "foo", time.Second)
-	checkSubInterest(t, leaf1, globalAccountName, "foo", time.Second)
-	checkSubInterest(t, leaf2, globalAccountName, "foo", time.Second)
+	// For this test, we want to make sure that the 2 queue subs are
+	// registered on all servers, so we don't use checkSubInterest
+	// which would simply return "true" if there is any interest on "foo".
+	servers := []*Server{srv1, leaf1, leaf2}
+	checkFor(t, time.Second, 15*time.Millisecond, func() error {
+		for _, s := range servers {
+			acc, err := s.LookupAccount(globalAccountName)
+			if err != nil {
+				return err
+			}
+			acc.mu.RLock()
+			r := acc.sl.Match("foo")
+			ok := len(r.qsubs) == 1 && len(r.qsubs[0]) == 2
+			acc.mu.RUnlock()
+			if !ok {
+				return fmt.Errorf("interest not propagated on %q", s.Name())
+			}
+		}
+		return nil
+	})
 
 	// Send requests (from leaf2). For this test to make sure that
 	// there is no duplicate, we want to make sure that we check for
@@ -1936,6 +2001,7 @@ func TestLeafNodeNoDuplicateWithinCluster(t *testing.T) {
 	sub := natsSubSync(t, ncLeaf2, "reply_subj")
 	natsFlush(t, ncLeaf2)
 
+	// Here we have a single sub on "reply_subj" so using checkSubInterest is ok.
 	checkSubInterest(t, srv1, globalAccountName, "reply_subj", time.Second)
 	checkSubInterest(t, leaf1, globalAccountName, "reply_subj", time.Second)
 	checkSubInterest(t, leaf2, globalAccountName, "reply_subj", time.Second)
diff --git a/server/mqtt_test.go b/server/mqtt_test.go
index f12ea878..5d3b1d9a 100644
--- a/server/mqtt_test.go
+++ b/server/mqtt_test.go
@@ -30,6 +30,7 @@ import (
 
 	"github.com/nats-io/jwt/v2"
 	"github.com/nats-io/nats.go"
+	"github.com/nats-io/nkeys"
 )
 
 type mqttErrorReader struct {
@@ -1119,6 +1120,83 @@ func TestMQTTTokenAuth(t *testing.T) {
 	}
 }
 
+func TestMQTTJWTWithAllowedConnectionTypes(t *testing.T) {
+	o := testMQTTDefaultOptions()
+	// Create System Account
+	syskp, _ := nkeys.CreateAccount()
+	syspub, _ := syskp.PublicKey()
+	sysAc := jwt.NewAccountClaims(syspub)
+	sysjwt, err := sysAc.Encode(oKp)
+	if err != nil {
+		t.Fatalf("Error generating account JWT: %v", err)
+	}
+	// Create memory resolver and store system account
+	mr := &MemAccResolver{}
+	mr.Store(syspub, sysjwt)
+	if err != nil {
+		t.Fatalf("Error saving system account JWT to memory resolver: %v", err)
+	}
+	// Add system account and memory resolver to server options
+	o.SystemAccount = syspub
+	o.AccountResolver = mr
+	setupAddTrusted(o)
+
+	s := testMQTTRunServer(t, o)
+	defer testMQTTShutdownServer(s)
+
+	for _, test := range []struct {
+		name            string
+		connectionTypes []string
+		rc              byte
+	}{
+		{"not allowed", []string{jwt.ConnectionTypeStandard}, mqttConnAckRCNotAuthorized},
+		{"allowed", []string{jwt.ConnectionTypeStandard, strings.ToLower(jwt.ConnectionTypeMqtt)}, mqttConnAckRCConnectionAccepted},
+		{"allowed with unknown", []string{jwt.ConnectionTypeMqtt, "SomeNewType"}, mqttConnAckRCConnectionAccepted},
+		{"not allowed with unknown", []string{"SomeNewType"}, mqttConnAckRCNotAuthorized},
+	} {
+		t.Run(test.name, func(t *testing.T) {
+
+			nuc := newJWTTestUserClaims()
+			nuc.AllowedConnectionTypes = test.connectionTypes
+			nuc.BearerToken = true
+
+			okp, _ := nkeys.FromSeed(oSeed)
+
+			akp, _ := nkeys.CreateAccount()
+			apub, _ := akp.PublicKey()
+			nac := jwt.NewAccountClaims(apub)
+			// Enable Jetstream on account with lax limitations
+			nac.Limits.JetStreamLimits.Consumer = -1
+			nac.Limits.JetStreamLimits.Streams = -1
+			nac.Limits.JetStreamLimits.MemoryStorage = 1024 * 1024
+			ajwt, err := nac.Encode(okp)
+			if err != nil {
+				t.Fatalf("Error generating account JWT: %v", err)
+			}
+
+			nkp, _ := nkeys.CreateUser()
+			pub, _ := nkp.PublicKey()
+			nuc.Subject = pub
+			jwt, err := nuc.Encode(akp)
+			if err != nil {
+				t.Fatalf("Error generating user JWT: %v", err)
+			}
+
+			addAccountToMemResolver(s, apub, ajwt)
+
+			ci := &mqttConnInfo{
+				cleanSess: true,
+				user:      "ignore_use_token",
+				pass:      jwt,
+			}
+
+			mc, r := testMQTTConnect(t, ci, o.MQTT.Host, o.MQTT.Port)
+			defer mc.Close()
+			testMQTTCheckConnAck(t, r, test.rc, false)
+		})
+	}
+}
+
 func TestMQTTUsersAuth(t *testing.T) {
 	users := []*User{&User{Username: "user", Password: "pwd"}}
 	for _, test := range []struct {
@@ -2282,6 +2360,11 @@ func TestMQTTSubWithNATSStream(t *testing.T) {
 	testMQTTSub(t, 1, mc, r, []*mqttFilter{&mqttFilter{filter: "foo/bar", qos: 1}}, []byte{1})
 	testMQTTFlush(t, mc, nil, r)
 
+	mcp, rp := testMQTTConnect(t, &mqttConnInfo{cleanSess: true}, o.MQTT.Host, o.MQTT.Port)
+	defer mcp.Close()
+	testMQTTCheckConnAck(t, rp, mqttConnAckRCConnectionAccepted, false)
+	testMQTTFlush(t, mcp, nil, rp)
+
 	nc := natsConnect(t, s.ClientURL())
 	defer nc.Close()
 
@@ -2327,11 +2410,11 @@ func TestMQTTSubWithNATSStream(t *testing.T) {
 	checkRecv("nats", 0)
 
 	// Send from MQTT as a QoS0
-	testMQTTPublish(t, mc, r, 0, false, false, "foo/bar", 0, []byte("qos0"))
+	testMQTTPublish(t, mcp, rp, 0, false, false, "foo/bar", 0, []byte("qos0"))
 	checkRecv("qos0", 0)
 
 	// Send from MQTT as a QoS1
-	testMQTTPublish(t, mc, r, 1, false, false, "foo/bar", 1, []byte("qos1"))
+	testMQTTPublish(t, mcp, rp, 1, false, false, "foo/bar", 1, []byte("qos1"))
 	checkRecv("qos1", mqttPubQos1)
 }
 
@@ -3670,6 +3753,9 @@ func TestMQTTMaxAckPending(t *testing.T) {
 	testMQTTCheckPubMsg(t, c, r, "foo", mqttPubQos1, []byte("msg2"))
 	testMQTTDisconnect(t, c, nil)
 
+	// Give a chance to the server to register that this client is gone.
+	checkClientsCount(t, s, 1)
+
 	// Send 2 messages while sub is offline
 	testMQTTPublish(t, cp, rp, 1, false, false, "foo", 1, []byte("msg3"))
 	testMQTTPublish(t, cp, rp, 1, false, false, "foo", 1, []byte("msg4"))