Skip to content

Commit

Permalink
fix(jsonrpc): enable HTTP basic auth in WS client (#2434)
Browse files Browse the repository at this point in the history
Hello,

I noticed that the JSON-RPC client is compatible with URLs containing
**HTTP basic auth**, but not on the WebSocket side.

For instance:

```golang
client, err := http.New("https://foo:bar@example.org:443", "/websocket")

client.Block(ctx, nil) // works

client.Start() // websocket error: the client doesn't forward auth headers
```

The HTTP client handle the username/password from the given endpoint
correctly:
-
https://github.com/cometbft/cometbft/blob/v0.38.5/rpc/jsonrpc/client/http_json_client.go#L140-L141
-
https://github.com/cometbft/cometbft/blob/v0.38.5/rpc/jsonrpc/client/http_json_client.go#L184-L185


So this PR brings the same logic for the WS client.

---

#### PR checklist

- [ ] Tests written/updated
- [x] Changelog entry added in `.changelog` (we use
[unclog](https://github.com/informalsystems/unclog) to manage our
changelog)
- [ ] Updated relevant documentation (`docs/` or `spec/`) and code
comments
- [x] Title follows the [Conventional
Commits](https://www.conventionalcommits.org/en/v1.0.0/) spec
  • Loading branch information
MattKetmo committed Feb 27, 2024
1 parent a911b04 commit 8bf81d4
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- `[jsonrpc]` enable HTTP basic auth in websocket client ([#2434](https://github.com/cometbft/cometbft/pull/2434))
22 changes: 21 additions & 1 deletion rpc/jsonrpc/client/ws_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package client

import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"net"
Expand Down Expand Up @@ -35,7 +36,10 @@ type WSClient struct {

Address string // IP:PORT or /path/to/socket
Endpoint string // /websocket/url/endpoint
Dialer func(string, string) (net.Conn, error)
Username string
Password string

Dialer func(string, string) (net.Conn, error)

// Single user facing channel to read RPCResponses from, closed only when the
// client is being stopped.
Expand Down Expand Up @@ -96,13 +100,23 @@ func NewWS(remoteAddr, endpoint string, options ...func(*WSClient)) (*WSClient,
parsedURL.Scheme = protoWS
}

// extract username and password from URL if any
username := ""
password := ""
if parsedURL.User.String() != "" {
username = parsedURL.User.Username()
password, _ = parsedURL.User.Password()
}

dialFn, err := MakeHTTPDialer(remoteAddr)
if err != nil {
return nil, err
}

c := &WSClient{
Address: parsedURL.GetTrimmedHostWithPath(),
Username: username,
Password: password,
Dialer: dialFn,
Endpoint: endpoint,
PingPongLatencyTimer: metrics.NewTimer(),
Expand Down Expand Up @@ -267,6 +281,12 @@ func (c *WSClient) dial() error {
Proxy: http.ProxyFromEnvironment,
}
rHeader := http.Header{}

// Set basic auth header if username and password are provided
if c.Username != "" && c.Password != "" {
rHeader.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(c.Username+":"+c.Password)))
}

conn, _, err := dialer.Dial(c.protocol+"://"+c.Address+c.Endpoint, rHeader) //nolint:bodyclose
if err != nil {
return err
Expand Down

0 comments on commit 8bf81d4

Please sign in to comment.