Repository: nats-io/nats-server
Pull request #1599 (2020-09-17 23:15:10)

Adding new subscription for account conns
This fits better with similar events
New subject is $SYS.ACCOUNT.%s.SERVER.CONNS
Old subject remains for backwards compatibility

Signed-off-by: Matthias Hanel <mh@synadia.com>

For backwards compatibility 2 subjects are used.
The utility to check if interest exists comes in handy here.

Going forward I'd add those to other system events as well, just thought this is a good first show case.


Patch:
diff --git a/go.mod b/go.mod
index 4fa5d648..20a7f33a 100644
--- a/go.mod
+++ b/go.mod
@@ -2,7 +2,7 @@ module github.com/nats-io/nats-server/v2
 
 require (
 	github.com/minio/highwayhash v1.0.0
-	github.com/nats-io/jwt/v2 v2.0.0-20200827232814-292806fa48ba
+	github.com/nats-io/jwt/v2 v2.0.0-20200916203241-1f8ce17dff02
 	github.com/nats-io/nats.go v1.10.1-0.20200606002146-fc6fed82929a
 	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 029d1375..3bf1f39a 100644
--- a/go.sum
+++ b/go.sum
@@ -14,8 +14,8 @@ github.com/minio/highwayhash v1.0.0/go.mod h1:xQboMTeM9nY9v/LlAOxFctujiv5+Aq2hR5
 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/v2 v2.0.0-20200827232814-292806fa48ba h1:1sdQj5FtOMA3AWDyDYXwFNfLiOqMX0/u8EeyzBxHCRo=
-github.com/nats-io/jwt/v2 v2.0.0-20200827232814-292806fa48ba/go.mod h1:vs+ZEjP+XKy8szkBmQwCB7RjYdIlMaPsFPs4VdS4bTQ=
+github.com/nats-io/jwt/v2 v2.0.0-20200916203241-1f8ce17dff02 h1:WloZv3SCb55D/rOHYy1rWBXLrj3BYc9zw8VIq6X54lI=
+github.com/nats-io/jwt/v2 v2.0.0-20200916203241-1f8ce17dff02/go.mod h1:vs+ZEjP+XKy8szkBmQwCB7RjYdIlMaPsFPs4VdS4bTQ=
 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.go v1.10.0/go.mod h1:AjGArbfyR50+afOUotNX2Xs5SYHf+CoOa5HH1eEl2HE=
diff --git a/server/accounts.go b/server/accounts.go
index b5e8416a..9b464227 100644
--- a/server/accounts.go
+++ b/server/accounts.go
@@ -2764,8 +2764,8 @@ func buildPermissionsFromJwt(uc *jwt.Permissions) *Permissions {
 }
 
 // Helper to build internal NKeyUser.
-func buildInternalNkeyUser(uc *jwt.UserClaims, acc *Account) *NkeyUser {
-	nu := &NkeyUser{Nkey: uc.Subject, Account: acc}
+func buildInternalNkeyUser(uc *jwt.UserClaims, acts map[string]struct{}, acc *Account) *NkeyUser {
+	nu := &NkeyUser{Nkey: uc.Subject, Account: acc, AllowedConnectionTypes: acts}
 	if uc.IssuerAccount != "" {
 		nu.SigningKey = uc.Issuer
 	}
diff --git a/server/auth.go b/server/auth.go
index e802cb3e..14eff310 100644
--- a/server/auth.go
+++ b/server/auth.go
@@ -50,18 +50,20 @@ type ClientAuthentication interface {
 
 // NkeyUser is for multiple nkey based users
 type NkeyUser struct {
-	Nkey        string       `json:"user"`
-	Permissions *Permissions `json:"permissions,omitempty"`
-	Account     *Account     `json:"account,omitempty"`
-	SigningKey  string       `json:"signing_key,omitempty"`
+	Nkey                   string              `json:"user"`
+	Permissions            *Permissions        `json:"permissions,omitempty"`
+	Account                *Account            `json:"account,omitempty"`
+	SigningKey             string              `json:"signing_key,omitempty"`
+	AllowedConnectionTypes map[string]struct{} `json:"connection_types,omitempty"`
 }
 
 // User is for multiple accounts/users.
 type User struct {
-	Username    string       `json:"user"`
-	Password    string       `json:"password"`
-	Permissions *Permissions `json:"permissions,omitempty"`
-	Account     *Account     `json:"account,omitempty"`
+	Username               string              `json:"user"`
+	Password               string              `json:"password"`
+	Permissions            *Permissions        `json:"permissions,omitempty"`
+	Account                *Account            `json:"account,omitempty"`
+	AllowedConnectionTypes map[string]struct{} `json:"connection_types,omitempty"`
 }
 
 // clone performs a deep copy of the User struct, returning a new clone with
@@ -356,8 +358,6 @@ func (s *Server) processClientOrLeafAuthentication(c *client, opts *Options) boo
 		password   string
 		token      string
 		noAuthUser string
-		users      map[string]*User
-		nkusers    map[string]*NkeyUser
 	)
 	tlsMap := opts.TLSMap
 	if c.ws != nil {
@@ -371,8 +371,6 @@ func (s *Server) processClientOrLeafAuthentication(c *client, opts *Options) boo
 			username = wo.Username
 			password = wo.Password
 			token = wo.Token
-			users = s.websocket.users
-			nkusers = s.websocket.nkeys
 			ao = true
 		}
 	} else if c.kind == LEAF {
@@ -383,8 +381,6 @@ func (s *Server) processClientOrLeafAuthentication(c *client, opts *Options) boo
 		username = opts.Username
 		password = opts.Password
 		token = opts.Authorization
-		users = s.users
-		nkusers = s.nkeys
 	}
 
 	// Check if we have trustedKeys defined in the server. If so we require a user jwt.
@@ -411,11 +407,11 @@ func (s *Server) processClientOrLeafAuthentication(c *client, opts *Options) boo
 	}
 
 	// Check if we have nkeys or users for client.
-	hasNkeys := len(nkusers) > 0
-	hasUsers := len(users) > 0
+	hasNkeys := len(s.nkeys) > 0
+	hasUsers := len(s.users) > 0
 	if hasNkeys && c.opts.Nkey != "" {
-		nkey, ok = nkusers[c.opts.Nkey]
-		if !ok {
+		nkey, ok = s.nkeys[c.opts.Nkey]
+		if !ok || !c.connectionTypeAllowed(nkey.AllowedConnectionTypes) {
 			s.mu.Unlock()
 			return false
 		}
@@ -426,8 +422,8 @@ func (s *Server) processClientOrLeafAuthentication(c *client, opts *Options) boo
 				// First do literal lookup using the resulting string representation
 				// of RDNSequence as implemented by the pkix package from Go.
 				if u != "" {
-					usr, ok := users[u]
-					if !ok {
+					usr, ok := s.users[u]
+					if !ok || !c.connectionTypeAllowed(usr.AllowedConnectionTypes) {
 						return "", ok
 					}
 					user = usr
@@ -440,7 +436,10 @@ func (s *Server) processClientOrLeafAuthentication(c *client, opts *Options) boo
 
 				// Look through the accounts for an RDN that is equal to the one
 				// presented by the certificate.
-				for _, usr := range users {
+				for _, usr := range s.users {
+					if !c.connectionTypeAllowed(usr.AllowedConnectionTypes) {
+						continue
+					}
 					// TODO: Use this utility to make a full validation pass
 					// on start in case tlsmap feature is being used.
 					inputRDN, err := ldap.ParseDN(usr.Username)
@@ -466,14 +465,14 @@ func (s *Server) processClientOrLeafAuthentication(c *client, opts *Options) boo
 			c.opts.Username = user.Username
 		} else {
 			if c.kind == CLIENT && c.opts.Username == "" && noAuthUser != "" {
-				if u, exists := users[noAuthUser]; exists {
+				if u, exists := s.users[noAuthUser]; exists {
 					c.opts.Username = u.Username
 					c.opts.Password = u.Password
 				}
 			}
 			if c.opts.Username != "" {
-				user, ok = users[c.opts.Username]
-				if !ok {
+				user, ok = s.users[c.opts.Username]
+				if !ok || !c.connectionTypeAllowed(user.AllowedConnectionTypes) {
 					s.mu.Unlock()
 					return false
 				}
@@ -485,6 +484,34 @@ func (s *Server) processClientOrLeafAuthentication(c *client, opts *Options) boo
 	// If we have a jwt and a userClaim, make sure we have the Account, etc associated.
 	// We need to look up the account. This will use an account resolver if one is present.
 	if juc != nil {
+		allowedConnTypes, err := convertAllowedConnectionTypes(juc.AllowedConnectionTypes)
+		if err != nil {
+			// We got an error, which means some connection types were unknown. As long as
+			// a valid one is returned, we proceed with auth. If not, we have to reject.
+			// In other words, suppose that JWT allows "WEBSOCKET" in the array. No error
+			// is returned and allowedConnTypes will contain "WEBSOCKET" only.
+			// Client will be rejected if not a websocket client, or proceed with rest of
+			// auth if it is.
+			// Now suppose JWT allows "WEBSOCKET, MQTT" and say MQTT is not known by this
+			// server. In this case, allowedConnTypes would contain "WEBSOCKET" and we
+			// would get `err` indicating that "MQTT" is an unknown connection type.
+			// If a websocket client connects, it should still be allowed, since after all
+			// the admin wanted to allow websocket and mqtt connection types.
+			// However, say that the JWT only allows "MQTT" (and again suppose this server
+			// does not know about MQTT connection type), then since the allowedConnTypes
+			// map would be empty (no valid types found), and since empty means allow-all,
+			// then we should reject because the intent was to allow connections for this
+			// user only as an MQTT client.
+			c.Debugf("%v", err)
+			if len(allowedConnTypes) == 0 {
+				return false
+			}
+			err = nil
+		}
+		if !c.connectionTypeAllowed(allowedConnTypes) {
+			c.Debugf("Connection type not allowed")
+			return false
+		}
 		issuer := juc.Issuer
 		if juc.IssuerAccount != "" {
 			issuer = juc.IssuerAccount
@@ -546,7 +573,7 @@ func (s *Server) processClientOrLeafAuthentication(c *client, opts *Options) boo
 			return false
 		}
 
-		nkey = buildInternalNkeyUser(juc, acc)
+		nkey = buildInternalNkeyUser(juc, allowedConnTypes, acc)
 		if err := c.RegisterNkeyUser(nkey); err != nil {
 			return false
 		}
@@ -895,21 +922,51 @@ func comparePasswords(serverPassword, clientPassword string) bool {
 }
 
 func validateAuth(o *Options) error {
-	if o.NoAuthUser == "" {
+	for _, u := range o.Users {
+		if err := validateAllowedConnectionTypes(u.AllowedConnectionTypes); err != nil {
+			return err
+		}
+	}
+	for _, u := range o.Nkeys {
+		if err := validateAllowedConnectionTypes(u.AllowedConnectionTypes); err != nil {
+			return err
+		}
+	}
+	return validateNoAuthUser(o, o.NoAuthUser)
+}
+
+func validateAllowedConnectionTypes(m map[string]struct{}) error {
+	for ct := range m {
+		ctuc := strings.ToUpper(ct)
+		switch ctuc {
+		case jwt.ConnectionTypeStandard, jwt.ConnectionTypeWebsocket, jwt.ConnectionTypeLeafnode:
+		default:
+			return fmt.Errorf("unknown connection type %q", ct)
+		}
+		if ctuc != ct {
+			delete(m, ct)
+			m[ctuc] = struct{}{}
+		}
+	}
+	return nil
+}
+
+func validateNoAuthUser(o *Options, noAuthUser string) error {
+	if noAuthUser == "" {
 		return nil
 	}
 	if len(o.TrustedOperators) > 0 {
 		return fmt.Errorf("no_auth_user not compatible with Trusted Operator")
 	}
 	if o.Users == nil {
-		return fmt.Errorf(`no_auth_user: "%s" present, but users are not defined`, o.NoAuthUser)
+		return fmt.Errorf(`no_auth_user: "%s" present, but users are not defined`, noAuthUser)
 	}
 	for _, u := range o.Users {
-		if u.Username == o.NoAuthUser {
+		if u.Username == noAuthUser {
 			return nil
 		}
 	}
 	return fmt.Errorf(
 		`no_auth_user: "%s" not present as user in authorization block or account configuration`,
-		o.NoAuthUser)
+		noAuthUser)
 }
diff --git a/server/client.go b/server/client.go
index 110c93e1..9553ed3a 100644
--- a/server/client.go
+++ b/server/client.go
@@ -4395,6 +4395,55 @@ func (c *client) getAuthUser() string {
 	}
 }
 
+// Given an array of strings, this function converts it to a map as long
+// as all the content (converted to upper-case) matches some constants.
+
+// Converts the given array of strings to a map of string.
+// The strings are converted to upper-case and added to the map only
+// if the server recognize them as valid connection types.
+// If there are unknown connection types, the map of valid ones is returned
+// along with an error that contains the name of the unknown.
+func convertAllowedConnectionTypes(cts []string) (map[string]struct{}, error) {
+	var unknown []string
+	m := make(map[string]struct{}, len(cts))
+	for _, i := range cts {
+		i = strings.ToUpper(i)
+		switch i {
+		case jwt.ConnectionTypeStandard, jwt.ConnectionTypeWebsocket, jwt.ConnectionTypeLeafnode:
+			m[i] = struct{}{}
+		default:
+			unknown = append(unknown, i)
+		}
+	}
+	var err error
+	// We will still return the map of valid ones.
+	if len(unknown) != 0 {
+		err = fmt.Errorf("invalid connection types %q", unknown)
+	}
+	return m, err
+}
+
+// This will return true if the connection is of a type present in the given `acts` map.
+// Note that so far this is used only for CLIENT or LEAF connections.
+// But a CLIENT can be standard or websocket (and other types in the future).
+func (c *client) connectionTypeAllowed(acts map[string]struct{}) bool {
+	// Empty means all type of clients are allowed
+	if len(acts) == 0 {
+		return true
+	}
+	// Assume standard client, then update based on presence of websocket
+	// or other type.
+	want := jwt.ConnectionTypeStandard
+	if c.kind == LEAF {
+		want = jwt.ConnectionTypeLeafnode
+	}
+	if c.ws != nil {
+		want = jwt.ConnectionTypeWebsocket
+	}
+	_, ok := acts[want]
+	return ok
+}
+
 // isClosed returns true if either closeConnection or connMarkedClosed
 // flag have been set, or if `nc` is nil, which may happen in tests.
 func (c *client) isClosed() bool {
diff --git a/server/events.go b/server/events.go
index 96dfe2a3..7974161b 100644
--- a/server/events.go
+++ b/server/events.go
@@ -41,7 +41,8 @@ const (
 	accConnsReqSubj          = "$SYS.REQ.ACCOUNT.%s.CONNS"
 	accUpdateEventSubj       = "$SYS.ACCOUNT.%s.CLAIMS.UPDATE"
 	connsRespSubj            = "$SYS._INBOX_.%s"
-	accConnsEventSubj        = "$SYS.SERVER.ACCOUNT.%s.CONNS"
+	accConnsEventSubjNew     = "$SYS.ACCOUNT.%s.SERVER.CONNS"
+	accConnsEventSubjOld     = "$SYS.SERVER.ACCOUNT.%s.CONNS" // kept for backward compatibility
 	shutdownEventSubj        = "$SYS.SERVER.%s.SHUTDOWN"
 	authErrorEventSubj       = "$SYS.SERVER.%s.CLIENT.AUTH.ERR"
 	serverStatsSubj          = "$SYS.SERVER.%s.STATSZ"
@@ -569,9 +570,12 @@ func (s *Server) initEventTracking() {
 	}
 	s.sys.inboxPre = subject
 	// This is for remote updates for connection accounting.
-	subject = fmt.Sprintf(accConnsEventSubj, "*")
-	if _, err := s.sysSubscribe(subject, s.remoteConnsUpdate); err != nil {
-		s.Errorf("Error setting up internal tracking: %v", err)
+
+	for _, subj := range []string{accConnsEventSubjOld, accConnsEventSubjNew} {
+		subject = fmt.Sprintf(subj, "*")
+		if _, err := s.sysSubscribe(subject, s.remoteConnsUpdate); err != nil {
+			s.Errorf("Error setting up internal tracking for %s: %v", subject, err)
+		}
 	}
 	// This will be for responses for account info that we send out.
 	subject = fmt.Sprintf(connsRespSubj, s.info.ID)
@@ -1067,23 +1071,22 @@ func (s *Server) sendLeafNodeConnectMsg(accName string) {
 // sendAccConnsUpdate is called to send out our information on the
 // account's local connections.
 // Lock should be held on entry.
-func (s *Server) sendAccConnsUpdate(a *Account, subj string) {
+func (s *Server) sendAccConnsUpdate(a *Account, subj ...string) {
 	if !s.eventsEnabled() || a == nil {
 		return
 	}
-	a.mu.RLock()
-
 	// Build event with account name and number of local clients and leafnodes.
-	m := AccountNumConns{
+	a.mu.RLock()
+	m := &AccountNumConns{
 		Account:    a.Name,
 		Conns:      a.numLocalConnections(),
 		LeafNodes:  a.numLocalLeafNodes(),
 		TotalConns: a.numLocalConnections() + a.numLocalLeafNodes(),
 	}
 	a.mu.RUnlock()
-
-	s.sendInternalMsg(subj, _EMPTY_, &m.Server, &m)
-
+	for _, sub := range subj {
+		s.sendInternalMsg(sub, _EMPTY_, &m.Server, &m)
+	}
 	// Set timer to fire again unless we are at zero.
 	a.mu.Lock()
 	if a.numLocalConnections() == 0 {
@@ -1107,8 +1110,7 @@ func (s *Server) accConnsUpdate(a *Account) {
 	if !s.eventsEnabled() || a == nil {
 		return
 	}
-	subj := fmt.Sprintf(accConnsEventSubj, a.Name)
-	s.sendAccConnsUpdate(a, subj)
+	s.sendAccConnsUpdate(a, fmt.Sprintf(accConnsEventSubjOld, a.Name), fmt.Sprintf(accConnsEventSubjNew, a.Name))
 }
 
 // server lock should be held
diff --git a/server/opts.go b/server/opts.go
index 7015bb1a..5eae3fdf 100644
--- a/server/opts.go
+++ b/server/opts.go
@@ -263,9 +263,8 @@ type WebsocketOpts struct {
 	// The host:port to advertise to websocket clients in the cluster.
 	Advertise string
 
-	// If no user is provided when a client connects, will default to this
-	// user and associated account. This user has to exist either in the
-	// Users defined here or in the global options.
+	// If no user name is provided when a client connects, will default to the
+	// matching user from the global list of users in `Options.Users`.
 	NoAuthUser string
 
 	// Name of the cookie, which if present in WebSocket upgrade headers,
@@ -274,12 +273,10 @@ type WebsocketOpts struct {
 	JWTCookie string
 
 	// Authentication section. If anything is configured in this section,
-	// it will override the authorization configuration for regular clients.
+	// it will override the authorization configuration of regular clients.
 	Username string
 	Password string
 	Token    string
-	Users    []*User
-	Nkeys    []*NkeyUser
 
 	// Timeout for the authentication process.
 	AuthTimeout float64
@@ -2714,6 +2711,10 @@ func parseUsers(mv interface{}, opts *Options, errors *[]error, warnings *[]erro
 					*errors = append(*errors, err)
 					continue
 				}
+			case "allowed_connection_types", "connection_types", "clients":
+				cts := parseAllowedConnectionTypes(tk, &lt, v, errors, warnings)
+				nkey.AllowedConnectionTypes = cts
+				user.AllowedConnectionTypes = cts
 			default:
 				if !tk.IsUsedVariable() {
 					err := &unknownConfigFieldErr{
@@ -2757,6 +2758,19 @@ func parseUsers(mv interface{}, opts *Options, errors *[]error, warnings *[]erro
 	return keys, users, nil
 }
 
+func parseAllowedConnectionTypes(tk token, lt *token, mv interface{}, errors *[]error, warnings *[]error) map[string]struct{} {
+	cts, err := parseStringArray("allowed connection types", tk, lt, mv, errors, warnings)
+	// If error, it has already been added to the `errors` array, simply return
+	if err != nil {
+		return nil
+	}
+	m, err := convertAllowedConnectionTypes(cts)
+	if err != nil {
+		*errors = append(*errors, &configErr{tk, err.Error()})
+	}
+	return m
+}
+
 // Helper function to parse user/account permissions
 func parseUserPermissions(mv interface{}, errors, warnings *[]error) (*Permissions, error) {
 	var (
@@ -3119,6 +3133,75 @@ func parseTLS(v interface{}) (t *TLSConfigOpts, retErr error) {
 	return &tc, nil
 }
 
+func parseAuthForWS(v interface{}, errors *[]error, warnings *[]error) *authorization {
+	var (
+		am   map[string]interface{}
+		tk   token
+		lt   token
+		auth = &authorization{}
+	)
+	defer convertPanicToErrorList(&lt, errors)
+
+	_, v = unwrapValue(v, &lt)
+	am = v.(map[string]interface{})
+	for mk, mv := range am {
+		tk, mv = unwrapValue(mv, &lt)
+		switch strings.ToLower(mk) {
+		case "user", "username":
+			auth.user = mv.(string)
+		case "pass", "password":
+			auth.pass = mv.(string)
+		case "token":
+			auth.token = mv.(string)
+		case "timeout":
+			at := float64(1)
+			switch mv := mv.(type) {
+			case int64:
+				at = float64(mv)
+			case float64:
+				at = mv
+			}
+			auth.timeout = at
+		default:
+			if !tk.IsUsedVariable() {
+				err := &unknownConfigFieldErr{
+					field: mk,
+					configErr: configErr{
+						token: tk,
+					},
+				}
+				*errors = append(*errors, err)
+			}
+			continue
+		}
+	}
+	return auth
+}
+
+func parseStringArray(fieldName string, tk token, lt *token, mv interface{}, errors *[]error, warnings *[]error) ([]string, error) {
+	switch mv := mv.(type) {
+	case string:
+		return []string{mv}, nil
+	case []interface{}:
+		strs := make([]string, 0, len(mv))
+		for _, val := range mv {
+			tk, val = unwrapValue(val, lt)
+			if str, ok := val.(string); ok {
+				strs = append(strs, str)
+			} else {
+				err := &configErr{tk, fmt.Sprintf("error parsing %s: unsupported type in array %T", fieldName, val)}
+				*errors = append(*errors, err)
+				continue
+			}
+		}
+		return strs, nil
+	default:
+		err := &configErr{tk, fmt.Sprintf("error parsing %s: unsupported type %T", fieldName, mv)}
+		*errors = append(*errors, err)
+		return nil, err
+	}
+}
+
 func parseWebsocket(v interface{}, o *Options, errors *[]error, warnings *[]error) error {
 	var lt token
 	defer convertPanicToErrorList(&lt, errors)
@@ -3164,26 +3247,7 @@ func parseWebsocket(v interface{}, o *Options, errors *[]error, warnings *[]erro
 		case "same_origin":
 			o.Websocket.SameOrigin = mv.(bool)
 		case "allowed_origins", "allowed_origin", "allow_origins", "allow_origin", "origins", "origin":
-			switch mv := mv.(type) {
-			case string:
-				o.Websocket.AllowedOrigins = []string{mv}
-			case []interface{}:
-				keys := make([]string, 0, len(mv))
-				for _, val := range mv {
-					tk, val = unwrapValue(val, &lt)
-					if key, ok := val.(string); ok {
-						keys = append(keys, key)
-					} else {
-						err := &configErr{tk, fmt.Sprintf("error parsing allowed origins: unsupported type in array %T", val)}
-						*errors = append(*errors, err)
-						continue
-					}
-				}
-				o.Websocket.AllowedOrigins = keys
-			default:
-				err := &configErr{tk, fmt.Sprintf("error parsing allowed origins: unsupported type %T", mv)}
-				*errors = append(*errors, err)
-			}
+			o.Websocket.AllowedOrigins, _ = parseStringArray("allowed origins", tk, &lt, mv, errors, warnings)
 		case "handshake_timeout":
 			ht := time.Duration(0)
 			switch mv := mv.(type) {
@@ -3205,39 +3269,11 @@ func parseWebsocket(v interface{}, o *Options, errors *[]error, warnings *[]erro
 		case "compression":
 			o.Websocket.Compression = mv.(bool)
 		case "authorization", "authentication":
-			auth, err := parseAuthorization(tk, o, errors, warnings)
-			if err != nil {
-				*errors = append(*errors, err)
-				continue
-			}
+			auth := parseAuthForWS(tk, errors, warnings)
 			o.Websocket.Username = auth.user
 			o.Websocket.Password = auth.pass
 			o.Websocket.Token = auth.token
-			if (auth.user != "" || auth.pass != "") && auth.token != "" {
-				err := &configErr{tk, "Cannot have a user/pass and token"}
-				*errors = append(*errors, err)
-				continue
-			}
 			o.Websocket.AuthTimeout = auth.timeout
-			// Check for multiple users defined
-			if auth.users != nil {
-				if auth.user != "" {
-					err := &configErr{tk, "Can not have a single user/pass and a users array"}
-					*errors = append(*errors, err)
-					continue
-				}
-				if auth.token != "" {
-					err := &configErr{tk, "Can not have a token and a users array"}
-					*errors = append(*errors, err)
-					continue
-				}
-				// Users may have been added from Accounts parsing, so do an append here
-				o.Websocket.Users = append(o.Websocket.Users, auth.users...)
-			}
-			// Check for nkeys
-			if auth.nkeys != nil {
-				o.Websocket.Nkeys = append(o.Websocket.Nkeys, auth.nkeys...)
-			}
 		case "jwt_cookie":
 			o.Websocket.JWTCookie = mv.(string)
 		case "no_auth_user":
diff --git a/server/reload.go b/server/reload.go
index acd89e69..7861dcca 100644
--- a/server/reload.go
+++ b/server/reload.go
@@ -754,9 +754,7 @@ func imposeOrder(value interface{}) error {
 			return value[i].String() < value[j].String()
 		})
 	case []string:
-		sort.Slice(value, func(i, j int) bool {
-			return value[i] < value[j]
-		})
+		sort.Strings(value)
 	case []*jwt.OperatorClaims:
 		sort.Slice(value, func(i, j int) bool {
 			return value[i].Issuer < value[j].Issuer
@@ -766,9 +764,7 @@ func imposeOrder(value interface{}) error {
 			return value.Gateways[i].Name < value.Gateways[j].Name
 		})
 	case WebsocketOpts:
-		sort.Slice(value.AllowedOrigins, func(i, j int) bool {
-			return value.AllowedOrigins[i] < value.AllowedOrigins[j]
-		})
+		sort.Strings(value.AllowedOrigins)
 	case string, bool, int, int32, int64, time.Duration, float64, nil,
 		LeafNodeOpts, ClusterOpts, *tls.Config, *URLAccResolver, *MemAccResolver, *DirAccResolver, *CacheDirAccResolver, Authentication:
 		// explicitly skipped types
diff --git a/server/server.go b/server/server.go
index cba5b81b..c6ddb819 100644
--- a/server/server.go
+++ b/server/server.go
@@ -1995,6 +1995,11 @@ func (s *Server) startMonitoring(secure bool) error {
 		MaxHeaderBytes: 1 << 20,
 	}
 	s.mu.Lock()
+	if s.shutdown {
+		httpListener.Close()
+		s.mu.Unlock()
+		return nil
+	}
 	s.http = httpListener
 	s.httpHandler = mux
 	s.monitoringServer = srv
diff --git a/server/websocket.go b/server/websocket.go
index 3e877395..67fd8074 100644
--- a/server/websocket.go
+++ b/server/websocket.go
@@ -102,8 +102,6 @@ type srvWebsocket struct {
 	sameOrigin     bool
 	connectURLs    []string
 	connectURLsMap refCountedUrlSet
-	users          map[string]*User
-	nkeys          map[string]*NkeyUser
 	authOverride   bool // indicate if there is auth override in websocket config
 }
 
@@ -744,15 +742,18 @@ func validateWebsocketOptions(o *Options) error {
 	// If there is a NoAuthUser, we need to have Users defined and
 	// the user to be present.
 	if wo.NoAuthUser != _EMPTY_ {
-		if wo.Users == nil {
-			return fmt.Errorf("websocket no_auth_user %q configured, but users are not", wo.NoAuthUser)
+		if err := validateNoAuthUser(o, wo.NoAuthUser); err != nil {
+			return err
 		}
-		for _, u := range wo.Users {
-			if u.Username == wo.NoAuthUser {
-				return nil
-			}
+	}
+	// Token/Username not possible if there are users/nkeys
+	if len(o.Users) > 0 || len(o.Nkeys) > 0 {
+		if wo.Username != _EMPTY_ {
+			return fmt.Errorf("websocket authentication username not compatible with presence of users/nkeys")
+		}
+		if wo.Token != _EMPTY_ {
+			return fmt.Errorf("websocket authentication token not compatible with presence of users/nkeys")
 		}
-		return fmt.Errorf("websocket no_auth_user %q not found in users configuration", wo.NoAuthUser)
 	}
 	// Using JWT requires Trusted Keys
 	if wo.JWTCookie != "" {
@@ -799,16 +800,8 @@ func (s *Server) wsSetOriginOptions(o *WebsocketOpts) {
 // Server lock is held on entry.
 func (s *Server) wsConfigAuth(opts *WebsocketOpts) {
 	ws := &s.websocket
-	if len(opts.Nkeys) > 0 || len(opts.Users) > 0 {
-		ws.nkeys, ws.users = s.buildNkeysAndUsersFromOptions(opts.Nkeys, opts.Users)
-		ws.authOverride = true
-	} else if opts.Username != "" || opts.Token != "" {
-		ws.authOverride = true
-	} else {
-		ws.users = nil
-		ws.nkeys = nil
-		ws.authOverride = false
-	}
+	// If any of those is specified, we consider that there is an override.
+	ws.authOverride = opts.Username != _EMPTY_ || opts.Token != _EMPTY_ || opts.NoAuthUser != _EMPTY_
 }
 
 func (s *Server) startWebsocketServer() {
diff --git a/vendor/github.com/nats-io/jwt/.gitignore b/vendor/github.com/nats-io/jwt/.gitignore
deleted file mode 100644
index 7117a678..00000000
--- a/vendor/github.com/nats-io/jwt/.gitignore
+++ /dev/null
@@ -1,16 +0,0 @@
-# Binaries for programs and plugins
-*.exe
-*.exe~
-*.dll
-*.so
-*.dylib
-
-# Test binary, build with `go test -c`
-*.test
-
-# Output of the go coverage tool, specifically when used with LiteIDE
-*.out
-
-# IDE Files
-.vscode
-.idea/
\ No newline at end of file
diff --git a/vendor/github.com/nats-io/jwt/.travis.yml b/vendor/github.com/nats-io/jwt/.travis.yml
deleted file mode 100644
index 50e27a6b..00000000
--- a/vendor/github.com/nats-io/jwt/.travis.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-language: go
-sudo: false
-go:
-- 1.13.x
-- 1.12.x
-
-install:
-- go get -t ./...
-- go get github.com/mattn/goveralls
-- go get github.com/wadey/gocovmerge
-- go get -u honnef.co/go/tools/cmd/staticcheck
-- go get -u github.com/client9/misspell/cmd/misspell
-
-before_script:
-- $(exit $(go fmt ./... | wc -l))
-- go vet ./...
-- misspell -error -locale US .
-- staticcheck ./...
-
-script:
-- go test -v -race ./...
-- if [[ "$TRAVIS_GO_VERSION" =~ 1.12 ]]; then ./scripts/cov.sh TRAVIS; fi
diff --git a/vendor/github.com/nats-io/jwt/LICENSE b/vendor/github.com/nats-io/jwt/LICENSE
deleted file mode 100644
index 261eeb9e..00000000
--- a/vendor/github.com/nats-io/jwt/LICENSE
+++ /dev/null
@@ -1,201 +0,0 @@
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
diff --git a/vendor/github.com/nats-io/jwt/Makefile b/vendor/github.com/nats-io/jwt/Makefile
deleted file mode 100644
index 95e468e1..00000000
--- a/vendor/github.com/nats-io/jwt/Makefile
+++ /dev/null
@@ -1,19 +0,0 @@
-.PHONY: test cover
-
-build:
-	go build
-
-test:
-	gofmt -s -w *.go
-	goimports -w *.go
-	go vet ./...
-	go test -v
-	go test -v --race
-	staticcheck ./...
-
-fmt:
-	gofmt -w -s *.go
-
-cover:
-	 go test -v -covermode=count -coverprofile=coverage.out
-	 go tool cover -html=coverage.out
diff --git a/vendor/github.com/nats-io/jwt/README.md b/vendor/github.com/nats-io/jwt/README.md
deleted file mode 100644
index d3cb88ac..00000000
--- a/vendor/github.com/nats-io/jwt/README.md
+++ /dev/null
@@ -1,54 +0,0 @@
-# JWT
-A [JWT](https://jwt.io/) implementation that uses [nkeys](https://github.com/nats-io/nkeys) to digitally sign JWT tokens. 
-Nkeys use [Ed25519](https://ed25519.cr.yp.to/) to provide authentication of JWT claims.
-
-
-[![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
-[![ReportCard](http://goreportcard.com/badge/nats-io/jwt)](http://goreportcard.com/report/nats-io/jwt)
-[![Build Status](https://travis-ci.org/nats-io/jwt.svg?branch=master)](http://travis-ci.org/nats-io/jwt)
-[![GoDoc](http://godoc.org/github.com/nats-io/jwt?status.png)](http://godoc.org/github.com/nats-io/jwt)
-[![Coverage Status](https://coveralls.io/repos/github/nats-io/jwt/badge.svg?branch=master&t=NmEFup)](https://coveralls.io/github/nats-io/jwt?branch=master)
-
-```go
-// Need a private key to sign the claim, nkeys makes it easy to create
-kp, err := nkeys.CreateAccount()
-if err != nil {
-    t.Fatal("unable to create account key", err)
-}
-
-pk, err := kp.PublicKey()
-if err != nil {
-	t.Fatal("error getting public key", err)
-}
-
-// create a new claim
-claims := NewAccountClaims(pk)
-claims.Expires = time.Now().Add(time.Duration(time.Hour)).Unix()
-
-
-// add details by modifying claims.Account
-
-// serialize the claim to a JWT token
-token, err := claims.Encode(kp)
-if err != nil {
-    t.Fatal("error encoding token", err)
-}
-
-// on the receiving side, decode the token
-c, err := DecodeAccountClaims(token)
-if err != nil {
-    t.Fatal(err)
-}
-
-// if the token was decoded, it means that it
-// validated and it wasn't tampered. the remaining and
-// required test is to insure the issuer is trusted
-pk, err := kp.PublicKey()
-if err != nil {
-    t.Fatalf("unable to read public key: %v", err)
-}
-
-if c.Issuer != pk {
-    t.Fatalf("the public key is not trusted")
-}
-```
\ No newline at end of file
diff --git a/vendor/github.com/nats-io/jwt/ReleaseNotes.md b/vendor/github.com/nats-io/jwt/ReleaseNotes.md
deleted file mode 100644
index 500965ea..00000000
--- a/vendor/github.com/nats-io/jwt/ReleaseNotes.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Release Notes
-
-## 0.3.0
-
-* Removed revocation claims in favor of timestamp-based revocation maps in account and export claims.
diff --git a/vendor/github.com/nats-io/jwt/account_claims.go b/vendor/github.com/nats-io/jwt/account_claims.go
deleted file mode 100644
index 945bd987..00000000
--- a/vendor/github.com/nats-io/jwt/account_claims.go
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright 2018-2019 The NATS Authors
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package jwt
-
-import (
-	"errors"
-	"sort"
-	"time"
-
-	"github.com/nats-io/nkeys"
-)
-
-// NoLimit is used to indicate a limit field is unlimited in value.
-const NoLimit = -1
-
-// OperatorLimits are used to limit access by an account
-type OperatorLimits struct {
-	Subs            int64 `json:"subs,omitempty"`      // Max number of subscriptions
-	Conn            int64 `json:"conn,omitempty"`      // Max number of active connections
-	LeafNodeConn    int64 `json:"leaf,omitempty"`      // Max number of active leaf node connections
-	Imports         int64 `json:"imports,omitempty"`   // Max number of imports
-	Exports         int64 `json:"exports,omitempty"`   // Max number of exports
-	Data            int64 `json:"data,omitempty"`      // Max number of bytes
-	Payload         int64 `json:"payload,omitempty"`   // Max message payload
-	WildcardExports bool  `json:"wildcards,omitempty"` // Are wildcards allowed in exports
-}
-
-// IsEmpty returns true if all of the limits are 0/false.
-func (o *OperatorLimits) IsEmpty() bool {
-	return *o == OperatorLimits{}
-}
-
-// IsUnlimited returns true if all limits are
-func (o *OperatorLimits) IsUnlimited() bool {
-	return *o == OperatorLimits{NoLimit, NoLimit, NoLimit, NoLimit, NoLimit, NoLimit, NoLimit, true}
-}
-
-// Validate checks that the operator limits contain valid values
-func (o *OperatorLimits) Validate(vr *ValidationResults) {
-	// negative values mean unlimited, so all numbers are valid
-}
-
-// Account holds account specific claims data
-type Account struct {
-	Imports     Imports        `json:"imports,omitempty"`
-	Exports     Exports        `json:"exports,omitempty"`
-	Identities  []Identity     `json:"identity,omitempty"`
-	Limits      OperatorLimits `json:"limits,omitempty"`
-	SigningKeys StringList     `json:"signing_keys,omitempty"`
-	Revocations RevocationList `json:"revocations,omitempty"`
-}
-
-// Validate checks if the account is valid, based on the wrapper
-func (a *Account) Validate(acct *AccountClaims, vr *ValidationResults) {
-	a.Imports.Validate(acct.Subject, vr)
-	a.Exports.Validate(vr)
-	a.Limits.Validate(vr)
-
-	for _, i := range a.Identities {
-		i.Validate(vr)
-	}
-
-	if !a.Limits.IsEmpty() && a.Limits.Imports >= 0 && int64(len(a.Imports)) > a.Limits.Imports {
-		vr.AddError("the account contains more imports than allowed by the operator")
-	}
-
-	// Check Imports and Exports for limit violations.
-	if a.Limits.Imports != NoLimit {
-		if int64(len(a.Imports)) > a.Limits.Imports {
-			vr.AddError("the account contains more imports than allowed by the operator")
-		}
-	}
-	if a.Limits.Exports != NoLimit {
-		if int64(len(a.Exports)) > a.Limits.Exports {
-			vr.AddError("the account contains more exports than allowed by the operator")
-		}
-		// Check for wildcard restrictions
-		if !a.Limits.WildcardExports {
-			for _, ex := range a.Exports {
-				if ex.Subject.HasWildCards() {
-					vr.AddError("the account contains wildcard exports that are not allowed by the operator")
-				}
-			}
-		}
-	}
-
-	for _, k := range a.SigningKeys {
-		if !nkeys.IsValidPublicAccountKey(k) {
-			vr.AddError("%s is not an account public key", k)
-		}
-	}
-}
-
-// AccountClaims defines the body of an account JWT
-type AccountClaims struct {
-	ClaimsData
-	Account `json:"nats,omitempty"`
-}
-
-// NewAccountClaims creates a new account JWT
-func NewAccountClaims(subject string) *AccountClaims {
-	if subject == "" {
-		return nil
-	}
-	c := &AccountClaims{}
-	// Set to unlimited to start. We do it this way so we get compiler
-	// errors if we add to the OperatorLimits.
-	c.Limits = OperatorLimits{NoLimit, NoLimit, NoLimit, NoLimit, NoLimit, NoLimit, NoLimit, true}
-	c.Subject = subject
-	return c
-}
-
-// Encode converts account claims into a JWT string
-func (a *AccountClaims) Encode(pair nkeys.KeyPair) (string, error) {
-	if !nkeys.IsValidPublicAccountKey(a.Subject) {
-		return "", errors.New("expected subject to be account public key")
-	}
-	sort.Sort(a.Exports)
-	sort.Sort(a.Imports)
-	a.ClaimsData.Type = AccountClaim
-	return a.ClaimsData.Encode(pair, a)
-}
-
-// DecodeAccountClaims decodes account claims from a JWT string
-func DecodeAccountClaims(token string) (*AccountClaims, error) {
-	v := AccountClaims{}
-	if err := Decode(token, &v); err != nil {
-		return nil, err
-	}
-	return &v, nil
-}
-
-func (a *AccountClaims) String() string {
-	return a.ClaimsData.String(a)
-}
-
-// Payload pulls the accounts specific payload out of the claims
-func (a *AccountClaims) Payload() interface{} {
-	return &a.Account
-}
-
-// Validate checks the accounts contents
-func (a *AccountClaims) Validate(vr *ValidationResults) {
-	a.ClaimsData.Validate(vr)
-	a.Account.Validate(a, vr)
-
-	if nkeys.IsValidPublicAccountKey(a.ClaimsData.Issuer) {
-		if len(a.Identities) > 0 {
-			vr.AddWarning("self-signed account JWTs shouldn't contain identity proofs")
-		}
-		if !a.Limits.IsEmpty() {
-			vr.AddWarning("self-signed account JWTs shouldn't contain operator limits")
-		}
-	}
-}
-
-// ExpectedPrefixes defines the types that can encode an account jwt, account and operator
-func (a *AccountClaims) ExpectedPrefixes() []nkeys.PrefixByte {
-	return []nkeys.PrefixByte{nkeys.PrefixByteAccount, nkeys.PrefixByteOperator}
-}
-
-// Claims returns the accounts claims data
-func (a *AccountClaims) Claims() *ClaimsData {
-	return &a.ClaimsData
-}
-
-// DidSign checks the claims against the account's public key and its signing keys
-func (a *AccountClaims) DidSign(op Claims) bool {
-	if op != nil {
-		issuer := op.Claims().Issuer
-		if issuer == a.Subject {
-			return true
-		}
-		return a.SigningKeys.Contains(issuer)
-	}
-	return false
-}
-
-// Revoke enters a revocation by publickey using time.Now().
-func (a *AccountClaims) Revoke(pubKey string) {
-	a.RevokeAt(pubKey, time.Now())
-}
-
-// RevokeAt enters a revocation by publickey and timestamp into this export
-// If there is already a revocation for this public key that is newer, it is kept.
-func (a *AccountClaims) RevokeAt(pubKey string, timestamp time.Time) {
-	if a.Revocations == nil {
-		a.Revocations = RevocationList{}
-	}
-
-	a.Revocations.Revoke(pubKey, timestamp)
-}
-
-// ClearRevocation removes any revocation for the public key
-func (a *AccountClaims) ClearRevocation(pubKey string) {
-	a.Revocations.ClearRevocation(pubKey)
-}
-
-// IsRevokedAt checks if the public key is in the revoked list with a timestamp later than
-// the one passed in. Generally this method is called with time.Now() but other time's can
-// be used for testing.
-func (a *AccountClaims) IsRevokedAt(pubKey string, timestamp time.Time) bool {
-	return a.Revocations.IsRevoked(pubKey, timestamp)
-}
-
-// IsRevoked checks if the public key is in the revoked list with time.Now()
-func (a *AccountClaims) IsRevoked(pubKey string) bool {
-	return a.Revocations.IsRevoked(pubKey, time.Now())
-}
diff --git a/vendor/github.com/nats-io/jwt/activation_claims.go b/vendor/github.com/nats-io/jwt/activation_claims.go
deleted file mode 100644
index 99228a75..00000000
--- a/vendor/github.com/nats-io/jwt/activation_claims.go
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright 2018 The NATS Authors
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package jwt
-
-import (
-	"crypto/sha256"
-	"encoding/base32"
-	"errors"
-	"fmt"
-	"strings"
-
-	"github.com/nats-io/nkeys"
-)
-
-// Activation defines the custom parts of an activation claim
-type Activation struct {
-	ImportSubject Subject    `json:"subject,omitempty"`
-	ImportType    ExportType `json:"type,omitempty"`
-	Limits
-}
-
-// IsService returns true if an Activation is for a service
-func (a *Activation) IsService() bool {
-	return a.ImportType == Service
-}
-
-// IsStream returns true if an Activation is for a stream
-func (a *Activation) IsStream() bool {
-	return a.ImportType == Stream
-}
-
-// Validate checks the exports and limits in an activation JWT
-func (a *Activation) Validate(vr *ValidationResults) {
-	if !a.IsService() && !a.IsStream() {
-		vr.AddError("invalid export type: %q", a.ImportType)
-	}
-
-	if a.IsService() {
-		if a.ImportSubject.HasWildCards() {
-			vr.AddError("services cannot have wildcard subject: %q", a.ImportSubject)
-		}
-	}
-
-	a.ImportSubject.Validate(vr)
-	a.Limits.Validate(vr)
-}
-
-// ActivationClaims holds the data specific to an activation JWT
-type ActivationClaims struct {
-	ClaimsData
-	Activation `json:"nats,omitempty"`
-	// IssuerAccount stores the public key for the account the issuer represents.
-	// When set, the claim was issued by a signing key.
-	IssuerAccount string `json:"issuer_account,omitempty"`
-}
-
-// NewActivationClaims creates a new activation claim with the provided sub
-func NewActivationClaims(subject string) *ActivationClaims {
-	if subject == "" {
-		return nil
-	}
-	ac := &ActivationClaims{}
-	ac.Subject = subject
-	return ac
-}
-
-// Encode turns an activation claim into a JWT strimg
-func (a *ActivationClaims) Encode(pair nkeys.KeyPair) (string, error) {
-	if !nkeys.IsValidPublicAccountKey(a.ClaimsData.Subject) {
-		return "", errors.New("expected subject to be an account")
-	}
-	a.ClaimsData.Type = ActivationClaim
-	return a.ClaimsData.Encode(pair, a)
-}
-
-// DecodeActivationClaims tries to create an activation claim from a JWT string
-func DecodeActivationClaims(token string) (*ActivationClaims, error) {
-	v := ActivationClaims{}
-	if err := Decode(token, &v); err != nil {
-		return nil, err
-	}
-	return &v, nil
-}
-
-// Payload returns the activation specific part of the JWT
-func (a *ActivationClaims) Payload() interface{} {
-	return a.Activation
-}
-
-// Validate checks the claims
-func (a *ActivationClaims) Validate(vr *ValidationResults) {
-	a.ClaimsData.Validate(vr)
-	a.Activation.Validate(vr)
-	if a.IssuerAccount != "" && !nkeys.IsValidPublicAccountKey(a.IssuerAccount) {
-		vr.AddError("account_id is not an account public key")
-	}
-}
-
-// ExpectedPrefixes defines the types that can sign an activation jwt, account and oeprator
-func (a *ActivationClaims) ExpectedPrefixes() []nkeys.PrefixByte {
-	return []nkeys.PrefixByte{nkeys.PrefixByteAccount, nkeys.PrefixByteOperator}
-}
-
-// Claims returns the generic part of the JWT
-func (a *ActivationClaims) Claims() *ClaimsData {
-	return &a.ClaimsData
-}
-
-func (a *ActivationClaims) String() string {
-	return a.ClaimsData.String(a)
-}
-
-// HashID returns a hash of the claims that can be used to identify it.
-// The hash is calculated by creating a string with
-// issuerPubKey.subjectPubKey.<subject> and constructing the sha-256 hash and base32 encoding that.
-// <subject> is the exported subject, minus any wildcards, so foo.* becomes foo.
-// the one special case is that if the export start with "*" or is ">" the <subject> "_"
-func (a *ActivationClaims) HashID() (string, error) {
-
-	if a.Issuer == "" || a.Subject == "" || a.ImportSubject == "" {
-		return "", fmt.Errorf("not enough data in the activaion claims to create a hash")
-	}
-
-	subject := cleanSubject(string(a.ImportSubject))
-	base := fmt.Sprintf("%s.%s.%s", a.Issuer, a.Subject, subject)
-	h := sha256.New()
-	h.Write([]byte(base))
-	sha := h.Sum(nil)
-	hash := base32.StdEncoding.EncodeToString(sha)
-
-	return hash, nil
-}
-
-func cleanSubject(subject string) string {
-	split := strings.Split(subject, ".")
-	cleaned := ""
-
-	for i, tok := range split {
-		if tok == "*" || tok == ">" {
-			if i == 0 {
-				cleaned = "_"
-				break
-			}
-
-			cleaned = strings.Join(split[:i], ".")
-			break
-		}
-	}
-	if cleaned == "" {
-		cleaned = subject
-	}
-	return cleaned
-}
diff --git a/vendor/github.com/nats-io/jwt/claims.go b/vendor/github.com/nats-io/jwt/claims.go
deleted file mode 100644
index d402bcc5..00000000
--- a/vendor/github.com/nats-io/jwt/claims.go
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
- * Copyright 2018-2019 The NATS Authors
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package jwt
-
-import (
-	"crypto/sha512"
-	"encoding/base32"
-	"encoding/base64"
-	"encoding/json"
-	"errors"
-	"fmt"
-	"strings"
-	"time"
-
-	"github.com/nats-io/nkeys"
-)
-
-// ClaimType is used to indicate the type of JWT being stored in a Claim
-type ClaimType string
-
-const (
-	// AccountClaim is the type of an Account JWT
-	AccountClaim = "account"
-	//ActivationClaim is the type of an activation JWT
-	ActivationClaim = "activation"
-	//UserClaim is the type of an user JWT
-	UserClaim = "user"
-	//ServerClaim is the type of an server JWT
-	ServerClaim = "server"
-	//ClusterClaim is the type of an cluster JWT
-	ClusterClaim = "cluster"
-	//OperatorClaim is the type of an operator JWT
-	OperatorClaim = "operator"
-)
-
-// Claims is a JWT claims
-type Claims interface {
-	Claims() *ClaimsData
-	Encode(kp nkeys.KeyPair) (string, error)
-	ExpectedPrefixes() []nkeys.PrefixByte
-	Payload() interface{}
-	String() string
-	Validate(vr *ValidationResults)
-	Verify(payload string, sig []byte) bool
-}
-
-// ClaimsData is the base struct for all claims
-type ClaimsData struct {
-	Audience  string    `json:"aud,omitempty"`
-	Expires   int64     `json:"exp,omitempty"`
-	ID        string    `json:"jti,omitempty"`
-	IssuedAt  int64     `json:"iat,omitempty"`
-	Issuer    string    `json:"iss,omitempty"`
-	Name      string    `json:"name,omitempty"`
-	NotBefore int64     `json:"nbf,omitempty"`
-	Subject   string    `json:"sub,omitempty"`
-	Tags      TagList   `json:"tags,omitempty"`
-	Type      ClaimType `json:"type,omitempty"`
-}
-
-// Prefix holds the prefix byte for an NKey
-type Prefix struct {
-	nkeys.PrefixByte
-}
-
-func encodeToString(d []byte) string {
-	return base64.RawURLEncoding.EncodeToString(d)
-}
-
-func decodeString(s string) ([]byte, error) {
-	return base64.RawURLEncoding.DecodeString(s)
-}
-
-func serialize(v interface{}) (string, error) {
-	j, err := json.Marshal(v)
-	if err != nil {
-		return "", err
-	}
-	return encodeToString(j), nil
-}
-
-func (c *ClaimsData) doEncode(header *Header, kp nkeys.KeyPair, claim Claims) (string, error) {
-	if header == nil {
-		return "", errors.New("header is required")
-	}
-
-	if kp == nil {
-		return "", errors.New("keypair is required")
-	}
-
-	if c.Subject == "" {
-		return "", errors.New("subject is not set")
-	}
-
-	h, err := serialize(header)
-	if err != nil {
-		return "", err
-	}
-
-	issuerBytes, err := kp.PublicKey()
-	if err != nil {
-		return "", err
-	}
-
-	prefixes := claim.ExpectedPrefixes()
-	if prefixes != nil {
-		ok := false
-		for _, p := range prefixes {
-			switch p {
-			case nkeys.PrefixByteAccount:
-				if nkeys.IsValidPublicAccountKey(issuerBytes) {
-					ok = true
-				}
-			case nkeys.PrefixByteOperator:
-				if nkeys.IsValidPublicOperatorKey(issuerBytes) {
-					ok = true
-				}
-			case nkeys.PrefixByteServer:
-				if nkeys.IsValidPublicServerKey(issuerBytes) {
-					ok = true
-				}
-			case nkeys.PrefixByteCluster:
-				if nkeys.IsValidPublicClusterKey(issuerBytes) {
-					ok = true
-				}
-			case nkeys.PrefixByteUser:
-				if nkeys.IsValidPublicUserKey(issuerBytes) {
-					ok = true
-				}
-			}
-		}
-		if !ok {
-			return "", fmt.Errorf("unable to validate expected prefixes - %v", prefixes)
-		}
-	}
-
-	c.Issuer = string(issuerBytes)
-	c.IssuedAt = time.Now().UTC().Unix()
-
-	c.ID, err = c.hash()
-	if err != nil {
-		return "", err
-	}
-
-	payload, err := serialize(claim)
-	if err != nil {
-		return "", err
-	}
-
-	sig, err := kp.Sign([]byte(payload))
-	if err != nil {
-		return "", err
-	}
-	eSig := encodeToString(sig)
-	return fmt.Sprintf("%s.%s.%s", h, payload, eSig), nil
-}
-
-func (c *ClaimsData) hash() (string, error) {
-	j, err := json.Marshal(c)
-	if err != nil {
-		return "", err
-	}
-	h := sha512.New512_256()
-	h.Write(j)
-	return base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(h.Sum(nil)), nil
-}
-
-// Encode encodes a claim into a JWT token. The claim is signed with the
-// provided nkey's private key
-func (c *ClaimsData) Encode(kp nkeys.KeyPair, payload Claims) (string, error) {
-	return c.doEncode(&Header{TokenTypeJwt, AlgorithmNkey}, kp, payload)
-}
-
-// Returns a JSON representation of the claim
-func (c *ClaimsData) String(claim interface{}) string {
-	j, err := json.MarshalIndent(claim, "", "  ")
-	if err != nil {
-		return ""
-	}
-	return string(j)
-}
-
-func parseClaims(s string, target Claims) error {
-	h, err := decodeString(s)
-	if err != nil {
-		return err
-	}
-	return json.Unmarshal(h, &target)
-}
-
-// Verify verifies that the encoded payload was signed by the
-// provided public key. Verify is called automatically with
-// the claims portion of the token and the public key in the claim.
-// Client code need to insure that the public key in the
-// claim is trusted.
-func (c *ClaimsData) Verify(payload string, sig []byte) bool {
-	// decode the public key
-	kp, err := nkeys.FromPublicKey(c.Issuer)
-	if err != nil {
-		return false
-	}
-	if err := kp.Verify([]byte(payload), sig); err != nil {
-		return false
-	}
-	return true
-}
-
-// Validate checks a claim to make sure it is valid. Validity checks
-// include expiration and not before constraints.
-func (c *ClaimsData) Validate(vr *ValidationResults) {
-	now := time.Now().UTC().Unix()
-	if c.Expires > 0 && now > c.Expires {
-		vr.AddTimeCheck("claim is expired")
-	}
-
-	if c.NotBefore > 0 && c.NotBefore > now {
-		vr.AddTimeCheck("claim is not yet valid")
-	}
-}
-
-// IsSelfSigned returns true if the claims issuer is the subject
-func (c *ClaimsData) IsSelfSigned() bool {
-	return c.Issuer == c.Subject
-}
-
-// Decode takes a JWT string decodes it and validates it
-// and return the embedded Claims. If the token header
-// doesn't match the expected algorithm, or the claim is
-// not valid or verification fails an error is returned.
-func Decode(token string, target Claims) error {
-	// must have 3 chunks
-	chunks := strings.Split(token, ".")
-	if len(chunks) != 3 {
-		return errors.New("expected 3 chunks")
-	}
-
-	_, err := parseHeaders(chunks[0])
-	if err != nil {
-		return err
-	}
-
-	if err := parseClaims(chunks[1], target); err != nil {
-		return err
-	}
-
-	sig, err := decodeString(chunks[2])
-	if err != nil {
-		return err
-	}
-
-	if !target.Verify(chunks[1], sig) {
-		return errors.New("claim failed signature verification")
-	}
-
-	prefixes := target.ExpectedPrefixes()
-	if prefixes != nil {
-		ok := false
-		issuer := target.Claims().Issuer
-		for _, p := range prefixes {
-			switch p {
-			case nkeys.PrefixByteAccount:
-				if nkeys.IsValidPublicAccountKey(issuer) {
-					ok = true
-				}
-			case nkeys.PrefixByteOperator:
-				if nkeys.IsValidPublicOperatorKey(issuer) {
-					ok = true
-				}
-			case nkeys.PrefixByteServer:
-				if nkeys.IsValidPublicServerKey(issuer) {
-					ok = true
-				}
-			case nkeys.PrefixByteCluster:
-				if nkeys.IsValidPublicClusterKey(issuer) {
-					ok = true
-				}
-			case nkeys.PrefixByteUser:
-				if nkeys.IsValidPublicUserKey(issuer) {
-					ok = true
-				}
-			}
-		}
-		if !ok {
-			return fmt.Errorf("unable to validate expected prefixes - %v", prefixes)
-		}
-	}
-
-	return nil
-}
diff --git a/vendor/github.com/nats-io/jwt/cluster_claims.go b/vendor/github.com/nats-io/jwt/cluster_claims.go
deleted file mode 100644
index bbfcf06f..00000000
--- a/vendor/github.com/nats-io/jwt/cluster_claims.go
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2018 The NATS Authors
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package jwt
-
-import (
-	"errors"
-
-	"github.com/nats-io/nkeys"
-)
-
-// Cluster stores the cluster specific elements of a cluster JWT
-type Cluster struct {
-	Trust       []string `json:"identity,omitempty"`
-	Accounts    []string `json:"accts,omitempty"`
-	AccountURL  string   `json:"accturl,omitempty"`
-	OperatorURL string   `json:"opurl,omitempty"`
-}
-
-// Validate checks the cluster and permissions for a cluster JWT
-func (c *Cluster) Validate(vr *ValidationResults) {
-	// fixme validate cluster data
-}
-
-// ClusterClaims defines the data in a cluster JWT
-type ClusterClaims struct {
-	ClaimsData
-	Cluster `json:"nats,omitempty"`
-}
-
-// NewClusterClaims creates a new cluster JWT with the specified subject/public key
-func NewClusterClaims(subject string) *ClusterClaims {
-	if subject == "" {
-		return nil
-	}
-	c := &ClusterClaims{}
-	c.Subject = subject
-	return c
-}
-
-// Encode tries to turn the cluster claims into a JWT string
-func (c *ClusterClaims) Encode(pair nkeys.KeyPair) (string, error) {
-	if !nkeys.IsValidPublicClusterKey(c.Subject) {
-		return "", errors.New("expected subject to be a cluster public key")
-	}
-	c.ClaimsData.Type = ClusterClaim
-	return c.ClaimsData.Encode(pair, c)
-}
-
-// DecodeClusterClaims tries to parse cluster claims from a JWT string
-func DecodeClusterClaims(token string) (*ClusterClaims, error) {
-	v := ClusterClaims{}
-	if err := Decode(token, &v); err != nil {
-		return nil, err
-	}
-	return &v, nil
-}
-
-func (c *ClusterClaims) String() string {
-	return c.ClaimsData.String(c)
-}
-
-// Payload returns the cluster specific data
-func (c *ClusterClaims) Payload() interface{} {
-	return &c.Cluster
-}
-
-// Validate checks the generic and cluster data in the cluster claims
-func (c *ClusterClaims) Validate(vr *ValidationResults) {
-	c.ClaimsData.Validate(vr)
-	c.Cluster.Validate(vr)
-}
-
-// ExpectedPrefixes defines the types that can encode a cluster JWT, operator or cluster
-func (c *ClusterClaims) ExpectedPrefixes() []nkeys.PrefixByte {
-	return []nkeys.PrefixByte{nkeys.PrefixByteOperator, nkeys.PrefixByteCluster}
-}
-
-// Claims returns the generic data
-func (c *ClusterClaims) Claims() *ClaimsData {
-	return &c.ClaimsData
-}
diff --git a/vendor/github.com/nats-io/jwt/creds_utils.go b/vendor/github.com/nats-io/jwt/creds_utils.go
deleted file mode 100644
index 265057f1..00000000
--- a/vendor/github.com/nats-io/jwt/creds_utils.go
+++ /dev/null
@@ -1,203 +0,0 @@
-package jwt
-
-import (
-	"bytes"
-	"errors"
-	"fmt"
-	"regexp"
-	"strings"
-
-	"github.com/nats-io/nkeys"
-)
-
-// DecorateJWT returns a decorated JWT that describes the kind of JWT
-func DecorateJWT(jwtString string) ([]byte, error) {
-	gc, err := DecodeGeneric(jwtString)
-	if err != nil {
-		return nil, err
-	}
-	return formatJwt(string(gc.Type), jwtString)
-}
-
-func formatJwt(kind string, jwtString string) ([]byte, error) {
-	templ := `-----BEGIN NATS %s JWT-----
-%s
-------END NATS %s JWT------
-
-`
-	w := bytes.NewBuffer(nil)
-	kind = strings.ToUpper(kind)
-	_, err := fmt.Fprintf(w, templ, kind, jwtString, kind)
-	if err != nil {
-		return nil, err
-	}
-	return w.Bytes(), nil
-}
-
-// DecorateSeed takes a seed and returns a string that wraps
-// the seed in the form:
-//  ************************* IMPORTANT *************************
-//  NKEY Seed printed below can be used sign and prove identity.
-//  NKEYs are sensitive and should be treated as secrets.
-//
-//  -----BEGIN USER NKEY SEED-----
-//  SUAIO3FHUX5PNV2LQIIP7TZ3N4L7TX3W53MQGEIVYFIGA635OZCKEYHFLM
-//  ------END USER NKEY SEED------
-func DecorateSeed(seed []byte) ([]byte, error) {
-	w := bytes.NewBuffer(nil)
-	ts := bytes.TrimSpace(seed)
-	pre := string(ts[0:2])
-	kind := ""
-	switch pre {
-	case "SU":
-		kind = "USER"
-	case "SA":
-		kind = "ACCOUNT"
-	case "SO":
-		kind = "OPERATOR"
-	default:
-		return nil, errors.New("seed is not an operator, account or user seed")
-	}
-	header := `************************* IMPORTANT *************************
-NKEY Seed printed below can be used to sign and prove identity.
-NKEYs are sensitive and should be treated as secrets.
-
------BEGIN %s NKEY SEED-----
-`
-	_, err := fmt.Fprintf(w, header, kind)
-	if err != nil {
-		return nil, err
-	}
-	w.Write(ts)
-
-	footer := `
-------END %s NKEY SEED------
-
-*************************************************************
-`
-	_, err = fmt.Fprintf(w, footer, kind)
-	if err != nil {
-		return nil, err
-	}
-	return w.Bytes(), nil
-}
-
-var userConfigRE = regexp.MustCompile(`\s*(?:(?:[-]{3,}.*[-]{3,}\r?\n)([\w\-.=]+)(?:\r?\n[-]{3,}.*[-]{3,}\r?\n))`)
-
-// An user config file looks like this:
-//  -----BEGIN NATS USER JWT-----
-//  eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5...
-//  ------END NATS USER JWT------
-//
-//  ************************* IMPORTANT *************************
-//  NKEY Seed printed below can be used sign and prove identity.
-//  NKEYs are sensitive and should be treated as secrets.
-//
-//  -----BEGIN USER NKEY SEED-----
-//  SUAIO3FHUX5PNV2LQIIP7TZ3N4L7TX3W53MQGEIVYFIGA635OZCKEYHFLM
-//  ------END USER NKEY SEED------
-
-// FormatUserConfig returns a decorated file with a decorated JWT and decorated seed
-func FormatUserConfig(jwtString string, seed []byte) ([]byte, error) {
-	gc, err := DecodeGeneric(jwtString)
-	if err != nil {
-		return nil, err
-	}
-	if gc.Type != UserClaim {
-		return nil, fmt.Errorf("%q cannot be serialized as a user config", string(gc.Type))
-	}
-
-	w := bytes.NewBuffer(nil)
-
-	jd, err := formatJwt(string(gc.Type), jwtString)
-	if err != nil {
-		return nil, err
-	}
-	_, err = w.Write(jd)
-	if err != nil {
-		return nil, err
-	}
-	if !bytes.HasPrefix(bytes.TrimSpace(seed), []byte("SU")) {
-		return nil, fmt.Errorf("nkey seed is not an user seed")
-	}
-
-	d, err := DecorateSeed(seed)
-	if err != nil {
-		return nil, err
-	}
-	_, err = w.Write(d)
-	if err != nil {
-		return nil, err
-	}
-
-	return w.Bytes(), nil
-}
-
-// ParseDecoratedJWT takes a creds file and returns the JWT portion.
-func ParseDecoratedJWT(contents []byte) (string, error) {
-	items := userConfigRE.FindAllSubmatch(contents, -1)
-	if len(items) == 0 {
-		return string(contents), nil
-	}
-	// First result should be the user JWT.
-	// We copy here so that if the file contained a seed file too we wipe appropriately.
-	raw := items[0][1]
-	tmp := make([]byte, len(raw))
-	copy(tmp, raw)
-	return string(tmp), nil
-}
-
-// ParseDecoratedNKey takes a creds file, finds the NKey portion and creates a
-// key pair from it.
-func ParseDecoratedNKey(contents []byte) (nkeys.KeyPair, error) {
-	var seed []byte
-
-	items := userConfigRE.FindAllSubmatch(contents, -1)
-	if len(items) > 1 {
-		seed = items[1][1]
-	} else {
-		lines := bytes.Split(contents, []byte("\n"))
-		for _, line := range lines {
-			if bytes.HasPrefix(bytes.TrimSpace(line), []byte("SO")) ||
-				bytes.HasPrefix(bytes.TrimSpace(line), []byte("SA")) ||
-				bytes.HasPrefix(bytes.TrimSpace(line), []byte("SU")) {
-				seed = line
-				break
-			}
-		}
-	}
-	if seed == nil {
-		return nil, errors.New("no nkey seed found")
-	}
-	if !bytes.HasPrefix(seed, []byte("SO")) &&
-		!bytes.HasPrefix(seed, []byte("SA")) &&
-		!bytes.HasPrefix(seed, []byte("SU")) {
-		return nil, errors.New("doesn't contain a seed nkey")
-	}
-	kp, err := nkeys.FromSeed(seed)
-	if err != nil {
-		return nil, err
-	}
-	return kp, nil
-}
-
-// ParseDecoratedUserNKey takes a creds file, finds the NKey portion and creates a
-// key pair from it. Similar to ParseDecoratedNKey but fails for non-user keys.
-func ParseDecoratedUserNKey(contents []byte) (nkeys.KeyPair, error) {
-	nk, err := ParseDecoratedNKey(contents)
-	if err != nil {
-		return nil, err
-	}
-	seed, err := nk.Seed()
-	if err != nil {
-		return nil, err
-	}
-	if !bytes.HasPrefix(seed, []byte("SU")) {
-		return nil, errors.New("doesn't contain an user seed nkey")
-	}
-	kp, err := nkeys.FromSeed(seed)
-	if err != nil {
-		return nil, err
-	}
-	return kp, nil
-}
diff --git a/vendor/github.com/nats-io/jwt/exports.go b/vendor/github.com/nats-io/jwt/exports.go
deleted file mode 100644
index 5578f988..00000000
--- a/vendor/github.com/nats-io/jwt/exports.go
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright 2018-2019 The NATS Authors
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package jwt
-
-import (
-	"fmt"
-	"time"
-)
-
-// ResponseType is used to store an export response type
-type ResponseType string
-
-const (
-	// ResponseTypeSingleton is used for a service that sends a single response only
-	ResponseTypeSingleton = "Singleton"
-
-	// ResponseTypeStream is used for a service that will send multiple responses
-	ResponseTypeStream = "Stream"
-
-	// ResponseTypeChunked is used for a service that sends a single response in chunks (so not quite a stream)
-	ResponseTypeChunked = "Chunked"
-)
-
-// ServiceLatency is used when observing and exported service for
-// latency measurements.
-// Sampling 1-100, represents sampling rate, defaults to 100.
-// Results is the subject where the latency metrics are published.
-// A metric will be defined by the nats-server's ServiceLatency. Time durations
-// are in nanoseconds.
-// see https://github.com/nats-io/nats-server/blob/master/server/accounts.go#L524
-// e.g.
-// {
-//  "app": "dlc22",
-//  "start": "2019-09-16T21:46:23.636869585-07:00",
-//  "svc": 219732,
-//  "nats": {
-//    "req": 320415,
-//    "resp": 228268,
-//    "sys": 0
-//  },
-//  "total": 768415
-// }
-//
-type ServiceLatency struct {
-	Sampling int     `json:"sampling,omitempty"`
-	Results  Subject `json:"results"`
-}
-
-func (sl *ServiceLatency) Validate(vr *ValidationResults) {
-	if sl.Sampling < 1 || sl.Sampling > 100 {
-		vr.AddError("sampling percentage needs to be between 1-100")
-	}
-	sl.Results.Validate(vr)
-	if sl.Results.HasWildCards() {
-		vr.AddError("results subject can not contain wildcards")
-	}
-}
-
-// Export represents a single export
-type Export struct {
-	Name         string          `json:"name,omitempty"`
-	Subject      Subject         `json:"subject,omitempty"`
-	Type         ExportType      `json:"type,omitempty"`
-	TokenReq     bool            `json:"token_req,omitempty"`
-	Revocations  RevocationList  `json:"revocations,omitempty"`
-	ResponseType ResponseType    `json:"response_type,omitempty"`
-	Latency      *ServiceLatency `json:"service_latency,omitempty"`
-}
-
-// IsService returns true if an export is for a service
-func (e *Export) IsService() bool {
-	return e.Type == Service
-}
-
-// IsStream returns true if an export is for a stream
-func (e *Export) IsStream() bool {
-	return e.Type == Stream
-}
-
-// IsSingleResponse returns true if an export has a single response
-// or no resopnse type is set, also checks that the type is service
-func (e *Export) IsSingleResponse() bool {
-	return e.Type == Service && (e.ResponseType == ResponseTypeSingleton || e.ResponseType == "")
-}
-
-// IsChunkedResponse returns true if an export has a chunked response
-func (e *Export) IsChunkedResponse() bool {
-	return e.Type == Service && e.ResponseType == ResponseTypeChunked
-}
-
-// IsStreamResponse returns true if an export has a chunked response
-func (e *Export) IsStreamResponse() bool {
-	return e.Type == Service && e.ResponseType == ResponseTypeStream
-}
-
-// Validate appends validation issues to the passed in results list
-func (e *Export) Validate(vr *ValidationResults) {
-	if !e.IsService() && !e.IsStream() {
-		vr.AddError("invalid export type: %q", e.Type)
-	}
-	if e.IsService() && !e.IsSingleResponse() && !e.IsChunkedResponse() && !e.IsStreamResponse() {
-		vr.AddError("invalid response type for service: %q", e.ResponseType)
-	}
-	if e.IsStream() && e.ResponseType != "" {
-		vr.AddError("invalid response type for stream: %q", e.ResponseType)
-	}
-	if e.Latency != nil {
-		if !e.IsService() {
-			vr.AddError("latency tracking only permitted for services")
-		}
-		e.Latency.Validate(vr)
-	}
-	e.Subject.Validate(vr)
-}
-
-// Revoke enters a revocation by publickey using time.Now().
-func (e *Export) Revoke(pubKey string) {
-	e.RevokeAt(pubKey, time.Now())
-}
-
-// RevokeAt enters a revocation by publickey and timestamp into this export
-// If there is already a revocation for this public key that is newer, it is kept.
-func (e *Export) RevokeAt(pubKey string, timestamp time.Time) {
-	if e.Revocations == nil {
-		e.Revocations = RevocationList{}
-	}
-
-	e.Revocations.Revoke(pubKey, timestamp)
-}
-
-// ClearRevocation removes any revocation for the public key
-func (e *Export) ClearRevocation(pubKey string) {
-	e.Revocations.ClearRevocation(pubKey)
-}
-
-// IsRevokedAt checks if the public key is in the revoked list with a timestamp later than
-// the one passed in. Generally this method is called with time.Now() but other time's can
-// be used for testing.
-func (e *Export) IsRevokedAt(pubKey string, timestamp time.Time) bool {
-	return e.Revocations.IsRevoked(pubKey, timestamp)
-}
-
-// IsRevoked checks if the public key is in the revoked list with time.Now()
-func (e *Export) IsRevoked(pubKey string) bool {
-	return e.Revocations.IsRevoked(pubKey, time.Now())
-}
-
-// Exports is a slice of exports
-type Exports []*Export
-
-// Add appends exports to the list
-func (e *Exports) Add(i ...*Export) {
-	*e = append(*e, i...)
-}
-
-func isContainedIn(kind ExportType, subjects []Subject, vr *ValidationResults) {
-	m := make(map[string]string)
-	for i, ns := range subjects {
-		for j, s := range subjects {
-			if i == j {
-				continue
-			}
-			if ns.IsContainedIn(s) {
-				str := string(s)
-				_, ok := m[str]
-				if !ok {
-					m[str] = string(ns)
-				}
-			}
-		}
-	}
-
-	if len(m) != 0 {
-		for k, v := range m {
-			var vi ValidationIssue
-			vi.Blocking = true
-			vi.Description = fmt.Sprintf("%s export subject %q already exports %q", kind, k, v)
-			vr.Add(&vi)
-		}
-	}
-}
-
-// Validate calls validate on all of the exports
-func (e *Exports) Validate(vr *ValidationResults) error {
-	var serviceSubjects []Subject
-	var streamSubjects []Subject
-
-	for _, v := range *e {
-		if v.IsService() {
-			serviceSubjects = append(serviceSubjects, v.Subject)
-		} else {
-			streamSubjects = append(streamSubjects, v.Subject)
-		}
-		v.Validate(vr)
-	}
-
-	isContainedIn(Service, serviceSubjects, vr)
-	isContainedIn(Stream, streamSubjects, vr)
-
-	return nil
-}
-
-// HasExportContainingSubject checks if the export list has an export with the provided subject
-func (e *Exports) HasExportContainingSubject(subject Subject) bool {
-	for _, s := range *e {
-		if subject.IsContainedIn(s.Subject) {
-			return true
-		}
-	}
-	return false
-}
-
-func (e Exports) Len() int {
-	return len(e)
-}
-
-func (e Exports) Swap(i, j int) {
-	e[i], e[j] = e[j], e[i]
-}
-
-func (e Exports) Less(i, j int) bool {
-	return e[i].Subject < e[j].Subject
-}
diff --git a/vendor/github.com/nats-io/jwt/genericlaims.go b/vendor/github.com/nats-io/jwt/genericlaims.go
deleted file mode 100644
index 94cd86e0..00000000
--- a/vendor/github.com/nats-io/jwt/genericlaims.go
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2018 The NATS Authors
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package jwt
-
-import "github.com/nats-io/nkeys"
-
-// GenericClaims can be used to read a JWT as a map for any non-generic fields
-type GenericClaims struct {
-	ClaimsData
-	Data map[string]interface{} `json:"nats,omitempty"`
-}
-
-// NewGenericClaims creates a map-based Claims
-func NewGenericClaims(subject string) *GenericClaims {
-	if subject == "" {
-		return nil
-	}
-	c := GenericClaims{}
-	c.Subject = subject
-	c.Data = make(map[string]interface{})
-	return &c
-}
-
-// DecodeGeneric takes a JWT string and decodes it into a ClaimsData and map
-func DecodeGeneric(token string) (*GenericClaims, error) {
-	v := GenericClaims{}
-	if err := Decode(token, &v); err != nil {
-		return nil, err
-	}
-	return &v, nil
-}
-
-// Claims returns the standard part of the generic claim
-func (gc *GenericClaims) Claims() *ClaimsData {
-	return &gc.ClaimsData
-}
-
-// Payload returns the custom part of the claims data
-func (gc *GenericClaims) Payload() interface{} {
-	return &gc.Data
-}
-
-// Encode takes a generic claims and creates a JWT string
-func (gc *GenericClaims) Encode(pair nkeys.KeyPair) (string, error) {
-	return gc.ClaimsData.Encode(pair, gc)
-}
-
-// Validate checks the generic part of the claims data
-func (gc *GenericClaims) Validate(vr *ValidationResults) {
-	gc.ClaimsData.Validate(vr)
-}
-
-func (gc *GenericClaims) String() string {
-	return gc.ClaimsData.String(gc)
-}
-
-// ExpectedPrefixes returns the types allowed to encode a generic JWT, which is nil for all
-func (gc *GenericClaims) ExpectedPrefixes() []nkeys.PrefixByte {
-	return nil
-}
diff --git a/vendor/github.com/nats-io/jwt/go.mod b/vendor/github.com/nats-io/jwt/go.mod
deleted file mode 100644
index eebea6c2..00000000
--- a/vendor/github.com/nats-io/jwt/go.mod
+++ /dev/null
@@ -1,5 +0,0 @@
-module github.com/nats-io/jwt
-
-require github.com/nats-io/nkeys v0.1.4
-
-go 1.13
diff --git a/vendor/github.com/nats-io/jwt/go.sum b/vendor/github.com/nats-io/jwt/go.sum
deleted file mode 100644
index 5e6e47e0..00000000
--- a/vendor/github.com/nats-io/jwt/go.sum
+++ /dev/null
@@ -1,9 +0,0 @@
-github.com/nats-io/nkeys v0.1.4 h1:aEsHIssIk6ETN5m2/MD8Y4B2X7FfXrBAUdkyRvbVYzA=
-github.com/nats-io/nkeys v0.1.4/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-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/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/vendor/github.com/nats-io/jwt/header.go b/vendor/github.com/nats-io/jwt/header.go
deleted file mode 100644
index 27c65811..00000000
--- a/vendor/github.com/nats-io/jwt/header.go
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2018-2019 The NATS Authors
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package jwt
-
-import (
-	"encoding/json"
-	"fmt"
-	"strings"
-)
-
-const (
-	// Version is semantic version.
-	Version = "0.3.2"
-
-	// TokenTypeJwt is the JWT token type supported JWT tokens
-	// encoded and decoded by this library
-	TokenTypeJwt = "jwt"
-
-	// AlgorithmNkey is the algorithm supported by JWT tokens
-	// encoded and decoded by this library
-	AlgorithmNkey = "ed25519"
-)
-
-// Header is a JWT Jose Header
-type Header struct {
-	Type      string `json:"typ"`
-	Algorithm string `json:"alg"`
-}
-
-// Parses a header JWT token
-func parseHeaders(s string) (*Header, error) {
-	h, err := decodeString(s)
-	if err != nil {
-		return nil, err
-	}
-	header := Header{}
-	if err := json.Unmarshal(h, &header); err != nil {
-		return nil, err
-	}
-
-	if err := header.Valid(); err != nil {
-		return nil, err
-	}
-	return &header, nil
-}
-
-// Valid validates the Header. It returns nil if the Header is
-// a JWT header, and the algorithm used is the NKEY algorithm.
-func (h *Header) Valid() error {
-	if TokenTypeJwt != strings.ToLower(h.Type) {
-		return fmt.Errorf("not supported type %q", h.Type)
-	}
-
-	if AlgorithmNkey != strings.ToLower(h.Algorithm) {
-		return fmt.Errorf("unexpected %q algorithm", h.Algorithm)
-	}
-	return nil
-}
diff --git a/vendor/github.com/nats-io/jwt/imports.go b/vendor/github.com/nats-io/jwt/imports.go
deleted file mode 100644
index 8cd97479..00000000
--- a/vendor/github.com/nats-io/jwt/imports.go
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright 2018-2019 The NATS Authors
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package jwt
-
-import (
-	"io/ioutil"
-	"net/http"
-	"net/url"
-	"time"
-)
-
-// Import describes a mapping from another account into this one
-type Import struct {
-	Name string `json:"name,omitempty"`
-	// Subject field in an import is always from the perspective of the
-	// initial publisher - in the case of a stream it is the account owning
-	// the stream (the exporter), and in the case of a service it is the
-	// account making the request (the importer).
-	Subject Subject `json:"subject,omitempty"`
-	Account string  `json:"account,omitempty"`
-	Token   string  `json:"token,omitempty"`
-	// To field in an import is always from the perspective of the subscriber
-	// in the case of a stream it is the client of the stream (the importer),
-	// from the perspective of a service, it is the subscription waiting for
-	// requests (the exporter). If the field is empty, it will default to the
-	// value in the Subject field.
-	To   Subject    `json:"to,omitempty"`
-	Type ExportType `json:"type,omitempty"`
-}
-
-// IsService returns true if the import is of type service
-func (i *Import) IsService() bool {
-	return i.Type == Service
-}
-
-// IsStream returns true if the import is of type stream
-func (i *Import) IsStream() bool {
-	return i.Type == Stream
-}
-
-// Validate checks if an import is valid for the wrapping account
-func (i *Import) Validate(actPubKey string, vr *ValidationResults) {
-	if !i.IsService() && !i.IsStream() {
-		vr.AddError("invalid import type: %q", i.Type)
-	}
-
-	if i.Account == "" {
-		vr.AddWarning("account to import from is not specified")
-	}
-
-	i.Subject.Validate(vr)
-
-	if i.IsService() && i.Subject.HasWildCards() {
-		vr.AddError("services cannot have wildcard subject: %q", i.Subject)
-	}
-	if i.IsStream() && i.To.HasWildCards() {
-		vr.AddError("streams cannot have wildcard to subject: %q", i.Subject)
-	}
-
-	var act *ActivationClaims
-
-	if i.Token != "" {
-		// Check to see if its an embedded JWT or a URL.
-		if url, err := url.Parse(i.Token); err == nil && url.Scheme != "" {
-			c := &http.Client{Timeout: 5 * time.Second}
-			resp, err := c.Get(url.String())
-			if err != nil {
-				vr.AddWarning("import %s contains an unreachable token URL %q", i.Subject, i.Token)
-			}
-
-			if resp != nil {
-				defer resp.Body.Close()
-				body, err := ioutil.ReadAll(resp.Body)
-				if err != nil {
-					vr.AddWarning("import %s contains an unreadable token URL %q", i.Subject, i.Token)
-				} else {
-					act, err = DecodeActivationClaims(string(body))
-					if err != nil {
-						vr.AddWarning("import %s contains a url %q with an invalid activation token", i.Subject, i.Token)
-					}
-				}
-			}
-		} else {
-			var err error
-			act, err = DecodeActivationClaims(i.Token)
-			if err != nil {
-				vr.AddWarning("import %q contains an invalid activation token", i.Subject)
-			}
-		}
-	}
-
-	if act != nil {
-		if act.Issuer != i.Account {
-			vr.AddWarning("activation token doesn't match account for import %q", i.Subject)
-		}
-
-		if act.ClaimsData.Subject != actPubKey {
-			vr.AddWarning("activation token doesn't match account it is being included in, %q", i.Subject)
-		}
-	} else {
-		vr.AddWarning("no activation provided for import %s", i.Subject)
-	}
-
-}
-
-// Imports is a list of import structs
-type Imports []*Import
-
-// Validate checks if an import is valid for the wrapping account
-func (i *Imports) Validate(acctPubKey string, vr *ValidationResults) {
-	toSet := make(map[Subject]bool, len(*i))
-	for _, v := range *i {
-		if v.Type == Service {
-			if _, ok := toSet[v.To]; ok {
-				vr.AddError("Duplicate To subjects for %q", v.To)
-			}
-			toSet[v.To] = true
-		}
-		v.Validate(acctPubKey, vr)
-	}
-}
-
-// Add is a simple way to add imports
-func (i *Imports) Add(a ...*Import) {
-	*i = append(*i, a...)
-}
-
-func (i Imports) Len() int {
-	return len(i)
-}
-
-func (i Imports) Swap(j, k int) {
-	i[j], i[k] = i[k], i[j]
-}
-
-func (i Imports) Less(j, k int) bool {
-	return i[j].Subject < i[k].Subject
-}
diff --git a/vendor/github.com/nats-io/jwt/operator_claims.go b/vendor/github.com/nats-io/jwt/operator_claims.go
deleted file mode 100644
index 3c4d4a17..00000000
--- a/vendor/github.com/nats-io/jwt/operator_claims.go
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright 2018 The NATS Authors
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package jwt
-
-import (
-	"errors"
-	"fmt"
-	"net/url"
-	"strings"
-
-	"github.com/nats-io/nkeys"
-)
-
-// Operator specific claims
-type Operator struct {
-	// Slice of real identies (like websites) that can be used to identify the operator.
-	Identities []Identity `json:"identity,omitempty"`
-	// Slice of other operator NKeys that can be used to sign on behalf of the main
-	// operator identity.
-	SigningKeys StringList `json:"signing_keys,omitempty"`
-	// AccountServerURL is a partial URL like "https://host.domain.org:<port>/jwt/v1"
-	// tools will use the prefix and build queries by appending /accounts/<account_id>
-	// or /operator to the path provided. Note this assumes that the account server
-	// can handle requests in a nats-account-server compatible way. See
-	// https://github.com/nats-io/nats-account-server.
-	AccountServerURL string `json:"account_server_url,omitempty"`
-	// A list of NATS urls (tls://host:port) where tools can connect to the server
-	// using proper credentials.
-	OperatorServiceURLs StringList `json:"operator_service_urls,omitempty"`
-	// Identity of the system account
-	SystemAccount string `json:"system_account,omitempty"`
-}
-
-// Validate checks the validity of the operators contents
-func (o *Operator) Validate(vr *ValidationResults) {
-	if err := o.validateAccountServerURL(); err != nil {
-		vr.AddError(err.Error())
-	}
-
-	for _, v := range o.validateOperatorServiceURLs() {
-		if v != nil {
-			vr.AddError(v.Error())
-		}
-	}
-
-	for _, i := range o.Identities {
-		i.Validate(vr)
-	}
-
-	for _, k := range o.SigningKeys {
-		if !nkeys.IsValidPublicOperatorKey(k) {
-			vr.AddError("%s is not an operator public key", k)
-		}
-	}
-
-	if o.SystemAccount != "" {
-		if !nkeys.IsValidPublicAccountKey(o.SystemAccount) {
-			vr.AddError("%s is not an account public key", o.SystemAccount)
-		}
-	}
-}
-
-func (o *Operator) validateAccountServerURL() error {
-	if o.AccountServerURL != "" {
-		// We don't care what kind of URL it is so long as it parses
-		// and has a protocol. The account server may impose additional
-		// constraints on the type of URLs that it is able to notify to
-		u, err := url.Parse(o.AccountServerURL)
-		if err != nil {
-			return fmt.Errorf("error parsing account server url: %v", err)
-		}
-		if u.Scheme == "" {
-			return fmt.Errorf("account server url %q requires a protocol", o.AccountServerURL)
-		}
-	}
-	return nil
-}
-
-// ValidateOperatorServiceURL returns an error if the URL is not a valid NATS or TLS url.
-func ValidateOperatorServiceURL(v string) error {
-	// should be possible for the service url to not be expressed
-	if v == "" {
-		return nil
-	}
-	u, err := url.Parse(v)
-	if err != nil {
-		return fmt.Errorf("error parsing operator service url %q: %v", v, err)
-	}
-
-	if u.User != nil {
-		return fmt.Errorf("operator service url %q - credentials are not supported", v)
-	}
-
-	if u.Path != "" {
-		return fmt.Errorf("operator service url %q - paths are not supported", v)
-	}
-
-	lcs := strings.ToLower(u.Scheme)
-	switch lcs {
-	case "nats":
-		return nil
-	case "tls":
-		return nil
-	default:
-		return fmt.Errorf("operator service url %q - protocol not supported (only 'nats' or 'tls' only)", v)
-	}
-}
-
-func (o *Operator) validateOperatorServiceURLs() []error {
-	var errors []error
-	for _, v := range o.OperatorServiceURLs {
-		if v != "" {
-			if err := ValidateOperatorServiceURL(v); err != nil {
-				errors = append(errors, err)
-			}
-		}
-	}
-	return errors
-}
-
-// OperatorClaims define the data for an operator JWT
-type OperatorClaims struct {
-	ClaimsData
-	Operator `json:"nats,omitempty"`
-}
-
-// NewOperatorClaims creates a new operator claim with the specified subject, which should be an operator public key
-func NewOperatorClaims(subject string) *OperatorClaims {
-	if subject == "" {
-		return nil
-	}
-	c := &OperatorClaims{}
-	c.Subject = subject
-	return c
-}
-
-// DidSign checks the claims against the operator's public key and its signing keys
-func (oc *OperatorClaims) DidSign(op Claims) bool {
-	if op == nil {
-		return false
-	}
-	issuer := op.Claims().Issuer
-	if issuer == oc.Subject {
-		return true
-	}
-	return oc.SigningKeys.Contains(issuer)
-}
-
-// Deprecated: AddSigningKey, use claim.SigningKeys.Add()
-func (oc *OperatorClaims) AddSigningKey(pk string) {
-	oc.SigningKeys.Add(pk)
-}
-
-// Encode the claims into a JWT string
-func (oc *OperatorClaims) Encode(pair nkeys.KeyPair) (string, error) {
-	if !nkeys.IsValidPublicOperatorKey(oc.Subject) {
-		return "", errors.New("expected subject to be an operator public key")
-	}
-	err := oc.validateAccountServerURL()
-	if err != nil {
-		return "", err
-	}
-	oc.ClaimsData.Type = OperatorClaim
-	return oc.ClaimsData.Encode(pair, oc)
-}
-
-// DecodeOperatorClaims tries to create an operator claims from a JWt string
-func DecodeOperatorClaims(token string) (*OperatorClaims, error) {
-	v := OperatorClaims{}
-	if err := Decode(token, &v); err != nil {
-		return nil, err
-	}
-	return &v, nil
-}
-
-func (oc *OperatorClaims) String() string {
-	return oc.ClaimsData.String(oc)
-}
-
-// Payload returns the operator specific data for an operator JWT
-func (oc *OperatorClaims) Payload() interface{} {
-	return &oc.Operator
-}
-
-// Validate the contents of the claims
-func (oc *OperatorClaims) Validate(vr *ValidationResults) {
-	oc.ClaimsData.Validate(vr)
-	oc.Operator.Validate(vr)
-}
-
-// ExpectedPrefixes defines the nkey types that can sign operator claims, operator
-func (oc *OperatorClaims) ExpectedPrefixes() []nkeys.PrefixByte {
-	return []nkeys.PrefixByte{nkeys.PrefixByteOperator}
-}
-
-// Claims returns the generic claims data
-func (oc *OperatorClaims) Claims() *ClaimsData {
-	return &oc.ClaimsData
-}
diff --git a/vendor/github.com/nats-io/jwt/revocation_list.go b/vendor/github.com/nats-io/jwt/revocation_list.go
deleted file mode 100644
index fb1d8367..00000000
--- a/vendor/github.com/nats-io/jwt/revocation_list.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package jwt
-
-import (
-	"time"
-)
-
-// RevocationList is used to store a mapping of public keys to unix timestamps
-type RevocationList map[string]int64
-
-// Revoke enters a revocation by publickey and timestamp into this export
-// If there is already a revocation for this public key that is newer, it is kept.
-func (r RevocationList) Revoke(pubKey string, timestamp time.Time) {
-	newTS := timestamp.Unix()
-	if ts, ok := r[pubKey]; ok && ts > newTS {
-		return
-	}
-
-	r[pubKey] = newTS
-}
-
-// ClearRevocation removes any revocation for the public key
-func (r RevocationList) ClearRevocation(pubKey string) {
-	delete(r, pubKey)
-}
-
-// IsRevoked checks if the public key is in the revoked list with a timestamp later than
-// the one passed in. Generally this method is called with time.Now() but other time's can
-// be used for testing.
-func (r RevocationList) IsRevoked(pubKey string, timestamp time.Time) bool {
-	ts, ok := r[pubKey]
-	return ok && ts > timestamp.Unix()
-}
diff --git a/vendor/github.com/nats-io/jwt/server_claims.go b/vendor/github.com/nats-io/jwt/server_claims.go
deleted file mode 100644
index c18f167f..00000000
--- a/vendor/github.com/nats-io/jwt/server_claims.go
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2018 The NATS Authors
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package jwt
-
-import (
-	"errors"
-
-	"github.com/nats-io/nkeys"
-)
-
-// Server defines the custom part of a server jwt
-type Server struct {
-	Permissions
-	Cluster string `json:"cluster,omitempty"`
-}
-
-// Validate checks the cluster and permissions for a server JWT
-func (s *Server) Validate(vr *ValidationResults) {
-	if s.Cluster == "" {
-		vr.AddError("servers can't contain an empty cluster")
-	}
-}
-
-// ServerClaims defines the data in a server JWT
-type ServerClaims struct {
-	ClaimsData
-	Server `json:"nats,omitempty"`
-}
-
-// NewServerClaims creates a new server JWT with the specified subject/public key
-func NewServerClaims(subject string) *ServerClaims {
-	if subject == "" {
-		return nil
-	}
-	c := &ServerClaims{}
-	c.Subject = subject
-	return c
-}
-
-// Encode tries to turn the server claims into a JWT string
-func (s *ServerClaims) Encode(pair nkeys.KeyPair) (string, error) {
-	if !nkeys.IsValidPublicServerKey(s.Subject) {
-		return "", errors.New("expected subject to be a server public key")
-	}
-	s.ClaimsData.Type = ServerClaim
-	return s.ClaimsData.Encode(pair, s)
-}
-
-// DecodeServerClaims tries to parse server claims from a JWT string
-func DecodeServerClaims(token string) (*ServerClaims, error) {
-	v := ServerClaims{}
-	if err := Decode(token, &v); err != nil {
-		return nil, err
-	}
-	return &v, nil
-}
-
-func (s *ServerClaims) String() string {
-	return s.ClaimsData.String(s)
-}
-
-// Payload returns the server specific data
-func (s *ServerClaims) Payload() interface{} {
-	return &s.Server
-}
-
-// Validate checks the generic and server data in the server claims
-func (s *ServerClaims) Validate(vr *ValidationResults) {
-	s.ClaimsData.Validate(vr)
-	s.Server.Validate(vr)
-}
-
-// ExpectedPrefixes defines the types that can encode a server JWT, operator or cluster
-func (s *ServerClaims) ExpectedPrefixes() []nkeys.PrefixByte {
-	return []nkeys.PrefixByte{nkeys.PrefixByteOperator, nkeys.PrefixByteCluster}
-}
-
-// Claims returns the generic data
-func (s *ServerClaims) Claims() *ClaimsData {
-	return &s.ClaimsData
-}
diff --git a/vendor/github.com/nats-io/jwt/types.go b/vendor/github.com/nats-io/jwt/types.go
deleted file mode 100644
index e729c7eb..00000000
--- a/vendor/github.com/nats-io/jwt/types.go
+++ /dev/null
@@ -1,332 +0,0 @@
-/*
- * Copyright 2018-2019 The NATS Authors
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package jwt
-
-import (
-	"encoding/json"
-	"fmt"
-	"net"
-	"strings"
-	"time"
-)
-
-// ExportType defines the type of import/export.
-type ExportType int
-
-const (
-	// Unknown is used if we don't know the type
-	Unknown ExportType = iota
-	// Stream defines the type field value for a stream "stream"
-	Stream
-	// Service defines the type field value for a service "service"
-	Service
-)
-
-func (t ExportType) String() string {
-	switch t {
-	case Stream:
-		return "stream"
-	case Service:
-		return "service"
-	}
-	return "unknown"
-}
-
-// MarshalJSON marshals the enum as a quoted json string
-func (t *ExportType) MarshalJSON() ([]byte, error) {
-	switch *t {
-	case Stream:
-		return []byte("\"stream\""), nil
-	case Service:
-		return []byte("\"service\""), nil
-	}
-	return nil, fmt.Errorf("unknown export type")
-}
-
-// UnmarshalJSON unmashals a quoted json string to the enum value
-func (t *ExportType) UnmarshalJSON(b []byte) error {
-	var j string
-	err := json.Unmarshal(b, &j)
-	if err != nil {
-		return err
-	}
-	switch j {
-	case "stream":
-		*t = Stream
-		return nil
-	case "service":
-		*t = Service
-		return nil
-	}
-	return fmt.Errorf("unknown export type")
-}
-
-// Subject is a string that represents a NATS subject
-type Subject string
-
-// Validate checks that a subject string is valid, ie not empty and without spaces
-func (s Subject) Validate(vr *ValidationResults) {
-	v := string(s)
-	if v == "" {
-		vr.AddError("subject cannot be empty")
-	}
-	if strings.Contains(v, " ") {
-		vr.AddError("subject %q cannot have spaces", v)
-	}
-}
-
-// HasWildCards is used to check if a subject contains a > or *
-func (s Subject) HasWildCards() bool {
-	v := string(s)
-	return strings.HasSuffix(v, ".>") ||
-		strings.Contains(v, ".*.") ||
-		strings.HasSuffix(v, ".*") ||
-		strings.HasPrefix(v, "*.") ||
-		v == "*" ||
-		v == ">"
-}
-
-// IsContainedIn does a simple test to see if the subject is contained in another subject
-func (s Subject) IsContainedIn(other Subject) bool {
-	otherArray := strings.Split(string(other), ".")
-	myArray := strings.Split(string(s), ".")
-
-	if len(myArray) > len(otherArray) && otherArray[len(otherArray)-1] != ">" {
-		return false
-	}
-
-	if len(myArray) < len(otherArray) {
-		return false
-	}
-
-	for ind, tok := range otherArray {
-		myTok := myArray[ind]
-
-		if ind == len(otherArray)-1 && tok == ">" {
-			return true
-		}
-
-		if tok != myTok && tok != "*" {
-			return false
-		}
-	}
-
-	return true
-}
-
-// NamedSubject is the combination of a subject and a name for it
-type NamedSubject struct {
-	Name    string  `json:"name,omitempty"`
-	Subject Subject `json:"subject,omitempty"`
-}
-
-// Validate checks the subject
-func (ns *NamedSubject) Validate(vr *ValidationResults) {
-	ns.Subject.Validate(vr)
-}
-
-// TimeRange is used to represent a start and end time
-type TimeRange struct {
-	Start string `json:"start,omitempty"`
-	End   string `json:"end,omitempty"`
-}
-
-// Validate checks the values in a time range struct
-func (tr *TimeRange) Validate(vr *ValidationResults) {
-	format := "15:04:05"
-
-	if tr.Start == "" {
-		vr.AddError("time ranges start must contain a start")
-	} else {
-		_, err := time.Parse(format, tr.Start)
-		if err != nil {
-			vr.AddError("start in time range is invalid %q", tr.Start)
-		}
-	}
-
-	if tr.End == "" {
-		vr.AddError("time ranges end must contain an end")
-	} else {
-		_, err := time.Parse(format, tr.End)
-		if err != nil {
-			vr.AddError("end in time range is invalid %q", tr.End)
-		}
-	}
-}
-
-// Limits are used to control acccess for users and importing accounts
-// Src is a comma separated list of CIDR specifications
-type Limits struct {
-	Max     int64       `json:"max,omitempty"`
-	Payload int64       `json:"payload,omitempty"`
-	Src     string      `json:"src,omitempty"`
-	Times   []TimeRange `json:"times,omitempty"`
-}
-
-// Validate checks the values in a limit struct
-func (l *Limits) Validate(vr *ValidationResults) {
-	if l.Max < 0 {
-		vr.AddError("limits cannot contain a negative maximum, %d", l.Max)
-	}
-	if l.Payload < 0 {
-		vr.AddError("limits cannot contain a negative payload, %d", l.Payload)
-	}
-
-	if l.Src != "" {
-		elements := strings.Split(l.Src, ",")
-
-		for _, cidr := range elements {
-			cidr = strings.TrimSpace(cidr)
-			_, ipNet, err := net.ParseCIDR(cidr)
-			if err != nil || ipNet == nil {
-				vr.AddError("invalid cidr %q in user src limits", cidr)
-			}
-		}
-	}
-
-	if l.Times != nil && len(l.Times) > 0 {
-		for _, t := range l.Times {
-			t.Validate(vr)
-		}
-	}
-}
-
-// Permission defines allow/deny subjects
-type Permission struct {
-	Allow StringList `json:"allow,omitempty"`
-	Deny  StringList `json:"deny,omitempty"`
-}
-
-// Validate the allow, deny elements of a permission
-func (p *Permission) Validate(vr *ValidationResults) {
-	for _, subj := range p.Allow {
-		Subject(subj).Validate(vr)
-	}
-	for _, subj := range p.Deny {
-		Subject(subj).Validate(vr)
-	}
-}
-
-// ResponsePermission can be used to allow responses to any reply subject
-// that is received on a valid subscription.
-type ResponsePermission struct {
-	MaxMsgs int           `json:"max"`
-	Expires time.Duration `json:"ttl"`
-}
-
-// Validate the response permission.
-func (p *ResponsePermission) Validate(vr *ValidationResults) {
-	// Any values can be valid for now.
-}
-
-// Permissions are used to restrict subject access, either on a user or for everyone on a server by default
-type Permissions struct {
-	Pub  Permission          `json:"pub,omitempty"`
-	Sub  Permission          `json:"sub,omitempty"`
-	Resp *ResponsePermission `json:"resp,omitempty"`
-}
-
-// Validate the pub and sub fields in the permissions list
-func (p *Permissions) Validate(vr *ValidationResults) {
-	if p.Resp != nil {
-		p.Resp.Validate(vr)
-	}
-}
-
-// StringList is a wrapper for an array of strings
-type StringList []string
-
-// Contains returns true if the list contains the string
-func (u *StringList) Contains(p string) bool {
-	for _, t := range *u {
-		if t == p {
-			return true
-		}
-	}
-	return false
-}
-
-// Add appends 1 or more strings to a list
-func (u *StringList) Add(p ...string) {
-	for _, v := range p {
-		if !u.Contains(v) && v != "" {
-			*u = append(*u, v)
-		}
-	}
-}
-
-// Remove removes 1 or more strings from a list
-func (u *StringList) Remove(p ...string) {
-	for _, v := range p {
-		for i, t := range *u {
-			if t == v {
-				a := *u
-				*u = append(a[:i], a[i+1:]...)
-				break
-			}
-		}
-	}
-}
-
-// TagList is a unique array of lower case strings
-// All tag list methods lower case the strings in the arguments
-type TagList []string
-
-// Contains returns true if the list contains the tags
-func (u *TagList) Contains(p string) bool {
-	p = strings.ToLower(p)
-	for _, t := range *u {
-		if t == p {
-			return true
-		}
-	}
-	return false
-}
-
-// Add appends 1 or more tags to a list
-func (u *TagList) Add(p ...string) {
-	for _, v := range p {
-		v = strings.ToLower(v)
-		if !u.Contains(v) && v != "" {
-			*u = append(*u, v)
-		}
-	}
-}
-
-// Remove removes 1 or more tags from a list
-func (u *TagList) Remove(p ...string) {
-	for _, v := range p {
-		v = strings.ToLower(v)
-		for i, t := range *u {
-			if t == v {
-				a := *u
-				*u = append(a[:i], a[i+1:]...)
-				break
-			}
-		}
-	}
-}
-
-// Identity is used to associate an account or operator with a real entity
-type Identity struct {
-	ID    string `json:"id,omitempty"`
-	Proof string `json:"proof,omitempty"`
-}
-
-// Validate checks the values in an Identity
-func (u *Identity) Validate(vr *ValidationResults) {
-	//Fixme identity validation
-}
diff --git a/vendor/github.com/nats-io/jwt/user_claims.go b/vendor/github.com/nats-io/jwt/user_claims.go
deleted file mode 100644
index 78fe6a95..00000000
--- a/vendor/github.com/nats-io/jwt/user_claims.go
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright 2018-2019 The NATS Authors
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package jwt
-
-import (
-	"errors"
-
-	"github.com/nats-io/nkeys"
-)
-
-// User defines the user specific data in a user JWT
-type User struct {
-	Permissions
-	Limits
-	BearerToken bool `json:"bearer_token,omitempty"`
-}
-
-// Validate checks the permissions and limits in a User jwt
-func (u *User) Validate(vr *ValidationResults) {
-	u.Permissions.Validate(vr)
-	u.Limits.Validate(vr)
-	// When BearerToken is true server will ignore any nonce-signing verification
-}
-
-// UserClaims defines a user JWT
-type UserClaims struct {
-	ClaimsData
-	User `json:"nats,omitempty"`
-	// IssuerAccount stores the public key for the account the issuer represents.
-	// When set, the claim was issued by a signing key.
-	IssuerAccount string `json:"issuer_account,omitempty"`
-}
-
-// NewUserClaims creates a user JWT with the specific subject/public key
-func NewUserClaims(subject string) *UserClaims {
-	if subject == "" {
-		return nil
-	}
-	c := &UserClaims{}
-	c.Subject = subject
-	return c
-}
-
-// Encode tries to turn the user claims into a JWT string
-func (u *UserClaims) Encode(pair nkeys.KeyPair) (string, error) {
-	if !nkeys.IsValidPublicUserKey(u.Subject) {
-		return "", errors.New("expected subject to be user public key")
-	}
-	u.ClaimsData.Type = UserClaim
-	return u.ClaimsData.Encode(pair, u)
-}
-
-// DecodeUserClaims tries to parse a user claims from a JWT string
-func DecodeUserClaims(token string) (*UserClaims, error) {
-	v := UserClaims{}
-	if err := Decode(token, &v); err != nil {
-		return nil, err
-	}
-	return &v, nil
-}
-
-// Validate checks the generic and specific parts of the user jwt
-func (u *UserClaims) Validate(vr *ValidationResults) {
-	u.ClaimsData.Validate(vr)
-	u.User.Validate(vr)
-	if u.IssuerAccount != "" && !nkeys.IsValidPublicAccountKey(u.IssuerAccount) {
-		vr.AddError("account_id is not an account public key")
-	}
-}
-
-// ExpectedPrefixes defines the types that can encode a user JWT, account
-func (u *UserClaims) ExpectedPrefixes() []nkeys.PrefixByte {
-	return []nkeys.PrefixByte{nkeys.PrefixByteAccount}
-}
-
-// Claims returns the generic data from a user jwt
-func (u *UserClaims) Claims() *ClaimsData {
-	return &u.ClaimsData
-}
-
-// Payload returns the user specific data from a user JWT
-func (u *UserClaims) Payload() interface{} {
-	return &u.User
-}
-
-func (u *UserClaims) String() string {
-	return u.ClaimsData.String(u)
-}
-
-// IsBearerToken returns true if nonce-signing requirements should be skipped
-func (u *UserClaims) IsBearerToken() bool {
-	return u.BearerToken
-}
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 975cbf6e..0a9b9615 100644
--- a/vendor/github.com/nats-io/jwt/v2/account_claims.go
+++ b/vendor/github.com/nats-io/jwt/v2/account_claims.go
@@ -88,7 +88,6 @@ func (o *OperatorLimits) Validate(_ *ValidationResults) {
 type Account struct {
 	Imports            Imports        `json:"imports,omitempty"`
 	Exports            Exports        `json:"exports,omitempty"`
-	Identities         []Identity     `json:"identity,omitempty"`
 	Limits             OperatorLimits `json:"limits,omitempty"`
 	SigningKeys        StringList     `json:"signing_keys,omitempty"`
 	Revocations        RevocationList `json:"revocations,omitempty"`
@@ -103,10 +102,6 @@ func (a *Account) Validate(acct *AccountClaims, vr *ValidationResults) {
 	a.Limits.Validate(vr)
 	a.DefaultPermissions.Validate(vr)
 
-	for _, i := range a.Identities {
-		i.Validate(vr)
-	}
-
 	if !a.Limits.IsEmpty() && a.Limits.Imports >= 0 && int64(len(a.Imports)) > a.Limits.Imports {
 		vr.AddError("the account contains more imports than allowed by the operator")
 	}
@@ -199,9 +194,6 @@ func (a *AccountClaims) Validate(vr *ValidationResults) {
 	a.Account.Validate(a, vr)
 
 	if nkeys.IsValidPublicAccountKey(a.ClaimsData.Issuer) {
-		if len(a.Identities) > 0 {
-			vr.AddWarning("self-signed account JWTs shouldn't contain identity proofs")
-		}
 		if !a.Limits.IsEmpty() {
 			vr.AddWarning("self-signed account JWTs shouldn't contain operator limits")
 		}
diff --git a/vendor/github.com/nats-io/jwt/v2/decoder_account.go b/vendor/github.com/nats-io/jwt/v2/decoder_account.go
index 60215b23..2471b889 100644
--- a/vendor/github.com/nats-io/jwt/v2/decoder_account.go
+++ b/vendor/github.com/nats-io/jwt/v2/decoder_account.go
@@ -21,10 +21,9 @@ import (
 )
 
 type v1NatsAccount struct {
-	Imports    Imports    `json:"imports,omitempty"`
-	Exports    Exports    `json:"exports,omitempty"`
-	Identities []Identity `json:"identity,omitempty"`
-	Limits     struct {
+	Imports Imports `json:"imports,omitempty"`
+	Exports Exports `json:"exports,omitempty"`
+	Limits  struct {
 		NatsLimits
 		AccountLimits
 	} `json:"limits,omitempty"`
@@ -71,11 +70,11 @@ func (oa v1AccountClaims) migrateV1() (*AccountClaims, error) {
 	// copy the account data
 	a.Account.Imports = oa.v1NatsAccount.Imports
 	a.Account.Exports = oa.v1NatsAccount.Exports
-	a.Account.Identities = oa.v1NatsAccount.Identities
 	a.Account.Limits.AccountLimits = oa.v1NatsAccount.Limits.AccountLimits
 	a.Account.Limits.NatsLimits = oa.v1NatsAccount.Limits.NatsLimits
 	a.Account.Limits.JetStreamLimits = JetStreamLimits{0, 0, 0, 0}
 	a.Account.SigningKeys = oa.v1NatsAccount.SigningKeys
 	a.Account.Revocations = oa.v1NatsAccount.Revocations
+	a.Version = 1
 	return &a, nil
 }
diff --git a/vendor/github.com/nats-io/jwt/v2/decoder_activation.go b/vendor/github.com/nats-io/jwt/v2/decoder_activation.go
index c4f1f5fc..d10dbf57 100644
--- a/vendor/github.com/nats-io/jwt/v2/decoder_activation.go
+++ b/vendor/github.com/nats-io/jwt/v2/decoder_activation.go
@@ -72,5 +72,6 @@ func (oa v1ActivationClaims) migrateV1() (*ActivationClaims, error) {
 	// copy the activation data
 	a.ImportSubject = oa.ImportSubject
 	a.ImportType = oa.ImportType
+	a.Version = 1
 	return &a, nil
 }
diff --git a/vendor/github.com/nats-io/jwt/v2/decoder_operator.go b/vendor/github.com/nats-io/jwt/v2/decoder_operator.go
index 42efbc16..511b204c 100644
--- a/vendor/github.com/nats-io/jwt/v2/decoder_operator.go
+++ b/vendor/github.com/nats-io/jwt/v2/decoder_operator.go
@@ -21,7 +21,6 @@ import (
 )
 
 type v1NatsOperator struct {
-	Identities          []Identity `json:"identity,omitempty"`
 	SigningKeys         StringList `json:"signing_keys,omitempty"`
 	AccountServerURL    string     `json:"account_server_url,omitempty"`
 	OperatorServiceURLs StringList `json:"operator_service_urls,omitempty"`
@@ -65,10 +64,10 @@ func (oa v1OperatorClaims) migrateV1() (*OperatorClaims, error) {
 	a.Operator.Type = oa.v1ClaimsDataDeletedFields.Type
 	a.Operator.Tags = oa.v1ClaimsDataDeletedFields.Tags
 	// copy the account data
-	a.Operator.Identities = oa.v1NatsOperator.Identities
 	a.Operator.SigningKeys = oa.v1NatsOperator.SigningKeys
 	a.Operator.AccountServerURL = oa.v1NatsOperator.AccountServerURL
 	a.Operator.OperatorServiceURLs = oa.v1NatsOperator.OperatorServiceURLs
 	a.Operator.SystemAccount = oa.v1NatsOperator.SystemAccount
+	a.Version = 1
 	return &a, nil
 }
diff --git a/vendor/github.com/nats-io/jwt/v2/decoder_user.go b/vendor/github.com/nats-io/jwt/v2/decoder_user.go
index 2bd84d6d..e3e53c41 100644
--- a/vendor/github.com/nats-io/jwt/v2/decoder_user.go
+++ b/vendor/github.com/nats-io/jwt/v2/decoder_user.go
@@ -18,8 +18,6 @@ package jwt
 import (
 	"encoding/json"
 	"fmt"
-
-	"github.com/nats-io/jwt"
 )
 
 type v1User struct {
@@ -45,7 +43,7 @@ func loadUser(data []byte, version int) (*UserClaims, error) {
 	switch version {
 	case 1:
 		var v1a v1UserClaims
-		v1a.Limits = Limits{NatsLimits: NatsLimits{jwt.NoLimit, jwt.NoLimit, jwt.NoLimit}}
+		v1a.Limits = Limits{NatsLimits: NatsLimits{NoLimit, NoLimit, NoLimit}}
 		v1a.Max = NoLimit
 		if err := json.Unmarshal(data, &v1a); err != nil {
 			return nil, err
@@ -78,5 +76,6 @@ func (oa v1UserClaims) migrateV1() (*UserClaims, error) {
 	u.User.Permissions = oa.v1User.Permissions
 	u.User.Limits = oa.v1User.Limits
 	u.User.BearerToken = oa.v1User.BearerToken
+	u.Version = 1
 	return &u, nil
 }
diff --git a/vendor/github.com/nats-io/jwt/v2/genericlaims.go b/vendor/github.com/nats-io/jwt/v2/genericlaims.go
index ca3c5f75..9a657282 100644
--- a/vendor/github.com/nats-io/jwt/v2/genericlaims.go
+++ b/vendor/github.com/nats-io/jwt/v2/genericlaims.go
@@ -122,9 +122,9 @@ func (gc *GenericClaims) ClaimType() ClaimType {
 			}
 		}
 	}
-	ct, ctok := v.(ClaimType)
+	ct, ctok := v.(string)
 	if ctok {
-		return ct
+		return ClaimType(ct)
 	}
 	return ""
 }
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 b9ddce06..a41d9c04 100644
--- a/vendor/github.com/nats-io/jwt/v2/operator_claims.go
+++ b/vendor/github.com/nats-io/jwt/v2/operator_claims.go
@@ -27,8 +27,6 @@ import (
 
 // Operator specific claims
 type Operator struct {
-	// Slice of real identies (like websites) that can be used to identify the operator.
-	Identities []Identity `json:"identity,omitempty"`
 	// Slice of other operator NKeys that can be used to sign on behalf of the main
 	// operator identity.
 	SigningKeys StringList `json:"signing_keys,omitempty"`
@@ -80,10 +78,6 @@ func (o *Operator) Validate(vr *ValidationResults) {
 		}
 	}
 
-	for _, i := range o.Identities {
-		i.Validate(vr)
-	}
-
 	for _, k := range o.SigningKeys {
 		if !nkeys.IsValidPublicOperatorKey(k) {
 			vr.AddError("%s is not an operator public key", k)
diff --git a/vendor/github.com/nats-io/jwt/v2/types.go b/vendor/github.com/nats-io/jwt/v2/types.go
index 5f172f51..34a3209d 100644
--- a/vendor/github.com/nats-io/jwt/v2/types.go
+++ b/vendor/github.com/nats-io/jwt/v2/types.go
@@ -350,14 +350,3 @@ func (c *CIDRList) UnmarshalJSON(body []byte) (err error) {
 		return err
 	}
 }
-
-// Identity is used to associate an account or operator with a real entity
-type Identity struct {
-	ID    string `json:"id,omitempty"`
-	Proof string `json:"proof,omitempty"`
-}
-
-// Validate checks the values in an Identity
-func (u *Identity) Validate(_ *ValidationResults) {
-	//Fixme identity validation
-}
diff --git a/vendor/github.com/nats-io/jwt/v2/user_claims.go b/vendor/github.com/nats-io/jwt/v2/user_claims.go
index f22eb7ba..ad4a261e 100644
--- a/vendor/github.com/nats-io/jwt/v2/user_claims.go
+++ b/vendor/github.com/nats-io/jwt/v2/user_claims.go
@@ -21,11 +21,18 @@ import (
 	"github.com/nats-io/nkeys"
 )
 
+const (
+	ConnectionTypeStandard  = "STANDARD"
+	ConnectionTypeWebsocket = "WEBSOCKET"
+	ConnectionTypeLeafnode  = "LEAFNODE"
+)
+
 // User defines the user specific data in a user JWT
 type User struct {
 	Permissions
 	Limits
-	BearerToken bool `json:"bearer_token,omitempty"`
+	BearerToken            bool       `json:"bearer_token,omitempty"`
+	AllowedConnectionTypes StringList `json:"allowed_connection_types,omitempty"`
 	// IssuerAccount stores the public key for the account the issuer represents.
 	// When set, the claim was issued by a signing key.
 	IssuerAccount string `json:"issuer_account,omitempty"`
diff --git a/vendor/github.com/nats-io/jwt/validation.go b/vendor/github.com/nats-io/jwt/validation.go
deleted file mode 100644
index c87a9922..00000000
--- a/vendor/github.com/nats-io/jwt/validation.go
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright 2018 The NATS Authors
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package jwt
-
-import (
-	"errors"
-	"fmt"
-)
-
-// ValidationIssue represents an issue during JWT validation, it may or may not be a blocking error
-type ValidationIssue struct {
-	Description string
-	Blocking    bool
-	TimeCheck   bool
-}
-
-func (ve *ValidationIssue) Error() string {
-	return ve.Description
-}
-
-// ValidationResults is a list of ValidationIssue pointers
-type ValidationResults struct {
-	Issues []*ValidationIssue
-}
-
-// CreateValidationResults creates an empty list of validation issues
-func CreateValidationResults() *ValidationResults {
-	issues := []*ValidationIssue{}
-	return &ValidationResults{
-		Issues: issues,
-	}
-}
-
-//Add appends an issue to the list
-func (v *ValidationResults) Add(vi *ValidationIssue) {
-	v.Issues = append(v.Issues, vi)
-}
-
-// AddError creates a new validation error and adds it to the list
-func (v *ValidationResults) AddError(format string, args ...interface{}) {
-	v.Add(&ValidationIssue{
-		Description: fmt.Sprintf(format, args...),
-		Blocking:    true,
-		TimeCheck:   false,
-	})
-}
-
-// AddTimeCheck creates a new validation issue related to a time check and adds it to the list
-func (v *ValidationResults) AddTimeCheck(format string, args ...interface{}) {
-	v.Add(&ValidationIssue{
-		Description: fmt.Sprintf(format, args...),
-		Blocking:    false,
-		TimeCheck:   true,
-	})
-}
-
-// AddWarning creates a new validation warning and adds it to the list
-func (v *ValidationResults) AddWarning(format string, args ...interface{}) {
-	v.Add(&ValidationIssue{
-		Description: fmt.Sprintf(format, args...),
-		Blocking:    false,
-		TimeCheck:   false,
-	})
-}
-
-// IsBlocking returns true if the list contains a blocking error
-func (v *ValidationResults) IsBlocking(includeTimeChecks bool) bool {
-	for _, i := range v.Issues {
-		if i.Blocking {
-			return true
-		}
-
-		if includeTimeChecks && i.TimeCheck {
-			return true
-		}
-	}
-	return false
-}
-
-// IsEmpty returns true if the list is empty
-func (v *ValidationResults) IsEmpty() bool {
-	return len(v.Issues) == 0
-}
-
-// Errors returns only blocking issues as errors
-func (v *ValidationResults) Errors() []error {
-	var errs []error
-	for _, v := range v.Issues {
-		if v.Blocking {
-			errs = append(errs, errors.New(v.Description))
-		}
-	}
-	return errs
-}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 0149ffb0..4703d75a 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1,8 +1,6 @@
 # github.com/minio/highwayhash v1.0.0
 github.com/minio/highwayhash
-# github.com/nats-io/jwt v0.3.3-0.20200519195258-f2bf5ce574c7
-github.com/nats-io/jwt
-# github.com/nats-io/jwt/v2 v2.0.0-20200827232814-292806fa48ba
+# github.com/nats-io/jwt/v2 v2.0.0-20200916203241-1f8ce17dff02
 github.com/nats-io/jwt/v2
 # github.com/nats-io/nats.go v1.10.1-0.20200606002146-fc6fed82929a
 github.com/nats-io/nats.go


Test patch:
diff --git a/server/auth_test.go b/server/auth_test.go
index a0bc7920..031c70ec 100644
--- a/server/auth_test.go
+++ b/server/auth_test.go
@@ -15,7 +15,10 @@ package server
 
 import (
 	"reflect"
+	"strings"
 	"testing"
+
+	"github.com/nats-io/jwt/v2"
 )
 
 func TestUserCloneNilPermissions(t *testing.T) {
@@ -101,3 +104,53 @@ func TestUserCloneNil(t *testing.T) {
 		t.Fatalf("Expected nil, got: %+v", clone)
 	}
 }
+
+func TestUserUnknownAllowedConnectionType(t *testing.T) {
+	o := DefaultOptions()
+	o.Users = []*User{&User{
+		Username:               "user",
+		Password:               "pwd",
+		AllowedConnectionTypes: testCreateAllowedConnectionTypes([]string{jwt.ConnectionTypeStandard, "someNewType"}),
+	}}
+	_, err := NewServer(o)
+	if err == nil || !strings.Contains(err.Error(), "connection type") {
+		t.Fatalf("Expected error about unknown connection type, got %v", err)
+	}
+
+	o.Users[0].AllowedConnectionTypes = testCreateAllowedConnectionTypes([]string{"websocket"})
+	s, err := NewServer(o)
+	if err != nil {
+		t.Fatalf("Unexpected error: %v", err)
+	}
+	s.mu.Lock()
+	user := s.opts.Users[0]
+	s.mu.Unlock()
+	for act := range user.AllowedConnectionTypes {
+		if act != jwt.ConnectionTypeWebsocket {
+			t.Fatalf("Expected map to have been updated with proper case, got %v", act)
+		}
+	}
+	// Same with NKey user now.
+	o.Users = nil
+	o.Nkeys = []*NkeyUser{&NkeyUser{
+		Nkey:                   "somekey",
+		AllowedConnectionTypes: testCreateAllowedConnectionTypes([]string{jwt.ConnectionTypeStandard, "someNewType"}),
+	}}
+	_, err = NewServer(o)
+	if err == nil || !strings.Contains(err.Error(), "connection type") {
+		t.Fatalf("Expected error about unknown connection type, got %v", err)
+	}
+	o.Nkeys[0].AllowedConnectionTypes = testCreateAllowedConnectionTypes([]string{"websocket"})
+	s, err = NewServer(o)
+	if err != nil {
+		t.Fatalf("Unexpected error: %v", err)
+	}
+	s.mu.Lock()
+	nkey := s.opts.Nkeys[0]
+	s.mu.Unlock()
+	for act := range nkey.AllowedConnectionTypes {
+		if act != jwt.ConnectionTypeWebsocket {
+			t.Fatalf("Expected map to have been updated with proper case, got %v", act)
+		}
+	}
+}
diff --git a/server/config_check_test.go b/server/config_check_test.go
index 9c5418df..e6929146 100644
--- a/server/config_check_test.go
+++ b/server/config_check_test.go
@@ -1318,6 +1318,74 @@ func TestConfigCheck(t *testing.T) {
 			errorLine: 3,
 			errorPos:  20,
 		},
+		{
+			name: "connection types wrong type",
+			config: `
+                   authorization {
+                       users [
+                           {user: a, password: pwd, allowed_connection_types: 123}
+					   ]
+				   }
+			`,
+			err:       errors.New(`error parsing allowed connection types: unsupported type int64`),
+			errorLine: 4,
+			errorPos:  53,
+		},
+		{
+			name: "connection types content wrong type",
+			config: `
+                   authorization {
+                       users [
+                           {user: a, password: pwd, allowed_connection_types: [
+                               123
+                               WEBSOCKET
+							]}
+					   ]
+				   }
+			`,
+			err:       errors.New(`error parsing allowed connection types: unsupported type in array int64`),
+			errorLine: 5,
+			errorPos:  32,
+		},
+		{
+			name: "connection types type unknown",
+			config: `
+                   authorization {
+                       users [
+                           {user: a, password: pwd, allowed_connection_types: [ "UNKNOWN" ]}
+					   ]
+				   }
+			`,
+			err:       fmt.Errorf("invalid connection types [%q]", "UNKNOWN"),
+			errorLine: 4,
+			errorPos:  53,
+		},
+		{
+			name: "websocket auth unknown var",
+			config: `
+				websocket {
+					authorization {
+                        unknown: "field"
+				   }
+				}
+			`,
+			err:       fmt.Errorf("unknown field %q", "unknown"),
+			errorLine: 4,
+			errorPos:  25,
+		},
+		{
+			name: "websocket bad tls",
+			config: `
+				websocket {
+                    tls {
+						cert_file: "configs/certs/server.pem"
+					}
+				}
+			`,
+			err:       fmt.Errorf("missing 'key_file' in TLS configuration"),
+			errorLine: 3,
+			errorPos:  21,
+		},
 	}
 
 	checkConfig := func(config string) error {
diff --git a/server/events_test.go b/server/events_test.go
index e410cfda..01180acd 100644
--- a/server/events_test.go
+++ b/server/events_test.go
@@ -216,7 +216,24 @@ func TestSystemAccountNewConnection(t *testing.T) {
 	if err != nil {
 		t.Fatalf("Error receiving msg: %v", err)
 	}
-
+	connsMsg, err := sub.NextMsg(time.Second)
+	if err != nil {
+		t.Fatalf("Error receiving msg: %v", err)
+	}
+	if strings.HasPrefix(msg.Subject, fmt.Sprintf("$SYS.ACCOUNT.%s.SERVER.CONNS", acc2.Name)) {
+		msg, connsMsg = connsMsg, msg
+	}
+	if !strings.HasPrefix(connsMsg.Subject, fmt.Sprintf("$SYS.ACCOUNT.%s.SERVER.CONNS", acc2.Name)) {
+		t.Fatalf("Expected subject to start with %q, got %q", "$SYS.ACCOUNT.<account>.CONNECT", msg.Subject)
+	}
+	conns := AccountNumConns{}
+	if err := json.Unmarshal(connsMsg.Data, &conns); err != nil {
+		t.Fatalf("Error unmarshalling conns event message: %v", err)
+	} else if conns.Account != acc2.Name {
+		t.Fatalf("Wrong account in conns message: %v", conns)
+	} else if conns.Conns != 1 || conns.TotalConns != 1 || conns.LeafNodes != 0 {
+		t.Fatalf("Wrong counts in conns message: %v", conns)
+	}
 	if !strings.HasPrefix(msg.Subject, fmt.Sprintf("$SYS.ACCOUNT.%s.CONNECT", acc2.Name)) {
 		t.Fatalf("Expected subject to start with %q, got %q", "$SYS.ACCOUNT.<account>.CONNECT", msg.Subject)
 	}
@@ -272,7 +289,26 @@ func TestSystemAccountNewConnection(t *testing.T) {
 	if err != nil {
 		t.Fatalf("Error receiving msg: %v", err)
 	}
-
+	connsMsg, err = sub.NextMsg(time.Second)
+	if err != nil {
+		t.Fatalf("Error receiving msg: %v", err)
+	}
+	if strings.HasPrefix(msg.Subject, fmt.Sprintf("$SYS.ACCOUNT.%s.SERVER.CONNS", acc2.Name)) {
+		msg, connsMsg = connsMsg, msg
+	}
+	if !strings.HasPrefix(connsMsg.Subject, fmt.Sprintf("$SYS.ACCOUNT.%s.SERVER.CONNS", acc2.Name)) {
+		t.Fatalf("Expected subject to start with %q, got %q", "$SYS.ACCOUNT.<account>.CONNECT", msg.Subject)
+	} else if !strings.Contains(string(connsMsg.Data), `"total_conns": 0`) {
+		t.Fatalf("Expected event to reflect created connection, got: %s", string(msg.Data))
+	}
+	conns = AccountNumConns{}
+	if err := json.Unmarshal(connsMsg.Data, &conns); err != nil {
+		t.Fatalf("Error unmarshalling conns event message: %v", err)
+	} else if conns.Account != acc2.Name {
+		t.Fatalf("Wrong account in conns message: %v", conns)
+	} else if conns.Conns != 0 || conns.TotalConns != 0 || conns.LeafNodes != 0 {
+		t.Fatalf("Wrong counts in conns message: %v", conns)
+	}
 	if !strings.HasPrefix(msg.Subject, fmt.Sprintf("$SYS.ACCOUNT.%s.DISCONNECT", acc2.Name)) {
 		t.Fatalf("Expected subject to start with %q, got %q", "$SYS.ACCOUNT.<account>.DISCONNECT", msg.Subject)
 	}
@@ -722,7 +758,7 @@ func TestSystemAccountConnectionUpdatesStopAfterNoLocal(t *testing.T) {
 		copy := append([]byte(nil), msg...)
 		received <- &nats.Msg{Subject: subject, Reply: reply, Data: copy}
 	}
-	subj := fmt.Sprintf(accConnsEventSubj, pub)
+	subj := fmt.Sprintf(accConnsEventSubjOld, pub)
 	sub, err := sa.sysSubscribe(subj, cb)
 	if sub == nil || err != nil {
 		t.Fatalf("Expected to subscribe, got %v", err)
@@ -1354,7 +1390,7 @@ func TestSystemAccountWithGateways(t *testing.T) {
 
 	// If this tests fails with wrong number after 10 seconds we may have
 	// added a new inititial subscription for the eventing system.
-	checkExpectedSubs(t, 26, sa)
+	checkExpectedSubs(t, 27, sa)
 
 	// Create a client on B and see if we receive the event
 	urlb := fmt.Sprintf("nats://%s:%d", ob.Host, ob.Port)
@@ -1365,18 +1401,32 @@ func TestSystemAccountWithGateways(t *testing.T) {
 	if err != nil {
 		t.Fatalf("Error receiving msg: %v", err)
 	}
+	connsMsgA, err := sub.NextMsg(time.Second)
+	if err != nil {
+		t.Fatalf("Error receiving msg: %v", err)
+	}
+	connsMsgG, err := sub.NextMsg(time.Second)
+	if err != nil {
+		t.Fatalf("Error receiving msg: %v", err)
+	}
+	if strings.HasSuffix(connsMsgA.Subject, ".CONNECT") {
+		msg, connsMsgA = connsMsgA, msg
+	} else if strings.HasSuffix(connsMsgG.Subject, ".CONNECT") {
+		msg, connsMsgG = connsMsgG, msg
+	}
+	if connsMsgG.Subject != "$SYS.ACCOUNT.$G.SERVER.CONNS" {
+		connsMsgA, connsMsgG = connsMsgG, connsMsgA
+	}
+	if connsMsgG.Subject != "$SYS.ACCOUNT.$G.SERVER.CONNS" {
+		t.Fatalf("Expected subject $SYS.ACCOUNT.$G.SERVER.CONNS but got %s", connsMsgG.Subject)
+	}
 	// Basic checks, could expand on that...
 	accName := sa.SystemAccount().Name
-	if !strings.HasPrefix(msg.Subject, fmt.Sprintf("$SYS.ACCOUNT.%s.CONNECT", accName)) {
-		t.Fatalf("Expected subject to start with %q, got %q", "$SYS.ACCOUNT.<account>.CONNECT", msg.Subject)
+	if connsMsgA.Subject != fmt.Sprintf("$SYS.ACCOUNT.%s.SERVER.CONNS", accName) {
+		t.Fatalf("Expected subject to be $SYS.ACCOUNT.%s.SERVER.CONNS but got: %s", accName, connsMsgA.Subject)
 	}
-	tokens := strings.Split(msg.Subject, ".")
-	if len(tokens) < 4 {
-		t.Fatalf("Expected 4 tokens, got %d", len(tokens))
-	}
-	account := tokens[2]
-	if account != accName {
-		t.Fatalf("Expected %q for account, got %q", accName, account)
+	if msg.Subject != fmt.Sprintf("$SYS.ACCOUNT.%s.CONNECT", accName) {
+		t.Fatalf("Expected subject to be $SYS.ACCOUNT.%s.CONNECT but got: %s", accName, msg.Subject)
 	}
 }
 func TestServerEventsStatsZ(t *testing.T) {
@@ -1853,7 +1903,7 @@ func TestConnectionUpdatesTimerProperlySet(t *testing.T) {
 	cb := func(sub *subscription, _ *client, subject, reply string, msg []byte) {
 		atomic.AddInt32(&count, 1)
 	}
-	subj := fmt.Sprintf(accConnsEventSubj, pub)
+	subj := fmt.Sprintf(accConnsEventSubjOld, pub)
 	sub, err := sa.sysSubscribe(subj, cb)
 	if sub == nil || err != nil {
 		t.Fatalf("Expected to subscribe, got %v", err)
diff --git a/server/websocket_test.go b/server/websocket_test.go
index b27dd83b..1b03a72d 100644
--- a/server/websocket_test.go
+++ b/server/websocket_test.go
@@ -1420,6 +1420,68 @@ func TestWSParseOptions(t *testing.T) {
 				}
 				return nil
 			}, ""},
+		{"compression",
+			`
+			websocket {
+				compression: true
+			}
+			`, func(wo *WebsocketOpts) error {
+				if !wo.Compression {
+					return fmt.Errorf("Compression should have been set")
+				}
+				return nil
+			}, ""},
+		{"jwt cookie",
+			`
+			websocket {
+				jwt_cookie: "jwtcookie"
+			}
+			`, func(wo *WebsocketOpts) error {
+				if wo.JWTCookie != "jwtcookie" {
+					return fmt.Errorf("Invalid JWTCookie value: %q", wo.JWTCookie)
+				}
+				return nil
+			}, ""},
+		{"no auth user",
+			`
+			websocket {
+				no_auth_user: "noauthuser"
+			}
+			`, func(wo *WebsocketOpts) error {
+				if wo.NoAuthUser != "noauthuser" {
+					return fmt.Errorf("Invalid NoAuthUser value: %q", wo.NoAuthUser)
+				}
+				return nil
+			}, ""},
+		{"auth block",
+			`
+			websocket {
+				authorization {
+					user: "webuser"
+					password: "pwd"
+					token: "token"
+					timeout: 2.0
+				}
+			}
+			`, func(wo *WebsocketOpts) error {
+				if wo.Username != "webuser" || wo.Password != "pwd" || wo.Token != "token" || wo.AuthTimeout != 2.0 {
+					return fmt.Errorf("Invalid auth block: %+v", wo)
+				}
+				return nil
+			}, ""},
+		{"auth timeout as int",
+			`
+			websocket {
+				authorization {
+					timeout: 2
+				}
+			}
+			`, func(wo *WebsocketOpts) error {
+				if wo.AuthTimeout != 2.0 {
+					return fmt.Errorf("Invalid auth timeout: %v", wo.AuthTimeout)
+				}
+				return nil
+			}, ""},
 	} {
 		t.Run(test.name, func(t *testing.T) {
 			conf := createConfFile(t, []byte(test.content))
@@ -1460,6 +1522,19 @@ func TestWSValidateOptions(t *testing.T) {
 			o.Websocket.JWTCookie = "jwt"
 			return o
 		}, "keys configuration is required"},
+		{"websocket username not allowed if users specified", func() *Options {
+			o := wso.Clone()
+			o.Nkeys = []*NkeyUser{&NkeyUser{Nkey: "abc"}}
+			o.Websocket.Username = "b"
+			o.Websocket.Password = "pwd"
+			return o
+		}, "websocket authentication username not compatible with presence of users/nkeys"},
+		{"websocket token not allowed if users specified", func() *Options {
+			o := wso.Clone()
+			o.Nkeys = []*NkeyUser{&NkeyUser{Nkey: "abc"}}
+			o.Websocket.Token = "mytoken"
+			return o
+		}, "websocket authentication token not compatible with presence of users/nkeys"},
 	} {
 		t.Run(test.name, func(t *testing.T) {
 			err := validateWebsocketOptions(test.getOpts())
@@ -1595,6 +1670,7 @@ type testWSClientOptions struct {
 	host          string
 	port          int
 	extraHeaders  map[string]string
+	noTLS         bool
 }
 
 func testNewWSClient(t testing.TB, o testWSClientOptions) (net.Conn, *bufio.Reader, []byte) {
@@ -1604,9 +1680,11 @@ func testNewWSClient(t testing.TB, o testWSClientOptions) (net.Conn, *bufio.Read
 	if err != nil {
 		t.Fatalf("Error creating ws connection: %v", err)
 	}
-	wsc = tls.Client(wsc, &tls.Config{InsecureSkipVerify: true})
-	if err := wsc.(*tls.Conn).Handshake(); err != nil {
-		t.Fatalf("Error during handshake: %v", err)
+	if !o.noTLS {
+		wsc = tls.Client(wsc, &tls.Config{InsecureSkipVerify: true})
+		if err := wsc.(*tls.Conn).Handshake(); err != nil {
+			t.Fatalf("Error during handshake: %v", err)
+		}
 	}
 	req := testWSCreateValidReq()
 	if o.compress {
@@ -1991,6 +2069,17 @@ func TestWSTLSVerifyClientCert(t *testing.T) {
 	}
 }
 
+func testCreateAllowedConnectionTypes(list []string) map[string]struct{} {
+	if len(list) == 0 {
+		return nil
+	}
+	m := make(map[string]struct{}, len(list))
+	for _, l := range list {
+		m[l] = struct{}{}
+	}
+	return m
+}
+
 func TestWSTLSVerifyAndMap(t *testing.T) {
 	accName := "MyAccount"
 	acc := NewAccount(accName)
@@ -1999,21 +2088,20 @@ func TestWSTLSVerifyAndMap(t *testing.T) {
 
 	for _, test := range []struct {
 		name        string
-		wsUsers     bool
+		filtering   bool
 		provideCert bool
 	}{
-		{"no users override, client provides cert", false, true},
-		{"no users override, client does not provide cert", false, false},
-		{"sers override, client provides cert", true, true},
-		{"users override, client does not provide cert", true, false},
+		{"no filtering, client provides cert", false, true},
+		{"no filtering, client does not provide cert", false, false},
+		{"filtering, client provides cert", true, true},
+		{"filtering, client does not provide cert", true, false},
 	} {
 		t.Run(test.name, func(t *testing.T) {
 			o := testWSOptions()
 			o.Accounts = []*Account{acc}
-			if test.wsUsers {
-				o.Websocket.Users = users
-			} else {
-				o.Users = users
+			o.Users = users
+			if test.filtering {
+				o.Users[0].AllowedConnectionTypes = testCreateAllowedConnectionTypes([]string{jwt.ConnectionTypeStandard, jwt.ConnectionTypeWebsocket})
 			}
 			tc := &TLSConfigOpts{
 				CertFile: "../test/configs/certs/tlsauth/server.pem",
@@ -2967,7 +3055,57 @@ func TestWSTokenAuth(t *testing.T) {
 	}
 }
 
+func TestWSBindToProperAccount(t *testing.T) {
+	conf := createConfFile(t, []byte(fmt.Sprintf(`
+		listen: "127.0.0.1:-1"
+		accounts {
+			a {
+				users [
+					{user: a, password: pwd, allowed_connection_types: ["%s", "%s"]}
+				]
+			}
+			b {
+				users [
+					{user: b, password: pwd}
+				]
+			}
+		}
+		websocket {
+			listen: "127.0.0.1:-1"
+			no_tls: true
+		}
+	`, jwt.ConnectionTypeStandard, strings.ToLower(jwt.ConnectionTypeWebsocket)))) // on purpose use lower case to ensure that it is converted.
+	defer os.Remove(conf)
+	s, o := RunServerWithConfig(conf)
+	defer s.Shutdown()
+
+	nc := natsConnect(t, fmt.Sprintf("nats://a:pwd@127.0.0.1:%d", o.Port))
+	defer nc.Close()
+
+	sub := natsSubSync(t, nc, "foo")
+
+	wsc, br, _ := testNewWSClient(t, testWSClientOptions{host: o.Websocket.Host, port: o.Websocket.Port, noTLS: true})
+	// Send CONNECT and PING
+	wsmsg := testWSCreateClientMsg(wsBinaryMessage, 1, true, false,
+		[]byte(fmt.Sprintf("CONNECT {\"verbose\":false,\"protocol\":1,\"user\":\"%s\",\"pass\":\"%s\"}\r\nPING\r\n", "a", "pwd")))
+	if _, err := wsc.Write(wsmsg); err != nil {
+		t.Fatalf("Error sending message: %v", err)
+	}
+	// Wait for the PONG
+	if msg := testWSReadFrame(t, br); !bytes.HasPrefix(msg, []byte("PONG\r\n")) {
+		t.Fatalf("Expected PONG, got %s", msg)
+	}
+
+	wsmsg = testWSCreateClientMsg(wsBinaryMessage, 1, true, false, []byte("PUB foo 7\r\nfrom ws\r\n"))
+	if _, err := wsc.Write(wsmsg); err != nil {
+		t.Fatalf("Error sending message: %v", err)
+	}
+
+	natsNexMsg(t, sub, time.Second)
+}
+
 func TestWSUsersAuth(t *testing.T) {
+	users := []*User{&User{Username: "user", Password: "pwd"}}
 	for _, test := range []struct {
 		name string
 		opts func() *Options
@@ -2976,84 +3114,53 @@ func TestWSUsersAuth(t *testing.T) {
 		err  string
 	}{
 		{
-			"top level auth, no override, wrong user",
-			func() *Options {
-				o := testWSOptions()
-				o.Users = []*User{
-					&User{Username: "normal1", Password: "client1"},
-					&User{Username: "normal2", Password: "client2"},
-				}
-				return o
-			},
-			"websocket", "client", "-ERR 'Authorization Violation'",
-		},
-		{
-			"top level auth, no override, correct user",
+			"no filtering, wrong user",
 			func() *Options {
 				o := testWSOptions()
-				o.Users = []*User{
-					&User{Username: "normal1", Password: "client1"},
-					&User{Username: "normal2", Password: "client2"},
-				}
+				o.Users = users
 				return o
 			},
-			"normal2", "client2", "",
+			"wronguser", "pwd", "-ERR 'Authorization Violation'",
 		},
 		{
-			"no top level auth, ws auth, wrong user",
+			"no filtering, correct user",
 			func() *Options {
 				o := testWSOptions()
-				o.Websocket.Users = []*User{
-					&User{Username: "websocket1", Password: "client1"},
-					&User{Username: "websocket2", Password: "client2"},
-				}
+				o.Users = users
 				return o
 			},
-			"websocket", "client", "-ERR 'Authorization Violation'",
+			"user", "pwd", "",
 		},
 		{
-			"no top level auth, ws auth, correct token",
+			"filering, user not allowed",
 			func() *Options {
 				o := testWSOptions()
-				o.Websocket.Users = []*User{
-					&User{Username: "websocket1", Password: "client1"},
-					&User{Username: "websocket2", Password: "client2"},
-				}
+				o.Users = users
+				// Only allowed for regular clients
+				o.Users[0].AllowedConnectionTypes = testCreateAllowedConnectionTypes([]string{jwt.ConnectionTypeStandard})
 				return o
 			},
-			"websocket1", "client1", "",
+			"user", "pwd", "-ERR 'Authorization Violation'",
 		},
 		{
-			"top level auth, ws override, wrong user",
+			"filtering, user allowed",
 			func() *Options {
 				o := testWSOptions()
-				o.Users = []*User{
-					&User{Username: "normal1", Password: "client1"},
-					&User{Username: "normal2", Password: "client2"},
-				}
-				o.Websocket.Users = []*User{
-					&User{Username: "websocket1", Password: "client1"},
-					&User{Username: "websocket2", Password: "client2"},
-				}
+				o.Users = users
+				o.Users[0].AllowedConnectionTypes = testCreateAllowedConnectionTypes([]string{jwt.ConnectionTypeStandard, jwt.ConnectionTypeWebsocket})
 				return o
 			},
-			"normal2", "client2", "-ERR 'Authorization Violation'",
+			"user", "pwd", "",
 		},
 		{
-			"top level auth, ws override, correct token",
+			"filtering, wrong password",
 			func() *Options {
 				o := testWSOptions()
-				o.Users = []*User{
-					&User{Username: "normal1", Password: "client1"},
-					&User{Username: "normal2", Password: "client2"},
-				}
-				o.Websocket.Users = []*User{
-					&User{Username: "websocket1", Password: "client1"},
-					&User{Username: "websocket2", Password: "client2"},
-				}
+				o.Users = users
+				o.Users[0].AllowedConnectionTypes = testCreateAllowedConnectionTypes([]string{jwt.ConnectionTypeStandard, jwt.ConnectionTypeWebsocket})
 				return o
 			},
-			"websocket2", "client2", "",
+			"user", "badpassword", "-ERR 'Authorization Violation'",
 		},
 	} {
 		t.Run(test.name, func(t *testing.T) {
@@ -3082,31 +3189,29 @@ func TestWSUsersAuth(t *testing.T) {
 }
 
 func TestWSNoAuthUserValidation(t *testing.T) {
-	// It is illegal to configure a websocket's NoAuthUser if websocket's
-	// auth config does not have a matching User.
-	// Create regular clients's User to make sure that we fail even if
-	// the websocket's NoAuthUser is found in opts.Users.
 	o := testWSOptions()
 	o.Users = []*User{&User{Username: "user", Password: "pwd"}}
-	o.Websocket.NoAuthUser = "user"
-	if _, err := NewServer(o); err == nil || !strings.Contains(err.Error(), "users are not") {
-		t.Fatalf("Expected error saying that users are not configured, got %v", err)
+	// Should fail because it is not part of o.Users.
+	o.Websocket.NoAuthUser = "notfound"
+	if _, err := NewServer(o); err == nil || !strings.Contains(err.Error(), "not present as user") {
+		t.Fatalf("Expected error saying not present as user, got %v", err)
 	}
-
-	o.Websocket.Users = []*User{&User{Username: "wsuser", Password: "pwd"}}
+	// Set a valid no auth user for global options, but still should fail because
+	// of o.Websocket.NoAuthUser
+	o.NoAuthUser = "user"
 	o.Websocket.NoAuthUser = "notfound"
-	if _, err := NewServer(o); err == nil || !strings.Contains(err.Error(), "not found") {
-		t.Fatalf("Expected error saying no auth user not found, got %v", err)
+	if _, err := NewServer(o); err == nil || !strings.Contains(err.Error(), "not present as user") {
+		t.Fatalf("Expected error saying not present as user, got %v", err)
 	}
 }
 
 func TestWSNoAuthUser(t *testing.T) {
 	for _, test := range []struct {
-		name          string
-		createWSUsers bool
-		useAuth       bool
-		expectedUser  string
-		expectedAcc   string
+		name         string
+		override     bool
+		useAuth      bool
+		expectedUser string
+		expectedAcc  string
 	}{
 		{"no override, no user provided", false, false, "noauth", "normal"},
 		{"no override, user povided", false, true, "user", "normal"},
@@ -3121,13 +3226,11 @@ func TestWSNoAuthUser(t *testing.T) {
 			o.Users = []*User{
 				&User{Username: "noauth", Password: "pwd", Account: normalAcc},
 				&User{Username: "user", Password: "pwd", Account: normalAcc},
+				&User{Username: "wsnoauth", Password: "pwd", Account: websocketAcc},
+				&User{Username: "wsuser", Password: "pwd", Account: websocketAcc},
 			}
 			o.NoAuthUser = "noauth"
-			if test.createWSUsers {
-				o.Websocket.Users = []*User{
-					&User{Username: "wsnoauth", Password: "pwd", Account: websocketAcc},
-					&User{Username: "wsuser", Password: "pwd", Account: websocketAcc},
-				}
+			if test.override {
 				o.Websocket.NoAuthUser = "wsnoauth"
 			}
 			s := RunServer(o)
@@ -3177,6 +3280,9 @@ func TestWSNkeyAuth(t *testing.T) {
 	wsnkp, _ := nkeys.CreateUser()
 	wspub, _ := wsnkp.PublicKey()
 
+	badkp, _ := nkeys.CreateUser()
+	badpub, _ := badkp.PublicKey()
+
 	for _, test := range []struct {
 		name string
 		opts func() *Options
@@ -3185,16 +3291,16 @@ func TestWSNkeyAuth(t *testing.T) {
 		err  string
 	}{
 		{
-			"top level auth, no override, wrong nkey",
+			"no filtering, wrong nkey",
 			func() *Options {
 				o := testWSOptions()
 				o.Nkeys = []*NkeyUser{&NkeyUser{Nkey: pub}}
 				return o
 			},
-			wspub, wsnkp, "-ERR 'Authorization Violation'",
+			badpub, badkp, "-ERR 'Authorization Violation'",
 		},
 		{
-			"top level auth, no override, correct nkey",
+			"no filtering, correct nkey",
 			func() *Options {
 				o := testWSOptions()
 				o.Nkeys = []*NkeyUser{&NkeyUser{Nkey: pub}}
@@ -3203,42 +3309,51 @@ func TestWSNkeyAuth(t *testing.T) {
 			pub, nkp, "",
 		},
 		{
-			"no top level auth, ws auth, wrong nkey",
+			"filtering, nkey not allowed",
 			func() *Options {
 				o := testWSOptions()
-				o.Websocket.Nkeys = []*NkeyUser{&NkeyUser{Nkey: wspub}}
+				o.Nkeys = []*NkeyUser{
+					&NkeyUser{
+						Nkey:                   pub,
+						AllowedConnectionTypes: testCreateAllowedConnectionTypes([]string{jwt.ConnectionTypeStandard}),
+					},
+					&NkeyUser{
+						Nkey:                   wspub,
+						AllowedConnectionTypes: testCreateAllowedConnectionTypes([]string{jwt.ConnectionTypeWebsocket}),
+					},
+				}
 				return o
 			},
 			pub, nkp, "-ERR 'Authorization Violation'",
 		},
 		{
-			"no top level auth, ws auth, correct nkey",
+			"filtering, correct nkey",
 			func() *Options {
 				o := testWSOptions()
-				o.Websocket.Nkeys = []*NkeyUser{&NkeyUser{Nkey: wspub}}
+				o.Nkeys = []*NkeyUser{
+					&NkeyUser{Nkey: pub},
+					&NkeyUser{
+						Nkey:                   wspub,
+						AllowedConnectionTypes: testCreateAllowedConnectionTypes([]string{jwt.ConnectionTypeStandard, jwt.ConnectionTypeWebsocket}),
+					},
+				}
 				return o
 			},
 			wspub, wsnkp, "",
 		},
 		{
-			"top level auth, ws override, wrong nkey",
+			"filtering, wrong nkey",
 			func() *Options {
 				o := testWSOptions()
-				o.Nkeys = []*NkeyUser{&NkeyUser{Nkey: pub}}
-				o.Websocket.Nkeys = []*NkeyUser{&NkeyUser{Nkey: wspub}}
-				return o
-			},
-			pub, nkp, "-ERR 'Authorization Violation'",
-		},
-		{
-			"top level auth, ws override, correct nkey",
-			func() *Options {
-				o := testWSOptions()
-				o.Nkeys = []*NkeyUser{&NkeyUser{Nkey: pub}}
-				o.Websocket.Nkeys = []*NkeyUser{&NkeyUser{Nkey: wspub}}
+				o.Nkeys = []*NkeyUser{
+					&NkeyUser{
+						Nkey:                   wspub,
+						AllowedConnectionTypes: testCreateAllowedConnectionTypes([]string{jwt.ConnectionTypeStandard, jwt.ConnectionTypeWebsocket}),
+					},
+				}
 				return o
 			},
-			wspub, wsnkp, "",
+			badpub, badkp, "-ERR 'Authorization Violation'",
 		},
 	} {
 		t.Run(test.name, func(t *testing.T) {
@@ -3271,7 +3386,37 @@ func TestWSNkeyAuth(t *testing.T) {
 	}
 }
 
-func TestJWTWSCookieUser(t *testing.T) {
+func TestWSJWTWithAllowedConnectionTypes(t *testing.T) {
+	o := testWSOptions()
+	setupAddTrusted(o)
+	s := RunServer(o)
+	buildMemAccResolver(s)
+	defer s.Shutdown()
+
+	for _, test := range []struct {
+		name            string
+		connectionTypes []string
+		expectedAnswer  string
+	}{
+		{"not allowed", []string{jwt.ConnectionTypeStandard}, "-ERR"},
+		{"allowed", []string{jwt.ConnectionTypeStandard, strings.ToLower(jwt.ConnectionTypeWebsocket)}, "+OK"},
+		{"allowed with unknown", []string{jwt.ConnectionTypeWebsocket, "SomeNewType"}, "+OK"},
+		{"not allowed with unknown", []string{"SomeNewType"}, "-ERR"},
+	} {
+		t.Run(test.name, func(t *testing.T) {
+			nuc := newJWTTestUserClaims()
+			nuc.AllowedConnectionTypes = test.connectionTypes
+			claimOpt := testClaimsOptions{
+				nuc:          nuc,
+				expectAnswer: test.expectedAnswer,
+			}
+			_, c, _, _ := testWSWithClaims(t, s, testWSClientOptions{host: o.Websocket.Host, port: o.Websocket.Port}, claimOpt)
+			c.Close()
+		})
+	}
+}
+
+func TestWSJWTCookieUser(t *testing.T) {
 
 	nucSigFunc := func() *jwt.UserClaims { return newJWTTestUserClaims() }
 	nucBearerFunc := func() *jwt.UserClaims {
diff --git a/test/jetstream_test.go b/test/jetstream_test.go
index 8280932b..c319837f 100644
--- a/test/jetstream_test.go
+++ b/test/jetstream_test.go
@@ -2497,6 +2497,7 @@ func TestJetStreamPublishDeDupe(t *testing.T) {
 	s.Shutdown()
 	// Restart.
 	s = RunJetStreamServerOnPort(-1, sd)
+	defer s.Shutdown()
 
 	nc = clientConnectToServer(t, s)
 	defer nc.Close()