Repository: nats-io/nats-server
Pull request #1574 (2020-08-28 03:14:33)

Update JWT and incorporate change of cidr ranges from string to array
updates jwt library. This fixes an issue where limits where initialized wrong. (issue was caused by v1 to v2 conversion)

Signed-off-by: Matthias Hanel <mh@synadia.com>




Patch:
diff --git a/go.mod b/go.mod
index e307d1e0..4fa5d648 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-20200820224411-1e751ff168ab
+	github.com/nats-io/jwt/v2 v2.0.0-20200827232814-292806fa48ba
 	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 78b93404..029d1375 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-20200820224411-1e751ff168ab h1:jxoj9jYzAFyXKFeNO6LjBf6ro5p8uXnTIfHKSMIi6dI=
-github.com/nats-io/jwt/v2 v2.0.0-20200820224411-1e751ff168ab/go.mod h1:vs+ZEjP+XKy8szkBmQwCB7RjYdIlMaPsFPs4VdS4bTQ=
+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/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/jwt.go b/server/jwt.go
index f66237fa..d0fbf4f2 100644
--- a/server/jwt.go
+++ b/server/jwt.go
@@ -146,7 +146,7 @@ func validateTrustedOperators(o *Options) error {
 func validateSrc(claims *jwt.UserClaims, host string) bool {
 	if claims == nil {
 		return false
-	} else if claims.Src == "" {
+	} else if len(claims.Src) == 0 {
 		return true
 	} else if host == "" {
 		return false
@@ -155,7 +155,7 @@ func validateSrc(claims *jwt.UserClaims, host string) bool {
 	if ip == nil {
 		return false
 	}
-	for _, cidr := range strings.Split(claims.Src, ",") {
+	for _, cidr := range claims.Src {
 		if _, net, err := net.ParseCIDR(cidr); err != nil {
 			return false // should not happen as this jwt is invalid
 		} else if net.Contains(ip) {
diff --git a/vendor/github.com/nats-io/jwt/.gitignore b/vendor/github.com/nats-io/jwt/.gitignore
new file mode 100644
index 00000000..7117a678
--- /dev/null
+++ b/vendor/github.com/nats-io/jwt/.gitignore
@@ -0,0 +1,16 @@
+# 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
new file mode 100644
index 00000000..50e27a6b
--- /dev/null
+++ b/vendor/github.com/nats-io/jwt/.travis.yml
@@ -0,0 +1,22 @@
+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
new file mode 100644
index 00000000..261eeb9e
--- /dev/null
+++ b/vendor/github.com/nats-io/jwt/LICENSE
@@ -0,0 +1,201 @@
+                                 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
new file mode 100644
index 00000000..95e468e1
--- /dev/null
+++ b/vendor/github.com/nats-io/jwt/Makefile
@@ -0,0 +1,19 @@
+.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
new file mode 100644
index 00000000..d3cb88ac
--- /dev/null
+++ b/vendor/github.com/nats-io/jwt/README.md
@@ -0,0 +1,54 @@
+# 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
new file mode 100644
index 00000000..500965ea
--- /dev/null
+++ b/vendor/github.com/nats-io/jwt/ReleaseNotes.md
@@ -0,0 +1,5 @@
+# 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
new file mode 100644
index 00000000..945bd987
--- /dev/null
+++ b/vendor/github.com/nats-io/jwt/account_claims.go
@@ -0,0 +1,222 @@
+/*
+ * 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
new file mode 100644
index 00000000..99228a75
--- /dev/null
+++ b/vendor/github.com/nats-io/jwt/activation_claims.go
@@ -0,0 +1,166 @@
+/*
+ * 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
new file mode 100644
index 00000000..d402bcc5
--- /dev/null
+++ b/vendor/github.com/nats-io/jwt/claims.go
@@ -0,0 +1,302 @@
+/*
+ * 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
new file mode 100644
index 00000000..bbfcf06f
--- /dev/null
+++ b/vendor/github.com/nats-io/jwt/cluster_claims.go
@@ -0,0 +1,94 @@
+/*
+ * 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
new file mode 100644
index 00000000..265057f1
--- /dev/null
+++ b/vendor/github.com/nats-io/jwt/creds_utils.go
@@ -0,0 +1,203 @@
+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
new file mode 100644
index 00000000..5578f988
--- /dev/null
+++ b/vendor/github.com/nats-io/jwt/exports.go
@@ -0,0 +1,236 @@
+/*
+ * 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
new file mode 100644
index 00000000..94cd86e0
--- /dev/null
+++ b/vendor/github.com/nats-io/jwt/genericlaims.go
@@ -0,0 +1,73 @@
+/*
+ * 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
new file mode 100644
index 00000000..eebea6c2
--- /dev/null
+++ b/vendor/github.com/nats-io/jwt/go.mod
@@ -0,0 +1,5 @@
+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
new file mode 100644
index 00000000..5e6e47e0
--- /dev/null
+++ b/vendor/github.com/nats-io/jwt/go.sum
@@ -0,0 +1,9 @@
+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
new file mode 100644
index 00000000..27c65811
--- /dev/null
+++ b/vendor/github.com/nats-io/jwt/header.go
@@ -0,0 +1,71 @@
+/*
+ * 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
new file mode 100644
index 00000000..8cd97479
--- /dev/null
+++ b/vendor/github.com/nats-io/jwt/imports.go
@@ -0,0 +1,151 @@
+/*
+ * 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
new file mode 100644
index 00000000..3c4d4a17
--- /dev/null
+++ b/vendor/github.com/nats-io/jwt/operator_claims.go
@@ -0,0 +1,212 @@
+/*
+ * 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
new file mode 100644
index 00000000..fb1d8367
--- /dev/null
+++ b/vendor/github.com/nats-io/jwt/revocation_list.go
@@ -0,0 +1,32 @@
+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
new file mode 100644
index 00000000..c18f167f
--- /dev/null
+++ b/vendor/github.com/nats-io/jwt/server_claims.go
@@ -0,0 +1,94 @@
+/*
+ * 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
new file mode 100644
index 00000000..e729c7eb
--- /dev/null
+++ b/vendor/github.com/nats-io/jwt/types.go
@@ -0,0 +1,332 @@
+/*
+ * 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
new file mode 100644
index 00000000..78fe6a95
--- /dev/null
+++ b/vendor/github.com/nats-io/jwt/user_claims.go
@@ -0,0 +1,106 @@
+/*
+ * 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 4fc19c40..975cbf6e 100644
--- a/vendor/github.com/nats-io/jwt/v2/account_claims.go
+++ b/vendor/github.com/nats-io/jwt/v2/account_claims.go
@@ -30,24 +30,24 @@ type AccountLimits struct {
 	Imports         int64 `json:"imports,omitempty"`   // Max number of imports
 	Exports         int64 `json:"exports,omitempty"`   // Max number of exports
 	WildcardExports bool  `json:"wildcards,omitempty"` // Are wildcards allowed in exports
+	Conn            int64 `json:"conn,omitempty"`      // Max number of active connections
+	LeafNodeConn    int64 `json:"leaf,omitempty"`      // Max number of active leaf node connections
 }
 
 // IsUnlimited returns true if all limits are unlimited
 func (a *AccountLimits) IsUnlimited() bool {
-	return *a == AccountLimits{NoLimit, NoLimit, true}
+	return *a == AccountLimits{NoLimit, NoLimit, true, NoLimit, NoLimit}
 }
 
 type NatsLimits 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
-	Data         int64 `json:"data,omitempty"`    // Max number of bytes
-	Payload      int64 `json:"payload,omitempty"` // Max message payload
+	Subs    int64 `json:"subs,omitempty"`    // Max number of subscriptions
+	Data    int64 `json:"data,omitempty"`    // Max number of bytes
+	Payload int64 `json:"payload,omitempty"` // Max message payload
 }
 
 // IsUnlimited returns true if all limits are unlimited
 func (n *NatsLimits) IsUnlimited() bool {
-	return *n == NatsLimits{NoLimit, NoLimit, NoLimit, NoLimit, NoLimit}
+	return *n == NatsLimits{NoLimit, NoLimit, NoLimit}
 }
 
 type JetStreamLimits struct {
@@ -153,8 +153,8 @@ func NewAccountClaims(subject string) *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{
-		NatsLimits{NoLimit, NoLimit, NoLimit, NoLimit, NoLimit},
-		AccountLimits{NoLimit, NoLimit, true},
+		NatsLimits{NoLimit, NoLimit, NoLimit},
+		AccountLimits{NoLimit, NoLimit, true, NoLimit, NoLimit},
 		JetStreamLimits{NoLimit, NoLimit, NoLimit, NoLimit}}
 	c.Subject = subject
 	return c
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 c362c6a2..2bd84d6d 100644
--- a/vendor/github.com/nats-io/jwt/v2/decoder_user.go
+++ b/vendor/github.com/nats-io/jwt/v2/decoder_user.go
@@ -18,6 +18,8 @@ package jwt
 import (
 	"encoding/json"
 	"fmt"
+
+	"github.com/nats-io/jwt"
 )
 
 type v1User struct {
@@ -43,6 +45,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.Max = NoLimit
 		if err := json.Unmarshal(data, &v1a); err != nil {
 			return nil, err
diff --git a/vendor/github.com/nats-io/jwt/v2/go.mod b/vendor/github.com/nats-io/jwt/v2/go.mod
index ef7dcdcb..420392a3 100644
--- a/vendor/github.com/nats-io/jwt/v2/go.mod
+++ b/vendor/github.com/nats-io/jwt/v2/go.mod
@@ -3,7 +3,6 @@ module github.com/nats-io/jwt/v2
 require (
 	github.com/nats-io/jwt v0.3.2
 	github.com/nats-io/nkeys v0.2.0
-	github.com/stretchr/testify v1.6.1
 )
 
 replace github.com/nats-io/jwt v0.3.2 => ../
diff --git a/vendor/github.com/nats-io/jwt/v2/go.sum b/vendor/github.com/nats-io/jwt/v2/go.sum
index 66c4e826..05ef1fb0 100644
--- a/vendor/github.com/nats-io/jwt/v2/go.sum
+++ b/vendor/github.com/nats-io/jwt/v2/go.sum
@@ -1,12 +1,5 @@
-github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/nats-io/nkeys v0.2.0 h1:WXKF7diOaPU9cJdLD7nuzwasQy9vT1tBqzXZZf3AMJM=
 github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
-github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 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=
@@ -14,7 +7,3 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
 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=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/vendor/github.com/nats-io/jwt/v2/types.go b/vendor/github.com/nats-io/jwt/v2/types.go
index af572e36..5f172f51 100644
--- a/vendor/github.com/nats-io/jwt/v2/types.go
+++ b/vendor/github.com/nats-io/jwt/v2/types.go
@@ -127,17 +127,6 @@ func (s Subject) IsContainedIn(other Subject) bool {
 	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"`
@@ -169,12 +158,13 @@ func (tr *TimeRange) Validate(vr *ValidationResults) {
 
 // Src is a comma separated list of CIDR specifications
 type UserLimits struct {
-	Src   string      `json:"src,omitempty"`
-	Times []TimeRange `json:"times,omitempty"`
+	Src    CIDRList    `json:"src,omitempty"`
+	Times  []TimeRange `json:"times,omitempty"`
+	Locale string      `json:"times_location,omitempty"`
 }
 
 func (u *UserLimits) IsUnlimited() bool {
-	return u.Src == "" && len(u.Times) == 0
+	return len(u.Src) == 0 && len(u.Times) == 0
 }
 
 // Limits are used to control acccess for users and importing accounts
@@ -189,11 +179,8 @@ func (l *Limits) IsUnlimited() bool {
 
 // Validate checks the values in a limit struct
 func (l *Limits) Validate(vr *ValidationResults) {
-	if l.Src != "" {
-		elements := strings.Split(l.Src, ",")
-
-		for _, cidr := range elements {
-			cidr = strings.TrimSpace(cidr)
+	if len(l.Src) != 0 {
+		for _, cidr := range l.Src {
 			_, ipNet, err := net.ParseCIDR(cidr)
 			if err != nil || ipNet == nil {
 				vr.AddError("invalid cidr %q in user src limits", cidr)
@@ -206,6 +193,12 @@ func (l *Limits) Validate(vr *ValidationResults) {
 			t.Validate(vr)
 		}
 	}
+
+	if l.Locale != "" {
+		if _, err := time.LoadLocation(l.Locale); err != nil {
+			vr.AddError("could not parse iana time zone by name: %v", err)
+		}
+	}
 }
 
 // Permission defines allow/deny subjects
@@ -291,7 +284,7 @@ type TagList []string
 
 // Contains returns true if the list contains the tags
 func (u *TagList) Contains(p string) bool {
-	p = strings.ToLower(p)
+	p = strings.ToLower(strings.TrimSpace(p))
 	for _, t := range *u {
 		if t == p {
 			return true
@@ -303,7 +296,7 @@ func (u *TagList) Contains(p string) bool {
 // Add appends 1 or more tags to a list
 func (u *TagList) Add(p ...string) {
 	for _, v := range p {
-		v = strings.ToLower(v)
+		v = strings.ToLower(strings.TrimSpace(v))
 		if !u.Contains(v) && v != "" {
 			*u = append(*u, v)
 		}
@@ -313,7 +306,7 @@ func (u *TagList) Add(p ...string) {
 // Remove removes 1 or more tags from a list
 func (u *TagList) Remove(p ...string) {
 	for _, v := range p {
-		v = strings.ToLower(v)
+		v = strings.ToLower(strings.TrimSpace(v))
 		for i, t := range *u {
 			if t == v {
 				a := *u
@@ -324,6 +317,40 @@ func (u *TagList) Remove(p ...string) {
 	}
 }
 
+type CIDRList TagList
+
+func (c *CIDRList) Contains(p string) bool {
+	return (*TagList)(c).Contains(p)
+}
+
+func (c *CIDRList) Add(p ...string) {
+	(*TagList)(c).Add(p...)
+}
+
+func (c *CIDRList) Remove(p ...string) {
+	(*TagList)(c).Remove(p...)
+}
+
+func (c *CIDRList) Set(values string) {
+	*c = CIDRList{}
+	c.Add(strings.Split(strings.ToLower(values), ",")...)
+}
+
+func (c *CIDRList) UnmarshalJSON(body []byte) (err error) {
+	// parse either as array of strings or comma separate list
+	var request []string
+	var list string
+	if err := json.Unmarshal(body, &request); err == nil {
+		*c = request
+		return nil
+	} else if err := json.Unmarshal(body, &list); err == nil {
+		c.Set(list)
+		return nil
+	} else {
+		return err
+	}
+}
+
 // Identity is used to associate an account or operator with a real entity
 type Identity struct {
 	ID    string `json:"id,omitempty"`
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 64079a9c..f22eb7ba 100644
--- a/vendor/github.com/nats-io/jwt/v2/user_claims.go
+++ b/vendor/github.com/nats-io/jwt/v2/user_claims.go
@@ -53,8 +53,8 @@ func NewUserClaims(subject string) *UserClaims {
 	c := &UserClaims{}
 	c.Subject = subject
 	c.Limits = Limits{
-		UserLimits{"", nil},
-		NatsLimits{NoLimit, NoLimit, NoLimit, NoLimit, NoLimit},
+		UserLimits{CIDRList{}, nil, ""},
+		NatsLimits{NoLimit, NoLimit, NoLimit},
 	}
 	return c
 }
diff --git a/vendor/github.com/nats-io/jwt/validation.go b/vendor/github.com/nats-io/jwt/validation.go
new file mode 100644
index 00000000..c87a9922
--- /dev/null
+++ b/vendor/github.com/nats-io/jwt/validation.go
@@ -0,0 +1,107 @@
+/*
+ * 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 c72fb62c..0149ffb0 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1,6 +1,8 @@
 # github.com/minio/highwayhash v1.0.0
 github.com/minio/highwayhash
-# github.com/nats-io/jwt/v2 v2.0.0-20200817224207-b9df3db11eda
+# 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
 # github.com/nats-io/nats.go v1.10.1-0.20200606002146-fc6fed82929a
 github.com/nats-io/nats.go


Test patch:
diff --git a/server/jwt_test.go b/server/jwt_test.go
index 3aaf123b..8092ebfd 100644
--- a/server/jwt_test.go
+++ b/server/jwt_test.go
@@ -3279,11 +3279,11 @@ func TestJWTUserLimits(t *testing.T) {
 		f    func(*jwt.Limits)
 	}{
 		{true, nil},
-		{false, func(j *jwt.Limits) { j.Src = "8.8.8.8/8" }},
-		{true, func(j *jwt.Limits) { j.Src = "8.8.8.8/0" }},
-		{true, func(j *jwt.Limits) { j.Src = "127.0.0.1/8" }},
-		{true, func(j *jwt.Limits) { j.Src = "8.8.8.8/8,127.0.0.1/8" }},
-		{false, func(j *jwt.Limits) { j.Src = "8.8.8.8/8,9.9.9.9/8" }},
+		{false, func(j *jwt.Limits) { j.Src.Set("8.8.8.8/8") }},
+		{true, func(j *jwt.Limits) { j.Src.Set("8.8.8.8/0") }},
+		{true, func(j *jwt.Limits) { j.Src.Set("127.0.0.1/8") }},
+		{true, func(j *jwt.Limits) { j.Src.Set("8.8.8.8/8,127.0.0.1/8") }},
+		{false, func(j *jwt.Limits) { j.Src.Set("8.8.8.8/8,9.9.9.9/8") }},
 		{true, func(j *jwt.Limits) { j.Times = append(j.Times, newTimeRange(time.Now(), time.Hour)) }},
 		{false, func(j *jwt.Limits) { j.Times = append(j.Times, newTimeRange(time.Now().Add(time.Hour), time.Hour)) }},
 		{true, func(j *jwt.Limits) {