Repository: nats-io/nsc
Pull request #275 (2020-05-21 21:41:17)

added bearer token support


Patch:
diff --git a/cmd/adduser.go b/cmd/adduser.go
index 043f090..1c0a053 100644
--- a/cmd/adduser.go
+++ b/cmd/adduser.go
@@ -82,6 +82,8 @@ nsc add user --name <n> --allow-pub-response=5
 	cmd.Flags().StringVarP(&params.userName, "name", "n", "", "name to assign the user")
 	cmd.Flags().StringVarP(&params.pkOrPath, "public-key", "k", "", "public key identifying the user")
 
+	cmd.Flags().BoolVarP(&params.bearer, "bearer", "", false, "no connect challenge required for user")
+
 	params.TimeParams.BindFlags(cmd)
 	params.AccountContextParams.BindFlags(cmd)
 	params.ResponsePermsParams.bindSetFlags(cmd)
@@ -107,6 +109,7 @@ type AddUserParams struct {
 	src           []string
 	tags          []string
 	credsFilePath string
+	bearer        bool
 	userName      string
 	pkOrPath      string
 	kp            nkeys.KeyPair
@@ -350,6 +353,7 @@ func (p *AddUserParams) generateUserClaim(ctx ActionCtx) (*jwt.UserClaims, error
 	uc.Tags.Add(p.tags...)
 	sort.Strings(uc.Tags)
 
+	uc.BearerToken = p.bearer
 	return uc, nil
 }
 
diff --git a/cmd/describer.go b/cmd/describer.go
index 8cfd05c..95b36f9 100644
--- a/cmd/describer.go
+++ b/cmd/describer.go
@@ -148,6 +148,14 @@ func NewExportsDescriber(exports jwt.Exports) *ExportsDescriber {
 	return &e
 }
 
+func toYesNo(tf bool) string {
+	v := "Yes"
+	if !tf {
+		v = "No"
+	}
+	return v
+}
+
 func (e *ExportsDescriber) Describe() string {
 	table := tablewriter.CreateTable()
 	table.UTF8Box()
@@ -155,10 +163,6 @@ func (e *ExportsDescriber) Describe() string {
 	table.AddTitle("Exports")
 	table.AddHeaders("Name", "Type", "Subject", "Public", "Revocations", "Tracking")
 	for _, v := range e.Exports {
-		public := "Yes"
-		if v.TokenReq {
-			public = "No"
-		}
 		mon := "N/A"
 		rt := ""
 		if v.Type == jwt.Service {
@@ -177,7 +181,7 @@ func (e *ExportsDescriber) Describe() string {
 
 		st := strings.Title(v.Type.String())
 		k := fmt.Sprintf("%s%s", st, rt)
-		table.AddRow(v.Name, k, v.Subject, public, len(v.Revocations), mon)
+		table.AddRow(v.Name, k, v.Subject, toYesNo(v.TokenReq), len(v.Revocations), mon)
 	}
 	return table.Render()
 }
@@ -373,6 +377,7 @@ func (u *UserDescriber) Describe() string {
 	table.UTF8Box()
 	table.AddTitle("User")
 	AddStandardClaimInfo(table, &u.UserClaims)
+	table.AddRow("Bearer Token", toYesNo(u.BearerToken))
 
 	if len(u.Pub.Allow) > 0 || len(u.Pub.Deny) > 0 ||
 		len(u.Sub.Allow) > 0 || len(u.Sub.Deny) > 0 {
diff --git a/cmd/edituser.go b/cmd/edituser.go
index a1cfae9..a38dc55 100644
--- a/cmd/edituser.go
+++ b/cmd/edituser.go
@@ -86,6 +86,8 @@ nsc edit user --name <n> --rm-response-perms
 
 	cmd.Flags().StringVarP(&params.name, "name", "n", "", "user name")
 
+	cmd.Flags().BoolVarP(&params.bearer, "bearer", "", false, "no connect challenge required for user")
+
 	params.AccountContextParams.BindFlags(cmd)
 	params.GenericClaimsParams.BindFlags(cmd)
 	params.ResponsePermsParams.bindSetFlags(cmd)
@@ -118,6 +120,7 @@ type EditUserParams struct {
 	rmSrc       []string
 	src         []string
 	payload     DataParams
+	bearer      bool
 }
 
 func (p *EditUserParams) SetDefaults(ctx ActionCtx) error {
@@ -127,7 +130,7 @@ func (p *EditUserParams) SetDefaults(ctx ActionCtx) error {
 
 	if !InteractiveFlag && ctx.NothingToDo("start", "expiry", "rm", "allow-pub", "allow-sub", "allow-pubsub",
 		"deny-pub", "deny-sub", "deny-pubsub", "tag", "rm-tag", "source-network", "rm-source-network", "payload",
-		"rm-response-perms", "max-responses", "response-ttl", "allow-pub-response") {
+		"rm-response-perms", "max-responses", "response-ttl", "allow-pub-response", "bearer") {
 		ctx.CurrentCmd().SilenceUsage = false
 		return fmt.Errorf("specify an edit option")
 	}
@@ -292,6 +295,11 @@ func (p *EditUserParams) Run(ctx ActionCtx) (store.Status, error) {
 		r.AddOK("changed max imports to %d", p.claim.Limits.Payload)
 	}
 
+	if flags.Changed("bearer") {
+		p.claim.BearerToken = p.bearer
+		r.AddOK("changed bearer to %t", p.bearer)
+	}
+
 	src := strings.Split(p.claim.Src, ",")
 	var srcList jwt.StringList
 	srcList.Add(src...)
diff --git a/go.mod b/go.mod
index 8ebcf1a..aa50d02 100644
--- a/go.mod
+++ b/go.mod
@@ -13,10 +13,10 @@ require (
 	github.com/mitchellh/go-homedir v1.1.0
 	github.com/mitchellh/mapstructure v1.1.2 // indirect
 	github.com/nats-io/cliprompts/v2 v2.0.0-20191226174129-372d79b36768
-	github.com/nats-io/jwt v0.3.2
+	github.com/nats-io/jwt v0.3.3-0.20200519195258-f2bf5ce574c7
 	github.com/nats-io/nats-server/v2 v2.0.1-0.20190625001713-2db76bde3329
 	github.com/nats-io/nats.go v1.8.1
-	github.com/nats-io/nkeys v0.1.3
+	github.com/nats-io/nkeys v0.1.4
 	github.com/nats-io/nuid v1.0.1
 	github.com/onsi/ginkgo v1.7.0 // indirect
 	github.com/onsi/gomega v1.4.3 // indirect
diff --git a/go.sum b/go.sum
index fa609aa..c586c04 100644
--- a/go.sum
+++ b/go.sum
@@ -70,6 +70,8 @@ github.com/nats-io/jwt v0.2.6 h1:eAyoYvGgGLXR2EpnsBUvi/FcFrBqN6YKFVbOoEfPN4k=
 github.com/nats-io/jwt v0.2.6/go.mod h1:mQxQ0uHQ9FhEVPIcTSKwx2lqZEpXWWcCgA7R6NrWvvY=
 github.com/nats-io/jwt v0.3.2 h1:+RB5hMpXUUA2dfxuhBTEkMOrYmM+gKIZYS1KjSostMI=
 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/nats-server/v2 v2.0.1-0.20190625001713-2db76bde3329 h1:RjMdWlS/avIVeXLjWxrHmxoiNPcvLXVWqpO7Bp6hyTk=
 github.com/nats-io/nats-server/v2 v2.0.1-0.20190625001713-2db76bde3329/go.mod h1:RyVdsHHvY4B6c9pWG+uRLpZ0h0XsqiuKp2XCTurP5LI=
 github.com/nats-io/nats.go v1.8.1 h1:6lF/f1/NN6kzUDBz6pyvQDEXO39jqXcWRLu/tKjtOUQ=
@@ -78,6 +80,8 @@ github.com/nats-io/nkeys v0.0.2 h1:+qM7QpgXnvDDixitZtQUBDY9w/s9mu1ghS+JIbsrx6M=
 github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4=
 github.com/nats-io/nkeys v0.1.3 h1:6JrEfig+HzTH85yxzhSVbjHRJv9cn0p6n3IngIcM5/k=
 github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nkeys v0.1.4 h1:aEsHIssIk6ETN5m2/MD8Y4B2X7FfXrBAUdkyRvbVYzA=
+github.com/nats-io/nkeys v0.1.4/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s=
 github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
 github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
 github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
@@ -128,6 +132,8 @@ golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 h1:8dUaAV7K4uHsF56JQWkpre
 golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
 golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM=
+golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181108082009-03003ca0c849 h1:FSqE2GGG7wzsYUsWiQ8MZrvEd1EOyU3NCF0AW3Wtltg=
diff --git a/vendor/github.com/nats-io/jwt/creds_utils.go b/vendor/github.com/nats-io/jwt/creds_utils.go
index bb913dc..265057f 100644
--- a/vendor/github.com/nats-io/jwt/creds_utils.go
+++ b/vendor/github.com/nats-io/jwt/creds_utils.go
@@ -82,7 +82,7 @@ NKEYs are sensitive and should be treated as secrets.
 	return w.Bytes(), nil
 }
 
-var userConfigRE = regexp.MustCompile(`\s*(?:(?:[-]{3,}[^\n]*[-]{3,}\n)(.+)(?:\n\s*[-]{3,}[^\n]*[-]{3,}\n))`)
+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-----
diff --git a/vendor/github.com/nats-io/jwt/go.mod b/vendor/github.com/nats-io/jwt/go.mod
index a780dde..eebea6c 100644
--- a/vendor/github.com/nats-io/jwt/go.mod
+++ b/vendor/github.com/nats-io/jwt/go.mod
@@ -1,3 +1,5 @@
 module github.com/nats-io/jwt
 
-require github.com/nats-io/nkeys v0.1.3
+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
index 9baf67f..5e6e47e 100644
--- a/vendor/github.com/nats-io/jwt/go.sum
+++ b/vendor/github.com/nats-io/jwt/go.sum
@@ -1,8 +1,8 @@
-github.com/nats-io/nkeys v0.1.3 h1:6JrEfig+HzTH85yxzhSVbjHRJv9cn0p6n3IngIcM5/k=
-github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+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-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
-golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM=
+golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/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=
diff --git a/vendor/github.com/nats-io/jwt/operator_claims.go b/vendor/github.com/nats-io/jwt/operator_claims.go
index 6a99597..3c4d4a1 100644
--- a/vendor/github.com/nats-io/jwt/operator_claims.go
+++ b/vendor/github.com/nats-io/jwt/operator_claims.go
@@ -40,6 +40,8 @@ type Operator struct {
 	// 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
@@ -63,6 +65,12 @@ func (o *Operator) Validate(vr *ValidationResults) {
 			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 {
diff --git a/vendor/github.com/nats-io/jwt/types.go b/vendor/github.com/nats-io/jwt/types.go
index a1f09fd..e729c7e 100644
--- a/vendor/github.com/nats-io/jwt/types.go
+++ b/vendor/github.com/nats-io/jwt/types.go
@@ -241,8 +241,6 @@ type Permissions struct {
 
 // Validate the pub and sub fields in the permissions list
 func (p *Permissions) Validate(vr *ValidationResults) {
-	p.Pub.Validate(vr)
-	p.Sub.Validate(vr)
 	if p.Resp != nil {
 		p.Resp.Validate(vr)
 	}
diff --git a/vendor/github.com/nats-io/jwt/user_claims.go b/vendor/github.com/nats-io/jwt/user_claims.go
index 0ec1da3..78fe6a9 100644
--- a/vendor/github.com/nats-io/jwt/user_claims.go
+++ b/vendor/github.com/nats-io/jwt/user_claims.go
@@ -25,12 +25,14 @@ import (
 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
@@ -97,3 +99,8 @@ func (u *UserClaims) Payload() interface{} {
 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/nkeys/go.mod b/vendor/github.com/nats-io/nkeys/go.mod
index 34cad74..6cdaeb3 100644
--- a/vendor/github.com/nats-io/nkeys/go.mod
+++ b/vendor/github.com/nats-io/nkeys/go.mod
@@ -1,3 +1,3 @@
 module github.com/nats-io/nkeys
 
-require golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
+require golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59
diff --git a/vendor/github.com/nats-io/nkeys/go.sum b/vendor/github.com/nats-io/nkeys/go.sum
index b75b87f..8fdcca7 100644
--- a/vendor/github.com/nats-io/nkeys/go.sum
+++ b/vendor/github.com/nats-io/nkeys/go.sum
@@ -1,6 +1,6 @@
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
-golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM=
+golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/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=
diff --git a/vendor/github.com/nats-io/nkeys/main.go b/vendor/github.com/nats-io/nkeys/main.go
index 2ea3f90..5c7152d 100644
--- a/vendor/github.com/nats-io/nkeys/main.go
+++ b/vendor/github.com/nats-io/nkeys/main.go
@@ -20,7 +20,7 @@ import (
 )
 
 // Version is our current version
-const Version = "0.1.3"
+const Version = "0.1.4"
 
 // Errors
 var (
diff --git a/vendor/modules.txt b/vendor/modules.txt
index e5063e1..07342d7 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -57,7 +57,7 @@ github.com/mitchellh/go-wordwrap
 github.com/mitchellh/mapstructure
 # github.com/nats-io/cliprompts/v2 v2.0.0-20191226174129-372d79b36768
 github.com/nats-io/cliprompts/v2
-# github.com/nats-io/jwt v0.3.2
+# github.com/nats-io/jwt v0.3.3-0.20200519195258-f2bf5ce574c7
 github.com/nats-io/jwt
 # github.com/nats-io/nats-server/v2 v2.0.1-0.20190625001713-2db76bde3329
 github.com/nats-io/nats-server/v2/conf
@@ -68,7 +68,7 @@ github.com/nats-io/nats-server/v2/server/pse
 github.com/nats-io/nats.go
 github.com/nats-io/nats.go/encoders/builtin
 github.com/nats-io/nats.go/util
-# github.com/nats-io/nkeys v0.1.3
+# github.com/nats-io/nkeys v0.1.4
 github.com/nats-io/nkeys
 # github.com/nats-io/nuid v1.0.1
 github.com/nats-io/nuid
@@ -106,7 +106,7 @@ github.com/ulikunitz/xz/internal/xlog
 github.com/ulikunitz/xz/lzma
 # github.com/xlab/tablewriter v0.0.0-20160610135559-80b567a11ad5
 github.com/xlab/tablewriter
-# golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
+# golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59
 golang.org/x/crypto/bcrypt
 golang.org/x/crypto/blowfish
 golang.org/x/crypto/ed25519


Test patch:
diff --git a/cmd/adduser_test.go b/cmd/adduser_test.go
index a778442..2575bf4 100644
--- a/cmd/adduser_test.go
+++ b/cmd/adduser_test.go
@@ -340,3 +340,23 @@ func Test_AddUserWithExistingNkey(t *testing.T) {
 	_, _, err = ExecuteCmd(CreateAddUserCmd(), "U", "--public-key", pk)
 	require.NoError(t, err)
 }
+
+func Test_AddUser_BearerToken(t *testing.T) {
+	ts := NewTestStore(t, "O")
+	defer ts.Done(t)
+
+	ts.AddAccount(t, "A")
+	_, _, err := ExecuteCmd(HoistRootFlags(CreateAddUserCmd()), "--name", "UA")
+	require.NoError(t, err)
+
+	_, _, err = ExecuteCmd(HoistRootFlags(CreateAddUserCmd()), "--name", "UB", "--bearer")
+	require.NoError(t, err)
+
+	u, err := ts.Store.ReadUserClaim("A", "UA")
+	require.NoError(t, err)
+	require.False(t, u.BearerToken)
+
+	u, err = ts.Store.ReadUserClaim("A", "UB")
+	require.NoError(t, err)
+	require.True(t, u.BearerToken)
+}
diff --git a/cmd/edituser_test.go b/cmd/edituser_test.go
index 6f34de7..17557a0 100644
--- a/cmd/edituser_test.go
+++ b/cmd/edituser_test.go
@@ -331,3 +331,32 @@ func Test_EditUserResponsePermissions2(t *testing.T) {
 	require.NoError(t, err)
 	require.Nil(t, uc.Resp)
 }
+
+func Test_EditUserBearerToken(t *testing.T) {
+	ts := NewTestStore(t, "O")
+	defer ts.Done(t)
+	ts.AddAccount(t, "A")
+
+	_, _, err := ExecuteCmd(CreateAddUserCmd(), "U")
+	require.NoError(t, err)
+
+	u, err := ts.Store.ReadUserClaim("A", "U")
+	require.NoError(t, err)
+	require.False(t, u.BearerToken)
+
+	_, stderr, err := ExecuteCmd(createEditUserCmd(), "--name", "U", "--bearer")
+	require.NoError(t, err)
+	require.Contains(t, stderr, "changed bearer to true")
+
+	u, err = ts.Store.ReadUserClaim("A", "U")
+	require.NoError(t, err)
+	require.True(t, u.BearerToken)
+
+	_, stderr, err = ExecuteCmd(createEditUserCmd(), "--name", "U", "--bearer=false")
+	require.NoError(t, err)
+	require.Contains(t, stderr, "changed bearer to false")
+
+	u, err = ts.Store.ReadUserClaim("A", "U")
+	require.NoError(t, err)
+	require.False(t, u.BearerToken)
+}