Add BearerTokenFunc to support dynamic bearer token authentication for OTLP gRPC exporters. Tokens are injected via gRPC PerRPCCredentials on each export request. - Add BearerTokenFunc type and Config field in tracerconfig - Implement bearerCredentials (gRPC) and bearerRoundTripper (HTTP) - Wire bearer auth into all three gRPC exporter creation functions - Add token verification before flushing buffered logs - Fix race condition in buffering exporter initialization Note: HTTP exporters don't support dynamic bearer tokens due to OpenTelemetry SDK limitations (no WithHTTPClient option). Use gRPC protocol for dynamic tokens.
53 lines
1.6 KiB
Go
53 lines
1.6 KiB
Go
package tracerconfig
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
)
|
|
|
|
// bearerCredentials implements gRPC PerRPCCredentials for bearer token authentication.
|
|
// It is safe for concurrent use as required by the gRPC PerRPCCredentials interface.
|
|
type bearerCredentials struct {
|
|
tokenFunc BearerTokenFunc
|
|
}
|
|
|
|
// GetRequestMetadata returns authorization metadata for each RPC call.
|
|
// It calls the token function to retrieve the current token.
|
|
func (c *bearerCredentials) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
|
|
token, err := c.tokenFunc(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if token == "" {
|
|
return nil, nil // Omit header for empty token
|
|
}
|
|
return map[string]string{"authorization": "Bearer " + token}, nil
|
|
}
|
|
|
|
// RequireTransportSecurity returns true because bearer tokens require TLS.
|
|
func (c *bearerCredentials) RequireTransportSecurity() bool {
|
|
return true
|
|
}
|
|
|
|
// bearerRoundTripper wraps an http.RoundTripper to add bearer token authentication.
|
|
// It is safe for concurrent use as required by the http.RoundTripper interface.
|
|
type bearerRoundTripper struct {
|
|
base http.RoundTripper
|
|
tokenFunc BearerTokenFunc
|
|
}
|
|
|
|
// RoundTrip adds the Authorization header with the bearer token.
|
|
func (rt *bearerRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
token, err := rt.tokenFunc(req.Context())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if token == "" {
|
|
return rt.base.RoundTrip(req)
|
|
}
|
|
// Clone only when adding a header to preserve the original request
|
|
req = req.Clone(req.Context())
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
return rt.base.RoundTrip(req)
|
|
}
|