Skip to content

Commit

Permalink
add support for x-api-key auth method
Browse files Browse the repository at this point in the history
  • Loading branch information
sduchesneau committed Feb 16, 2024
1 parent e0208cf commit 7411f2b
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 7 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ Operators, you should copy/paste content of this content straight to your projec

If you were at `firehose-core` version `1.0.0` and are bumping to `1.1.0`, you should copy the content between those 2 version to your own repository, replacing placeholder value `fire{chain}` with your chain's own binary.

## v1.2.1

### Fixed

* Fixed `tools firehose-client` which was broken because of bad flag handling

### Added

* Added `--api-key-env-var` flag to firehose-clients, which allows you to pass your API Key from an environment variable (HTTP header `x-api-key`) instead of a JWT (`Authorization: bearer`), where supported.

## v1.2.0

* Poller is now fetching blocks in an optimized way, it will fetch several blocks at once and then process them.
Expand Down
7 changes: 5 additions & 2 deletions cmd/tools/firehose/firehose.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ func getFirehoseClientFromCmd[B firecore.Block, C any](cmd *cobra.Command, logge
requestInfo = &firehoseRequestInfo{}

jwt := os.Getenv(sflags.MustGetString(cmd, "api-token-env-var"))
apiKey := os.Getenv(sflags.MustGetString(cmd, "api-key-env-var"))

plaintext := sflags.MustGetBool(cmd, "plaintext")
insecure := sflags.MustGetBool(cmd, "insecure")

Expand All @@ -64,7 +66,7 @@ func getFirehoseClientFromCmd[B firecore.Block, C any](cmd *cobra.Command, logge

var rawClient any
if kind == "stream-client" {
rawClient, connClose, requestInfo.GRPCCallOpts, err = client.NewFirehoseClient(endpoint, jwt, insecure, plaintext)
rawClient, connClose, requestInfo.GRPCCallOpts, err = client.NewFirehoseClient(endpoint, jwt, apiKey, insecure, plaintext)
} else if kind == "fetch-client" {
rawClient, connClose, err = client.NewFirehoseFetchClient(endpoint, jwt, insecure, plaintext)
} else {
Expand Down Expand Up @@ -113,7 +115,8 @@ func addFirehoseStreamClientFlagsToSet[B firecore.Block](flags *pflag.FlagSet, c
}

func addFirehoseFetchClientFlagsToSet[B firecore.Block](flags *pflag.FlagSet, chain *firecore.Chain[B]) {
flags.StringP("api-token-env-var", "a", "FIREHOSE_API_TOKEN", "Look for a JWT in this environment variable to authenticate against endpoint")
flags.StringP("api-token-env-var", "a", "FIREHOSE_API_TOKEN", "Look for a JWT in this environment variable to authenticate against endpoint (alternative to api-key-env-var)")
flags.String("api-key-env-var", "FIREHOSE_API_KEY", "Look for an API key directly in this environment variable to authenticate against endpoint (alternative to api-token-env-var)")
flags.String("compression", "none", "The HTTP compression: use either 'none', 'gzip' or 'zstd'")
flags.BoolP("plaintext", "p", false, "Use plaintext connection to Firehose")
flags.BoolP("insecure", "k", false, "Use SSL connection to Firehose but skip SSL certificate validation")
Expand Down
40 changes: 35 additions & 5 deletions firehose/client/client.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package client

import (
"context"
"crypto/tls"
"fmt"

Expand All @@ -11,13 +12,13 @@ import (
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/credentials/oauth"
"google.golang.org/grpc/metadata"
)

// firehoseClient, closeFunc, grpcCallOpts, err := NewFirehoseClient(endpoint, jwt, insecure, plaintext)
// defer closeFunc()
// stream, err := firehoseClient.Blocks(context.Background(), request, grpcCallOpts...)
func NewFirehoseClient(endpoint, jwt string, useInsecureTSLConnection, usePlainTextConnection bool) (cli pbfirehose.StreamClient, closeFunc func() error, callOpts []grpc.CallOption, err error) {
skipAuth := jwt == "" || usePlainTextConnection
func NewFirehoseClient(endpoint, jwt, apiKey string, useInsecureTSLConnection, usePlainTextConnection bool) (cli pbfirehose.StreamClient, closeFunc func() error, callOpts []grpc.CallOption, err error) {

if useInsecureTSLConnection && usePlainTextConnection {
return nil, nil, nil, fmt.Errorf("option --insecure and --plaintext are mutually exclusive, they cannot be both specified at the same time")
Expand All @@ -39,14 +40,43 @@ func NewFirehoseClient(endpoint, jwt string, useInsecureTSLConnection, usePlainT
closeFunc = conn.Close
cli = pbfirehose.NewStreamClient(conn)

if !skipAuth {
credentials := oauth.NewOauthAccess(&oauth2.Token{AccessToken: jwt, TokenType: "Bearer"})
callOpts = append(callOpts, grpc.PerRPCCredentials(credentials))
if !usePlainTextConnection {
if jwt != "" {
credentials := oauth.NewOauthAccess(&oauth2.Token{AccessToken: jwt, TokenType: "Bearer"})
callOpts = append(callOpts, grpc.PerRPCCredentials(credentials))
} else if apiKey != "" {
callOpts = append(callOpts, grpc.PerRPCCredentials(&ApiKeyAuth{ApiKey: apiKey}))
}
}

return
}

type ApiKeyAuth struct {
ApiKey string
}

func (a *ApiKeyAuth) GetRequestMetadata(ctx context.Context, uri ...string) (out map[string]string, err error) {
md, ok := metadata.FromOutgoingContext(ctx)
if !ok {
md = metadata.New(nil)
}
out = make(map[string]string)
for k, v := range md {
if len(v) != 0 {
out[k] = v[0]
}
}
if a.ApiKey != "" {
out["x-api-key"] = a.ApiKey
}
return
}

func (a *ApiKeyAuth) RequireTransportSecurity() bool {
return true
}

func NewFirehoseFetchClient(endpoint, jwt string, useInsecureTSLConnection, usePlainTextConnection bool) (cli pbfirehose.FetchClient, closeFunc func() error, err error) {

if useInsecureTSLConnection && usePlainTextConnection {
Expand Down

0 comments on commit 7411f2b

Please sign in to comment.