diff --git a/client/v3/client.go b/client/v3/client.go index 2d415f10cc9c..5a0590cc7c61 100644 --- a/client/v3/client.go +++ b/client/v3/client.go @@ -68,7 +68,10 @@ type Client struct { // Username is a user name for authentication. Username string // Password is a password for authentication. - Password string + Password string + // Token is a JWT used for authentication instead of a password. + Token string + authTokenBundle credentials.PerRPCCredentialsBundle callOpts []grpc.CallOption @@ -262,9 +265,21 @@ func (c *Client) Dial(ep string) (*grpc.ClientConn, error) { return c.dial(creds, grpc.WithResolvers(resolver.New(ep))) } +// UpdateAuthToken allows updating the JWT auth token held by the +// client. It is safe to call this function concurrently with other +// operations. +func (c *Client) UpdateAuthToken(token string) { + c.authTokenBundle.UpdateAuthToken(token) +} + func (c *Client) getToken(ctx context.Context) error { var err error // return last error in a case of fail + if c.Token != "" { + c.UpdateAuthToken(c.Token) + return nil + } + if c.Username == "" || c.Password == "" { return nil } @@ -277,7 +292,7 @@ func (c *Client) getToken(ctx context.Context) error { } return err } - c.authTokenBundle.UpdateAuthToken(resp.Token) + c.UpdateAuthToken(resp.Token) return nil } @@ -386,11 +401,11 @@ func newClient(cfg *Config) (*Client, error) { return nil, err } - if cfg.Username != "" && cfg.Password != "" { - client.Username = cfg.Username - client.Password = cfg.Password - client.authTokenBundle = credentials.NewPerRPCCredentialBundle() - } + client.Username = cfg.Username + client.Password = cfg.Password + client.Token = cfg.Token + client.authTokenBundle = credentials.NewPerRPCCredentialBundle() + if cfg.MaxCallSendMsgSize > 0 || cfg.MaxCallRecvMsgSize > 0 { if cfg.MaxCallRecvMsgSize > 0 && cfg.MaxCallSendMsgSize > cfg.MaxCallRecvMsgSize { return nil, fmt.Errorf("gRPC message recv limit (%d bytes) must be greater than send limit (%d bytes)", cfg.MaxCallRecvMsgSize, cfg.MaxCallSendMsgSize) diff --git a/client/v3/config.go b/client/v3/config.go index 4a26714a8645..0074824d04cc 100644 --- a/client/v3/config.go +++ b/client/v3/config.go @@ -66,6 +66,9 @@ type Config struct { // Password is a password for authentication. Password string `json:"password"` + // Token is a JWT used for authentication instead of a password. + Token string `json:"token"` + // RejectOldCluster when set will refuse to create a client against an outdated cluster. RejectOldCluster bool `json:"reject-old-cluster"` @@ -119,10 +122,11 @@ type SecureConfig struct { type AuthConfig struct { Username string `json:"username"` Password string `json:"password"` + Token string `json:"token"` } func (cfg AuthConfig) Empty() bool { - return cfg.Username == "" && cfg.Password == "" + return cfg.Username == "" && cfg.Password == "" && cfg.Token == "" } // NewClientConfig creates a Config based on the provided ConfigSpec. @@ -143,6 +147,7 @@ func NewClientConfig(confSpec *ConfigSpec, lg *zap.Logger) (*Config, error) { if confSpec.Auth != nil { cfg.Username = confSpec.Auth.Username cfg.Password = confSpec.Auth.Password + cfg.Token = confSpec.Auth.Token } return cfg, nil