gobasic-authenticationgrpc-go

GRPC TransportCredentials for Basic Authentication


I'm trying establish a connection to a remote GRPC service (a Camunda Zeebe gateway) with basic authentication:

client, err := zbc.NewClient(&zbc.ClientConfig{
    GatewayAddress:         gatewayAddress,
    DialOpts: []grpcLib.DialOption{grpcLib.WithTransportCredentials(&BasicAuthCredentials{...})},
})

I'm a bit in loss when it comes to the implementation of BasicAuthCredentials, b/c I don't know how to set the Authorization header in the net.Conn object:

import (
    "context"
    "encoding/base64"
    "google.golang.org/grpc/credentials"
    "net"
)


type BasicAuthCredentials struct {
    username string
    password string
}

func (c *BasicAuthCredentials) ClientHandshake(ctx context.Context, s string, conn net.Conn) (net.Conn, credentials.AuthInfo, error) {
    // Create the Basic Auth string
    auth := b.username + ":" + b.password
    encodedAuth := base64.StdEncoding.EncodeToString([]byte(auth))
    // TODO set this encoded string as Authentication header
    return conn, c, nil
}

func (c *BasicAuthCredentials) AuthType() string {
    return "basic-auth"
}

// ... other methods to implement credentials.TransportCredentials

Is there a way, to attach these auth information to the connection, or am I approaching it fundamentally wrong?


Solution

  • You are trying to "alter" connection directly, but gRPC already handles the connection management internally. What you really want is to add authentication information to each RPC call. You should implement GetRequestMetadata instead of trying to set the Authorization header in the net.Conn object.

    pkg.go.dev GRPC documentation

    Implementation should look something like this ->>

    package main
    
    import (
        "context"
        "encoding/base64"
        "google.golang.org/grpc"
        "google.golang.org/grpc/metadata"
    )
    
    func basicAuth(username, password string) grpc.DialOption {
        auth := username + ":" + password
        encodedAuth := base64.StdEncoding.EncodeToString([]byte(auth))
        return grpc.WithPerRPCCredentials(&basicAuthCreds{token: encodedAuth})
    }
    
    type basicAuthCreds struct {
        token string
    }
    
    func (b *basicAuthCreds) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
        return map[string]string{
            "authorization": "Basic " + b.token,
        }, nil
    }
    
    func (b *basicAuthCreds) RequireTransportSecurity() bool {
        return false // Set to true if using TLS
    }
    

    Than use it like this

    client, err := zbc.NewClient(&zbc.ClientConfig{
        GatewayAddress: gatewayAddress,
        DialOpts: []grpc.DialOption{
            basicAuth("username", "password"),
        },
    })