Repository: jaegertracing/jaeger

Issue:
[Bug]: TLS options for Prometheus are leading to crash when specified in a YAML configuration file
### What happened?

Used image: jaegertracing/jaeger:2.8.0 (jaeger v2)
Deployed it using OpenTelemetryOperator via OpenTelemetryCollector resource, specified config in spec.config
Jaeger crashed with 
```
'prometheus' has invalid keys: tls
```
passing CLI flag:
```
--set=extensions.jaeger_storage.metric_backends.prom.prometheus.tls.ca_file=/ca/ca.crt'
```
also not working and produced same output

### Steps to reproduce

1. Specify Prometheus storage backend with TLS options:
```
metric_backends:
  prom:
    prometheus:
      endpoint: https://example.com
      normalize_calls: true
      normalize_duration: true
      token_file_path: /tokens/prometheus_token
      token_override_from_context: true
      tls:
        insecure: false
        ca_file: /tls/ca.crt
```
2. Deploy jaeger
3. See logs

### Expected behavior

Expected Prometheus TLS config to work just like in Cassandra storage config for example (since they are referring to the same struct go.opentelemetry.io/collector/config/configtls)



### Relevant log output

```shell
'extensions' error reading configuration for "jaeger_storage": decoding failed due to the following error(s):
'metric_backends[prom]' decoding failed due to the following error(s):
'prometheus' has invalid keys: tls
```

### Screenshot

_No response_

### Additional context

Seems like the issue is coming from forgotten mapstructure at line 17 in file internal/config/promcfg/config.go:
```
// Configuration describes the options to customize the storage behavior.
type Configuration struct {
	ServerURL      string        `valid:"required" mapstructure:"endpoint"`
	ConnectTimeout time.Duration `mapstructure:"connect_timeout"`
	TLS            configtls.ClientConfig <----------------- here

	TokenFilePath            string `mapstructure:"token_file_path"`
	TokenOverrideFromContext bool   `mapstructure:"token_override_from_context"`

	MetricNamespace   string `mapstructure:"metric_namespace"`
	LatencyUnit       string `mapstructure:"latency_unit"`
	NormalizeCalls    bool   `mapstructure:"normalize_calls"`
	NormalizeDuration bool   `mapstructure:"normalize_duration"`
	// ExtraQueryParams is used to provide extra parameters to be appended
	// to the URL of queries going out to the metrics backend.
	ExtraQueryParams map[string]string `mapstructure:"extra_query_parameters"`
}
```
But I'm no golang expert

### Jaeger backend version

2.8.0

### SDK

I'm not sending any data


### Pipeline

OTEL-SDK--->Jaeger all-in-one--->Scylladb


### Stogage backend

Scylladb, VictoriaMetrics

### Operating system

Linux

### Deployment model

Kubernetes OpenTelemetry Operator

### Deployment configs

```shell
# OpenTelemetryCollector spec.config:

config:
  service:
    telemetry:
      resource:
        service.name: jaeger-main
      metrics:
        readers:
          - pull:
              exporter:
                prometheus:
                  host: 0.0.0.0
                  port: 8888
      logs:
        level: DEBUG
    extensions: [jaeger_storage, jaeger_query]
    pipelines:
      traces:
        receivers: [otlp]
        processors: [batch]
        exporters: [jaeger_storage_exporter, spanmetrics]
      metrics/spanmetrics:
        receivers: [spanmetrics]
        exporters: [prometheus]
  connectors:
    spanmetrics:
  processors:
    batch:
  extensions:
    jaeger_query:
      storage:
        traces: main
        metrics: prom
    jaeger_storage:
      backends:
        main:
          cassandra:
            schema:
              create: true
              datacenter: dc1
              keyspace: jaeger
            query:
              timeout: 20s
              consistency: TWO
            connection:
              servers: scylladb-0.scylladb-1.tracing.svc.cluster.local,scylladb-0.scylladb-2.tracing.svc.cluster.local,scylladb-0.scylladb-3.tracing.svc.cluster.local
              tls:
                insecure: false
                ca_file: /scylladb-certs/ca.crt
                # Otherwise jaeger will resolve hostnames first, and then will try to connect via IP (and it will break TLS because of no IPSans)
                server_name_override: scylladb.tracing.svc.cluster.local
              auth:
                basic:
                  username: "jaeger"
                  password: "${env:CASSANDRA_PASSWORD}"
      metric_backends:
        prom:
          prometheus:
            endpoint: https://example.com
            normalize_calls: true
            normalize_duration: true
            token_file_path: /tokens/prometheus_token
            token_override_from_context: true
            tls:
              insecure: false
              ca_file: /ca/ca.crt
  receivers:
    otlp:
      protocols:
        grpc:
          endpoint: 0.0.0.0:4317
        http:
          endpoint: 0.0.0.0:4318
  exporters:
    jaeger_storage_exporter:
      trace_storage: main
    prometheus:
      endpoint: "0.0.0.0:8889"
```

Hints from issue comments:
@pipiland2612: Hmm, let me check this out, my work is also on the same `metricstore` pakage

Solution patch:
diff --git a/cmd/all-in-one/main.go b/cmd/all-in-one/main.go
index a70ba3d6..ecb52b44 100644
--- a/cmd/all-in-one/main.go
+++ b/cmd/all-in-one/main.go
@@ -18,7 +18,6 @@ import (
 	_ "go.uber.org/automaxprocs"
 	"go.uber.org/zap"
 
-	"github.com/jaegertracing/jaeger/cmd/all-in-one/setupcontext"
 	collectorApp "github.com/jaegertracing/jaeger/cmd/collector/app"
 	collectorFlags "github.com/jaegertracing/jaeger/cmd/collector/app/flags"
 	"github.com/jaegertracing/jaeger/cmd/internal/docs"
@@ -47,7 +46,6 @@ import (
 // all-in-one/main is a standalone full-stack jaeger backend, backed by a memory store
 func main() {
 	flags.PrintV1EOL()
-	setupcontext.SetAllInOne()
 
 	svc := flags.NewService(ports.CollectorAdminHTTP)
 
diff --git a/cmd/all-in-one/setupcontext/setupcontext.go b/cmd/all-in-one/setupcontext/setupcontext.go
deleted file mode 100644
index 396c1bd2..00000000
--- a/cmd/all-in-one/setupcontext/setupcontext.go
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (c) 2019 The Jaeger Authors.
-// SPDX-License-Identifier: Apache-2.0
-
-package setupcontext
-
-var isAllInOne bool
-
-// SetAllInOne sets the internal flag to all in one on.
-func SetAllInOne() {
-	isAllInOne = true
-}
-
-// UnsetAllInOne unsets the internal all-in-one flag.
-func UnsetAllInOne() {
-	isAllInOne = false
-}
-
-// IsAllInOne returns true when all in one mode is on.
-func IsAllInOne() bool {
-	return isAllInOne
-}
diff --git a/internal/auth/tokenloader.go b/internal/auth/tokenloader.go
new file mode 100644
index 00000000..8d6e2ae2
--- /dev/null
+++ b/internal/auth/tokenloader.go
@@ -0,0 +1,77 @@
+// Copyright (c) 2025 The Jaeger Authors.
+// SPDX-License-Identifier: Apache-2.0
+
+package auth
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+	"sync"
+	"time"
+
+	"go.uber.org/zap"
+)
+
+// CachedFileTokenLoader returns a function that loads a token from the given file path,
+// caches it for the specified interval, and reloads after the interval expires.
+// disable Reloading by setting interval to 0
+func cachedFileTokenLoader(path string, interval time.Duration, timeFn func() time.Time) func() (string, error) {
+	var (
+		mu          sync.Mutex
+		cachedToken string
+		lastRead    time.Time
+	)
+
+	return func() (string, error) {
+		mu.Lock()
+		defer mu.Unlock()
+
+		now := timeFn()
+
+		// Special case: interval = 0 means "never reload after first load"
+		// Otherwise reload only if `interval` time has passed since last load.
+		if !lastRead.IsZero() && (interval == 0 || now.Sub(lastRead) < interval) {
+			return cachedToken, nil
+		}
+
+		// Read from file
+		b, err := os.ReadFile(filepath.Clean(path))
+		if err != nil {
+			return "", fmt.Errorf("failed to read token file: %w", err)
+		}
+
+		cachedToken = strings.TrimRight(string(b), "\r\n")
+		lastRead = now
+		return cachedToken, nil
+	}
+}
+
+// TokenProvider creates a token provider that handles file loading and error handling consistently.
+func TokenProvider(path string, interval time.Duration, logger *zap.Logger) (func() string, error) {
+	return TokenProviderWithTime(path, interval, logger, time.Now) // Use real time.Now in production
+}
+
+// TokenProviderWithTime creates a token provider with injectable time (for testing)
+func TokenProviderWithTime(path string, interval time.Duration, logger *zap.Logger, timeFn func() time.Time) (func() string, error) {
+	loader := cachedFileTokenLoader(path, interval, timeFn)
+
+	// current token load
+	currentToken, err := loader()
+	if err != nil {
+		return nil, fmt.Errorf("failed to get token from file: %w", err)
+	}
+
+	return func() string {
+		newToken, err := loader()
+		if err != nil {
+			logger.Warn("Token reload failed", zap.Error(err))
+			return currentToken
+		}
+
+		// save it in case the load fails later (e.g. if file is removed)
+		currentToken = newToken
+		return currentToken
+	}, nil
+}
diff --git a/internal/auth/transport.go b/internal/auth/transport.go
index 76a1ae35..6ff12c38 100644
--- a/internal/auth/transport.go
+++ b/internal/auth/transport.go
@@ -4,23 +4,30 @@
 package auth
 
 import (
+	"context"
 	"errors"
+	"fmt"
 	"net/http"
-
-	"github.com/jaegertracing/jaeger/internal/auth/bearertoken"
 )
 
+// Method represents a single authentication method configuration
+type Method struct {
+	// Scheme is the authentication scheme (e.g., "Bearer")
+	Scheme string
+
+	// TokenFn returns the authentication token
+	TokenFn func() string
+
+	// FromCtx extracts token from context
+	FromCtx func(context.Context) (string, bool)
+}
+
 // RoundTripper wraps another http.RoundTripper and injects
-// an authentication header with bearer token into requests.
+// an authentication header with  token into requests.
 type RoundTripper struct {
 	// Transport is the underlying http.RoundTripper being wrapped. Required.
 	Transport http.RoundTripper
-
-	// StaticToken is the pre-configured bearer token. Optional.
-	StaticToken string
-
-	// OverrideFromCtx enables reading bearer token from Context.
-	OverrideFromCtx bool
+	Auths     []Method
 }
 
 // RoundTrip injects the outbound Authorization header with the
@@ -29,15 +36,29 @@ func (tr RoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
 	if tr.Transport == nil {
 		return nil, errors.New("no http.RoundTripper provided")
 	}
-	token := tr.StaticToken
-	if tr.OverrideFromCtx {
-		headerToken, _ := bearertoken.GetBearerToken(r.Context())
-		if headerToken != "" {
-			token = headerToken
+
+	req := r.Clone(r.Context())
+
+	for _, auth := range tr.Auths {
+		token := ""
+
+		// Get token from context if available
+		if auth.FromCtx != nil {
+			if t, ok := auth.FromCtx(r.Context()); ok {
+				token = t
+			}
+		}
+
+		// Fall back to TokenFn if no token from context
+		if token == "" && auth.TokenFn != nil {
+			token = auth.TokenFn()
+		}
+
+		// Add Authorization header if we have a token
+		if token != "" {
+			req.Header.Add("Authorization", fmt.Sprintf("%s %s", auth.Scheme, token))
 		}
 	}
-	if token != "" {
-		r.Header.Set("Authorization", "Bearer "+token)
-	}
-	return tr.Transport.RoundTrip(r)
+
+	return tr.Transport.RoundTrip(req)
 }
diff --git a/internal/config/promcfg/config.go b/internal/config/promcfg/config.go
index 864a5333..e029c13d 100644
--- a/internal/config/promcfg/config.go
+++ b/internal/config/promcfg/config.go
@@ -14,7 +14,8 @@ import (
 type Configuration struct {
 	ServerURL      string        `valid:"required" mapstructure:"endpoint"`
 	ConnectTimeout time.Duration `mapstructure:"connect_timeout"`
-	TLS            configtls.ClientConfig
+	// TLS contains the TLS configuration for the connection to the Prometheus clusters.
+	TLS configtls.ClientConfig `mapstructure:"tls"`
 
 	TokenFilePath            string `mapstructure:"token_file_path"`
 	TokenOverrideFromContext bool   `mapstructure:"token_override_from_context"`
diff --git a/internal/storage/elasticsearch/config/auth_helper.go b/internal/storage/elasticsearch/config/auth_helper.go
new file mode 100644
index 00000000..c3b8f60a
--- /dev/null
+++ b/internal/storage/elasticsearch/config/auth_helper.go
@@ -0,0 +1,55 @@
+// Copyright (c) 2025 The Jaeger Authors.
+// SPDX-License-Identifier: Apache-2.0
+
+package config
+
+import (
+	"context"
+	"time"
+
+	"go.uber.org/zap"
+
+	"github.com/jaegertracing/jaeger/internal/auth"
+	"github.com/jaegertracing/jaeger/internal/auth/bearertoken"
+)
+
+// initBearerAuth initializes bearer token authentication method
+func initBearerAuth(bearerAuth *BearerTokenAuthentication, logger *zap.Logger) (*auth.Method, error) {
+	return initBearerAuthWithTime(bearerAuth, logger, time.Now)
+}
+
+// initBearerAuthWithTime initializes bearer token authentication method with injectable time for testing
+func initBearerAuthWithTime(bearerAuth *BearerTokenAuthentication, logger *zap.Logger, timeFn func() time.Time) (*auth.Method, error) {
+	if bearerAuth == nil || (bearerAuth.FilePath == "" && !bearerAuth.AllowFromContext) {
+		return nil, nil
+	}
+
+	if bearerAuth.FilePath != "" && bearerAuth.AllowFromContext {
+		logger.Warn("Both Bearer Token file and context propagation are enabled - context token will take precedence over file-based token")
+	}
+
+	var tokenFn func() string
+	var fromCtx func(context.Context) (string, bool)
+
+	// file-based token setup
+	if bearerAuth.FilePath != "" {
+		reloadInterval := bearerAuth.ReloadInterval
+		tf, err := auth.TokenProviderWithTime(bearerAuth.FilePath, reloadInterval, logger, timeFn)
+		if err != nil {
+			return nil, err
+		}
+		tokenFn = tf
+	}
+
+	// context-based token setup
+	if bearerAuth.AllowFromContext {
+		fromCtx = bearertoken.GetBearerToken
+	}
+
+	// Return pointer to the auth method
+	return &auth.Method{
+		Scheme:  "Bearer",
+		TokenFn: tokenFn,
+		FromCtx: fromCtx,
+	}, nil
+}
diff --git a/internal/storage/elasticsearch/config/config.go b/internal/storage/elasticsearch/config/config.go
index 16ee3abf..09e42cd7 100644
--- a/internal/storage/elasticsearch/config/config.go
+++ b/internal/storage/elasticsearch/config/config.go
@@ -219,6 +219,9 @@ type BearerTokenAuthentication struct {
 	FilePath string `mapstructure:"file_path"`
 	// AllowTokenFromContext, if set to true, enables reading bearer token from the context.
 	AllowFromContext bool `mapstructure:"from_context"`
+	// ReloadInterval contains the interval at which the bearer token file is reloaded.
+	// If set to 0 then the file is only loaded once on startup.
+	ReloadInterval time.Duration `mapstructure:"reload_interval"`
 }
 
 // NewClient creates a new ElasticSearch client
@@ -641,29 +644,27 @@ func GetHTTPRoundTripper(ctx context.Context, c *Configuration, logger *zap.Logg
 		transport.TLSClientConfig = tlsConfig
 	}
 
-	// Wrap with authentication layer if configured.
-	var roundTripper http.RoundTripper = transport
+	// Initialize authentication methods.
+	var authMethods []auth.Method
 
+	// Bearer Token Authentication
 	if c.Authentication.BearerTokenAuthentication.HasValue() {
 		bearerAuth := c.Authentication.BearerTokenAuthentication.Get()
-		if bearerAuth.AllowFromContext || bearerAuth.FilePath != "" {
-			token := ""
-			if bearerAuth.FilePath != "" {
-				if bearerAuth.AllowFromContext {
-					logger.Warn("Token file and token propagation are both enabled, token from file won't be used")
-				}
-				tokenFromFile, err := loadTokenFromFile(bearerAuth.FilePath)
-				if err != nil {
-					return nil, err
-				}
-				token = tokenFromFile
-			}
+		ba, err := initBearerAuth(bearerAuth, logger)
+		if err != nil {
+			return nil, fmt.Errorf("failed to initialize bearer authentication: %w", err)
+		}
+		if ba != nil {
+			authMethods = append(authMethods, *ba)
+		}
+	}
 
-			roundTripper = &auth.RoundTripper{
-				Transport:       transport,
-				OverrideFromCtx: bearerAuth.AllowFromContext,
-				StaticToken:     token,
-			}
+	// Wrap with authentication layer.
+	var roundTripper http.RoundTripper = transport
+	if len(authMethods) > 0 {
+		roundTripper = &auth.RoundTripper{
+			Transport: transport,
+			Auths:     authMethods,
 		}
 	}
 
diff --git a/internal/storage/metricstore/prometheus/metricstore/reader.go b/internal/storage/metricstore/prometheus/metricstore/reader.go
index cf3aa2d5..19f33a75 100644
--- a/internal/storage/metricstore/prometheus/metricstore/reader.go
+++ b/internal/storage/metricstore/prometheus/metricstore/reader.go
@@ -9,8 +9,6 @@ import (
 	"net"
 	"net/http"
 	"net/url"
-	"os"
-	"path/filepath"
 	"strings"
 	"time"
 	"unicode"
@@ -23,6 +21,7 @@ import (
 	"go.uber.org/zap"
 
 	"github.com/jaegertracing/jaeger/internal/auth"
+	"github.com/jaegertracing/jaeger/internal/auth/bearertoken"
 	config "github.com/jaegertracing/jaeger/internal/config/promcfg"
 	"github.com/jaegertracing/jaeger/internal/proto-gen/api_v2/metrics"
 	"github.com/jaegertracing/jaeger/internal/storage/metricstore/prometheus/metricstore/dbmodel"
@@ -362,26 +361,32 @@ func getHTTPRoundTripper(c *config.Configuration) (rt http.RoundTripper, err err
 		TLSHandshakeTimeout: 10 * time.Second,
 		TLSClientConfig:     ctlsConfig,
 	}
-
-	token := ""
+	// dynamic token loader with interval-based caching
+	var tokenFn func() string
 	if c.TokenFilePath != "" {
-		tokenFromFile, err := loadToken(c.TokenFilePath)
+		var err error
+		tokenFn, err = auth.TokenProvider(
+			c.TokenFilePath,
+			10*time.Second,
+			nil,
+		)
 		if err != nil {
 			return nil, err
 		}
-		token = tokenFromFile
+	}
+	// Only set FromCtxFn if token override from context is enabled
+	var fromCtxFn func(context.Context) (string, bool)
+	if c.TokenOverrideFromContext {
+		fromCtxFn = bearertoken.GetBearerToken
 	}
 	return &auth.RoundTripper{
-		Transport:       httpTransport,
-		OverrideFromCtx: c.TokenOverrideFromContext,
-		StaticToken:     token,
+		Transport: httpTransport,
+		Auths: []auth.Method{
+			{
+				Scheme:  "Bearer",
+				TokenFn: tokenFn,
+				FromCtx: fromCtxFn,
+			},
+		},
 	}, nil
 }
-
-func loadToken(path string) (string, error) {
-	b, err := os.ReadFile(filepath.Clean(path))
-	if err != nil {
-		return "", fmt.Errorf("failed to get token from file: %w", err)
-	}
-	return strings.TrimRight(string(b), "\r\n"), nil
-}
diff --git a/internal/storage/v1/elasticsearch/options.go b/internal/storage/v1/elasticsearch/options.go
index 1f084701..e9b7391a 100644
--- a/internal/storage/v1/elasticsearch/options.go
+++ b/internal/storage/v1/elasticsearch/options.go
@@ -24,7 +24,8 @@ const (
 	suffixDisableHealthCheck             = ".disable-health-check"
 	suffixSnifferTLSEnabled              = ".sniffer-tls-enabled"
 	suffixTokenPath                      = ".token-file"
-	suffixBearerTokenPropagation         = ".bearer-token-propagation" // #nosec G101
+	suffixBearerTokenPropagation         = ".bearer-token-propagation"     // #nosec G101
+	suffixBearerTokenReloadInterval      = ".bearer-token-reload-interval" //#nosec G101
 	suffixPasswordPath                   = ".password-file"
 	suffixServerURLs                     = ".server-urls"
 	suffixRemoteReadClusters             = ".remote-read-clusters"
@@ -143,6 +144,7 @@ func addFlags(flagSet *flag.FlagSet, nsConfig *namespaceConfig) {
 		passwordPath                string
 		tokenPath                   string
 		bearerTokenAllowFromContext bool
+		reloadInterval              time.Duration = 10 * time.Second
 	)
 
 	if nsConfig.Authentication.BasicAuthentication.HasValue() {
@@ -156,6 +158,9 @@ func addFlags(flagSet *flag.FlagSet, nsConfig *namespaceConfig) {
 		bearerAuth := nsConfig.Authentication.BearerTokenAuthentication.Get()
 		tokenPath = bearerAuth.FilePath
 		bearerTokenAllowFromContext = bearerAuth.AllowFromContext
+		if bearerAuth.ReloadInterval != 0 {
+			reloadInterval = bearerAuth.ReloadInterval
+		}
 	}
 
 	flagSet.String(
@@ -171,6 +176,10 @@ func addFlags(flagSet *flag.FlagSet, nsConfig *namespaceConfig) {
 		tokenPath,
 		"Path to a file containing bearer token. This flag also loads CA if it is specified.")
 
+	flagSet.Duration(
+		nsConfig.namespace+suffixBearerTokenReloadInterval,
+		reloadInterval,
+		"Interval for reloading bearer token from file. Set to 0 to disable automatic reloading.")
 	flagSet.Bool(
 		nsConfig.namespace+suffixBearerTokenPropagation,
 		bearerTokenAllowFromContext,
@@ -361,15 +370,15 @@ func initFromViper(cfg *namespaceConfig, v *viper.Viper) {
 	// BearerAuthentication if tokenPath or allowFromContext is set
 	tokenPath := v.GetString(cfg.namespace + suffixTokenPath)
 	bearerTokenAllowFromContext := v.GetBool(cfg.namespace + suffixBearerTokenPropagation)
-
 	// Create BearerTokenAuthentication if either field is configured
 	if tokenPath != "" || bearerTokenAllowFromContext {
+		reloadInterval := v.GetDuration(cfg.namespace + suffixBearerTokenReloadInterval)
 		cfg.Authentication.BearerTokenAuthentication = configoptional.Some(config.BearerTokenAuthentication{
 			FilePath:         tokenPath,
 			AllowFromContext: bearerTokenAllowFromContext,
+			ReloadInterval:   reloadInterval,
 		})
 	}
-
 	cfg.Sniffing.Enabled = v.GetBool(cfg.namespace + suffixSniffer)
 	cfg.Sniffing.UseHTTPS = v.GetBool(cfg.namespace + suffixSnifferTLSEnabled)
 	cfg.DisableHealthCheck = v.GetBool(cfg.namespace + suffixDisableHealthCheck)



Test patch:
diff --git a/cmd/all-in-one/setupcontext/setupcontext_test.go b/cmd/all-in-one/setupcontext/setupcontext_test.go
deleted file mode 100644
index 5da20a0e..00000000
--- a/cmd/all-in-one/setupcontext/setupcontext_test.go
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) 2019 The Jaeger Authors.
-// SPDX-License-Identifier: Apache-2.0
-
-package setupcontext
-
-import (
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-
-	"github.com/jaegertracing/jaeger/internal/testutils"
-)
-
-func TestSetupContext(t *testing.T) {
-	// purely for code coverage
-	SetAllInOne()
-	defer UnsetAllInOne()
-	assert.True(t, IsAllInOne())
-}
-
-func TestMain(m *testing.M) {
-	testutils.VerifyGoLeaks(m)
-}
diff --git a/internal/auth/tokenloader_test.go b/internal/auth/tokenloader_test.go
new file mode 100644
index 00000000..e49842e3
--- /dev/null
+++ b/internal/auth/tokenloader_test.go
@@ -0,0 +1,316 @@
+// Copyright (c) 2025 The Jaeger Authors.
+// SPDX-License-Identifier: Apache-2.0
+
+package auth
+
+import (
+	"fmt"
+	"os"
+	"strings"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"go.uber.org/zap"
+	"go.uber.org/zap/zapcore"
+	"go.uber.org/zap/zaptest/observer"
+)
+
+// TestCachedFileTokenLoader_Deterministic covers basic cache and reload logic with mock time
+func TestCachedFileTokenLoader_Deterministic(t *testing.T) {
+	currentTime := time.Unix(0, 0)
+	timeFn := func() time.Time { return currentTime }
+
+	tokenFile := createTempTokenFile(t, "my-secret-token\n")
+
+	loader := cachedFileTokenLoader(tokenFile, 100*time.Millisecond, timeFn)
+
+	// T=0: First load - should read from file
+	token1, err := loader()
+	require.NoError(t, err)
+	assert.Equal(t, "my-secret-token", token1)
+
+	// Change the file content
+	updateTokenFile(t, tokenFile, "new-token\n")
+
+	// T=50ms: Still within cache interval (< 100ms)
+	currentTime = currentTime.Add(50 * time.Millisecond)
+	token2, err := loader()
+	require.NoError(t, err)
+	assert.Equal(t, "my-secret-token", token2, "Should return cached token within interval")
+
+	// T=150ms: Beyond cache interval (> 100ms)
+	currentTime = currentTime.Add(100 * time.Millisecond) // Total: 150ms
+	token3, err := loader()
+	require.NoError(t, err)
+	assert.Equal(t, "new-token", token3, "Should return refreshed token after cache expires")
+}
+
+// TestCachedFileTokenLoader_ExactBoundaries tests exact cache boundary conditions
+func TestCachedFileTokenLoader_ExactBoundaries(t *testing.T) {
+	currentTime := time.Unix(0, 0)
+	timeFn := func() time.Time { return currentTime }
+	tokenFile := createTempTokenFile(t, "boundary-token\n")
+	cacheInterval := 200 * time.Millisecond
+
+	loader := cachedFileTokenLoader(tokenFile, cacheInterval, timeFn)
+
+	// Load initial token
+	token, err := loader()
+	require.NoError(t, err)
+	assert.Equal(t, "boundary-token", token)
+
+	// Update file
+	updateTokenFile(t, tokenFile, "boundary-updated\n")
+
+	// Test exact boundary conditions
+	testCases := []struct {
+		timeAdvance   time.Duration
+		expectedToken string
+		description   string
+	}{
+		{199 * time.Millisecond, "boundary-token", "1ms before cache expires"},
+		{1 * time.Millisecond, "boundary-updated", "exactly at cache expiry"},
+		{50 * time.Millisecond, "boundary-updated", "well past cache expiry"},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.description, func(t *testing.T) {
+			currentTime = currentTime.Add(tc.timeAdvance)
+			token, err := loader()
+			require.NoError(t, err)
+			assert.Equal(t, tc.expectedToken, token, tc.description)
+		})
+	}
+}
+
+// TestCachedFileTokenLoader_ZeroInterval tests disabled reloading
+func TestCachedFileTokenLoader_ZeroInterval(t *testing.T) {
+	currentTime := time.Unix(0, 0)
+	timeFn := func() time.Time { return currentTime }
+	tokenFile := createTempTokenFile(t, "initial-token\n")
+	loader := cachedFileTokenLoader(tokenFile, 0, timeFn) // Zero interval = no reloading
+
+	// T=0: Initial load
+	token, err := loader()
+	require.NoError(t, err)
+	assert.Equal(t, "initial-token", token)
+
+	// Update file content
+	updateTokenFile(t, tokenFile, "updated-token\n")
+
+	// Should still return cached token (no reloading)
+	currentTime = currentTime.Add(1 * time.Hour) // Advance time significantly
+	token, err = loader()
+	require.NoError(t, err)
+	assert.Equal(t, "initial-token", token, "Zero interval should disable reloading completely")
+
+	// Multiple calls should continue returning cached token
+	for i := 0; i < 5; i++ {
+		// Generate different content for each iteration
+		newContent := fmt.Sprintf("different-token-%d-%d\n", i, currentTime.Unix())
+		updateTokenFile(t, tokenFile, newContent)
+
+		currentTime = currentTime.Add(1 * time.Hour)
+		token, err = loader()
+		require.NoError(t, err)
+		assert.Equal(t, "initial-token", token,
+			"Should always return initially cached token despite file change to %s at time %v",
+			strings.TrimSpace(newContent), currentTime)
+	}
+}
+
+// TestNewTokenProvider_InitialLoad covers initial load and fail-fast scenarios
+func TestNewTokenProvider_InitialLoad(t *testing.T) {
+	// Test successful initial load
+	tokenFile := createTempTokenFile(t, "initial-token\n")
+
+	tokenFn, err := TokenProvider(tokenFile, 100*time.Millisecond, nil)
+	require.NoError(t, err, "TokenProvider should not fail with valid token file")
+	assert.Equal(t, "initial-token", tokenFn(), "Token should match file contents")
+
+	// Test fail-fast on invalid file
+	_, err = TokenProvider("/nonexistent/file", 100*time.Millisecond, nil)
+	require.Error(t, err, "TokenProvider should fail fast on missing file")
+	assert.Contains(t, err.Error(), "failed to get token from file", "Error message should indicate token loading failure")
+
+	// Test empty file
+	emptyFile := createTempTokenFile(t, "")
+	tokenFn, err = TokenProvider(emptyFile, 100*time.Millisecond, nil)
+	require.NoError(t, err)
+	assert.Empty(t, tokenFn(), "Empty file should return empty token")
+	// Test file with trailing whitespace - properly trimmed
+	whitespaceFile := createTempTokenFile(t, "my-secret-token\r\n")
+	tokenFn, err = TokenProvider(whitespaceFile, 100*time.Millisecond, nil)
+	require.NoError(t, err)
+	assert.Equal(t, "my-secret-token", tokenFn(), "\\r\\n should be properly trimmed from end")
+}
+
+// TestNewTokenProvider_ReloadErrors_Deterministic ensures reload errors log and return cached token
+func TestNewTokenProvider_ReloadErrors_Deterministic(t *testing.T) {
+	currentTime := time.Unix(0, 0)
+	timeFn := func() time.Time { return currentTime }
+	tokenFile := createTempTokenFile(t, "initial-token\n")
+
+	// Create an observed zap logger
+	core, logs := observer.New(zapcore.InfoLevel)
+	logger := zap.New(core)
+
+	// Initialize token provider with mock time
+	tokenFn, err := TokenProviderWithTime(tokenFile, 10*time.Millisecond, logger, timeFn)
+	require.NoError(t, err)
+
+	// Initial call should succeed
+	token := tokenFn()
+	assert.Equal(t, "initial-token", token)
+
+	// Remove the file to force reload error
+	os.Remove(tokenFile)
+
+	// Advance time beyond cache interval
+	currentTime = currentTime.Add(15 * time.Millisecond)
+
+	// Call should return last cached token and log error
+	token = tokenFn()
+	assert.Equal(t, "initial-token", token, "Should return cached token even after file deletion")
+
+	// Verify the error was logged
+	require.Equal(t, 1, logs.Len(), "Expected one log message")
+	logEntry := logs.All()[0]
+	assert.Equal(t, "Token reload failed", logEntry.Message, "Expected log message to match")
+	assert.Equal(t, zapcore.WarnLevel, logEntry.Level, "Expected warning level log")
+}
+
+// TestTokenProviderWithTime_DirectCall tests the time-injectable function directly
+func TestTokenProviderWithTime_DirectCall(t *testing.T) {
+	currentTime := time.Unix(0, 0)
+	timeFn := func() time.Time { return currentTime }
+	tokenFile := createTempTokenFile(t, "time-test-token\n")
+	logger := zap.NewNop()
+
+	// Create token provider with mock time
+	tokenFn, err := TokenProviderWithTime(tokenFile, 200*time.Millisecond, logger, timeFn)
+	require.NoError(t, err)
+	require.NotNil(t, tokenFn)
+
+	// Test initial token
+	token := tokenFn()
+	assert.Equal(t, "time-test-token", token)
+
+	// Update file
+	updateTokenFile(t, tokenFile, "time-updated\n")
+
+	// Should still return cached token
+	currentTime = currentTime.Add(100 * time.Millisecond)
+	token = tokenFn()
+	assert.Equal(t, "time-test-token", token)
+
+	// Should return updated token after cache expires
+	currentTime = currentTime.Add(150 * time.Millisecond)
+	token = tokenFn()
+	assert.Equal(t, "time-updated", token)
+}
+
+// TestNewTokenProvider_WithZapLogger ensures zap logger is used properly
+func TestNewTokenProvider_WithZapLogger(t *testing.T) {
+	currentTime := time.Unix(0, 0)
+	timeFn := func() time.Time { return currentTime }
+	tokenFile := createTempTokenFile(t, "initial-token\n")
+
+	// Create an observed zap logger
+	core, logs := observer.New(zapcore.InfoLevel)
+	logger := zap.New(core)
+
+	// Initialize token provider with structured logger
+	tokenFn, err := TokenProviderWithTime(tokenFile, 10*time.Millisecond, logger, timeFn)
+	require.NoError(t, err)
+
+	// Initial call should succeed
+	token := tokenFn()
+	assert.Equal(t, "initial-token", token)
+
+	// No logs yet
+	assert.Equal(t, 0, logs.Len(), "No logs should be emitted for successful loads")
+
+	// Remove the file to force reload error
+	os.Remove(tokenFile)
+
+	// Advance time to trigger reload
+	currentTime = currentTime.Add(15 * time.Millisecond)
+
+	// Call should log using zap logger
+	tokenFn()
+	assert.Equal(t, 1, logs.Len(), "Error should be logged using zap")
+
+	logEntry := logs.All()[0]
+	assert.Equal(t, "Token reload failed", logEntry.Message, "Log message should match")
+	assert.NotNil(t, logEntry.Context[0].Interface, "Error should be attached to log")
+}
+
+// TestCachedFileTokenLoader_FilePermissions tests file permission errors
+func TestCachedFileTokenLoader_FilePermissions(t *testing.T) {
+	if os.Getuid() == 0 {
+		t.Skip("Running as root - file permission tests not meaningful")
+	}
+
+	currentTime := time.Unix(0, 0)
+	timeFn := func() time.Time { return currentTime }
+
+	// Create a file and make it unreadable
+	tokenFile := createTempTokenFile(t, "permission-test\n")
+	err := os.Chmod(tokenFile, 0o000) // No permissions
+	require.NoError(t, err)
+
+	// Cleanup with restored permissions
+	t.Cleanup(func() {
+		os.Chmod(tokenFile, 0o644)
+		os.Remove(tokenFile)
+	})
+
+	loader := cachedFileTokenLoader(tokenFile, 100*time.Millisecond, timeFn)
+
+	// Should return permission error
+	_, err = loader()
+	require.Error(t, err)
+	assert.Contains(t, err.Error(), "permission denied")
+}
+
+func TestTokenProvider_Wrapper(t *testing.T) {
+	tokenFile := createTempTokenFile(t, "production-test-token\n")
+	logger := zap.NewNop()
+
+	// Test that TokenProvider uses real time
+	tokenFn, err := TokenProvider(tokenFile, 100*time.Millisecond, logger)
+	require.NoError(t, err)
+
+	token := tokenFn()
+	assert.Equal(t, "production-test-token", token)
+}
+
+// Helper functions
+
+// createTempTokenFile creates a temp file with the given content and returns its name
+func createTempTokenFile(t *testing.T, content string) string {
+	t.Helper()
+
+	tmpFile, err := os.CreateTemp(t.TempDir(), "token-*.txt")
+	require.NoError(t, err, "Failed to create temp file")
+
+	_, err = tmpFile.WriteString(content)
+	require.NoError(t, err, "Failed to write to temp file")
+
+	err = tmpFile.Close()
+	require.NoError(t, err, "Failed to close temp file")
+
+	// Cleanup happens automatically with t.TempDir()
+	return tmpFile.Name()
+}
+
+// updateTokenFile updates an existing token file with new content
+func updateTokenFile(t *testing.T, filename, content string) {
+	t.Helper()
+
+	err := os.WriteFile(filename, []byte(content), 0o600)
+	require.NoError(t, err, "Failed to update token file")
+}
diff --git a/internal/auth/transport_test.go b/internal/auth/transport_test.go
index 6747d8c1..a70e9745 100644
--- a/internal/auth/transport_test.go
+++ b/internal/auth/transport_test.go
@@ -6,7 +6,6 @@ package auth
 import (
 	"context"
 	"net/http"
-	"net/http/httptest"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -22,80 +21,135 @@ func (s roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) {
 }
 
 func TestRoundTripper(t *testing.T) {
-	for _, tc := range []struct {
-		name             string
-		staticToken      string
-		overrideFromCtx  bool
-		wrappedTransport http.RoundTripper
-		requestContext   context.Context
-		wantError        bool
+	tests := []struct {
+		name               string
+		auths              []Method
+		requestContext     context.Context
+		expectedHeaders    []string // Expected Authorization headers
+		expectError        bool
+		expectNoAuthHeader bool
 	}{
 		{
-			name: "Default RoundTripper and request context set should have empty Bearer token",
-			wrappedTransport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
-				assert.Empty(t, r.Header.Get("Authorization"))
-				return &http.Response{
-					StatusCode: http.StatusOK,
-				}, nil
-			}),
-			requestContext: bearertoken.ContextWithBearerToken(context.Background(), "tokenFromContext"),
+			name:               "No auth configs - no headers added",
+			auths:              []Method{},
+			requestContext:     context.Background(),
+			expectNoAuthHeader: true,
 		},
 		{
-			name:            "Override from context provided, and request context set should use request context token",
-			overrideFromCtx: true,
-			wrappedTransport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
-				assert.Equal(t, "Bearer tokenFromContext", r.Header.Get("Authorization"))
-				return &http.Response{
-					StatusCode: http.StatusOK,
-				}, nil
-			}),
-			requestContext: bearertoken.ContextWithBearerToken(context.Background(), "tokenFromContext"),
+			name: "Single Bearer auth with static token",
+			auths: []Method{
+				{
+					Scheme:  "Bearer",
+					TokenFn: func() string { return "static-token" },
+				},
+			},
+			requestContext:  context.Background(),
+			expectedHeaders: []string{"Bearer static-token"},
 		},
 		{
-			name:            "Allow override from context and token provided, and request context unset should use defaultToken",
-			overrideFromCtx: true,
-			staticToken:     "initToken",
-			wrappedTransport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
-				assert.Equal(t, "Bearer initToken", r.Header.Get("Authorization"))
-				return &http.Response{}, nil
-			}),
-			requestContext: context.Background(),
+			name: "Bearer auth with context override - context token used",
+			auths: []Method{
+				{
+					Scheme:  "Bearer",
+					TokenFn: func() string { return "static-token" },
+					FromCtx: bearertoken.GetBearerToken,
+				},
+			},
+			requestContext:  bearertoken.ContextWithBearerToken(context.Background(), "context-token"),
+			expectedHeaders: []string{"Bearer context-token"},
+		},
+		{
+			name: "Multiple auth methods - both tokens added",
+			auths: []Method{
+				{
+					Scheme:  "Bearer",
+					TokenFn: func() string { return "bearer-token" },
+				},
+				{
+					Scheme:  "ApiKey",
+					TokenFn: func() string { return "api-key-value" },
+				},
+			},
+			requestContext:  context.Background(),
+			expectedHeaders: []string{"Bearer bearer-token", "ApiKey api-key-value"},
+		},
+		{
+			name: "Auth config with empty token - no header added",
+			auths: []Method{
+				{
+					Scheme:  "Bearer",
+					TokenFn: func() string { return "" }, // Empty token
+				},
+				{
+					Scheme:  "ApiKey",
+					TokenFn: func() string { return "valid-key" },
+				},
+			},
+			requestContext:  context.Background(),
+			expectedHeaders: []string{"ApiKey valid-key"}, // Only valid token
 		},
 		{
-			name:            "Allow override from context and token provided, and request context set should use context token",
-			overrideFromCtx: true,
-			staticToken:     "initToken",
-			wrappedTransport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
-				assert.Equal(t, "Bearer tokenFromContext", r.Header.Get("Authorization"))
-				return &http.Response{}, nil
-			}),
-			requestContext: bearertoken.ContextWithBearerToken(context.Background(), "tokenFromContext"),
+			name: "Only context extraction, no context token - no header added",
+			auths: []Method{
+				{
+					Scheme:  "Bearer",
+					FromCtx: bearertoken.GetBearerToken,
+				},
+			},
+			requestContext:     context.Background(),
+			expectNoAuthHeader: true,
 		},
 		{
-			name:           "Nil roundTripper provided should return an error",
+			name:           "Nil transport - should return error",
+			auths:          []Method{},
 			requestContext: context.Background(),
-			wantError:      true,
+			expectError:    true,
 		},
-	} {
+	}
+
+	for _, tc := range tests {
 		t.Run(tc.name, func(t *testing.T) {
-			server := httptest.NewServer(nil)
-			defer server.Close()
-			req, err := http.NewRequestWithContext(tc.requestContext, http.MethodGet, server.URL, nil)
+			t.Parallel() // Enable parallel execution for faster tests
+
+			// Each test gets its own isolated capturedRequest and transport
+			var capturedRequest *http.Request
+			wrappedTransport := roundTripFunc(func(r *http.Request) (*http.Response, error) {
+				capturedRequest = r
+				return &http.Response{StatusCode: http.StatusOK}, nil
+			})
+
+			req, err := http.NewRequestWithContext(tc.requestContext, http.MethodGet, "http://fake.example.com/api", nil)
 			require.NoError(t, err)
 
+			var transport http.RoundTripper = wrappedTransport
+			if tc.expectError {
+				transport = nil // Force nil transport for error test
+			}
+
 			tr := RoundTripper{
-				Transport:       tc.wrappedTransport,
-				OverrideFromCtx: tc.overrideFromCtx,
-				StaticToken:     tc.staticToken,
+				Transport: transport,
+				Auths:     tc.auths,
 			}
+
 			resp, err := tr.RoundTrip(req)
 
-			if tc.wantError {
+			if tc.expectError {
 				assert.Nil(t, resp)
 				require.Error(t, err)
-			} else {
-				assert.NotNil(t, resp)
-				require.NoError(t, err)
+				assert.Contains(t, err.Error(), "no http.RoundTripper provided")
+				return
+			}
+
+			require.NoError(t, err)
+			assert.NotNil(t, resp)
+			require.NotNil(t, capturedRequest, "Request should have been captured")
+
+			// Perform assertions on captured request
+			if tc.expectNoAuthHeader {
+				assert.Empty(t, capturedRequest.Header.Get("Authorization"))
+			} else if len(tc.expectedHeaders) > 0 {
+				authHeaders := capturedRequest.Header["Authorization"]
+				assert.ElementsMatch(t, tc.expectedHeaders, authHeaders)
 			}
 		})
 	}
diff --git a/internal/storage/elasticsearch/config/auth_helper_test.go b/internal/storage/elasticsearch/config/auth_helper_test.go
new file mode 100644
index 00000000..6b2bfc99
--- /dev/null
+++ b/internal/storage/elasticsearch/config/auth_helper_test.go
@@ -0,0 +1,229 @@
+// Copyright (c) 2025 The Jaeger Authors.
+// SPDX-License-Identifier: Apache-2.0
+
+package config
+
+import (
+	"os"
+	"path/filepath"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"go.uber.org/zap"
+	"go.uber.org/zap/zaptest/observer"
+
+	"github.com/jaegertracing/jaeger/internal/auth"
+)
+
+// Test the  initBearerAuth function that uses real time
+func TestInitBearerAuth(t *testing.T) {
+	logger := zap.NewNop()
+	tempDir := t.TempDir()
+	bearerFile := filepath.Join(tempDir, "bearer-token")
+	require.NoError(t, os.WriteFile(bearerFile, []byte("test-token"), 0o600))
+
+	tests := []struct {
+		name        string
+		bearerAuth  *BearerTokenAuthentication
+		expectError bool
+		expectNil   bool
+		validate    func(t *testing.T, method *auth.Method)
+	}{
+		{
+			name: "Valid file-based bearer auth",
+			bearerAuth: &BearerTokenAuthentication{
+				FilePath: bearerFile,
+			},
+			expectError: false,
+			expectNil:   false,
+			validate: func(t *testing.T, method *auth.Method) {
+				require.NotNil(t, method)
+				assert.Equal(t, "Bearer", method.Scheme)
+				assert.NotNil(t, method.TokenFn)
+				assert.Equal(t, "test-token", method.TokenFn())
+				assert.Nil(t, method.FromCtx)
+			},
+		},
+		{
+			name: "Valid context-based bearer auth",
+			bearerAuth: &BearerTokenAuthentication{
+				AllowFromContext: true,
+			},
+			expectError: false,
+			expectNil:   false,
+			validate: func(t *testing.T, method *auth.Method) {
+				require.NotNil(t, method)
+				assert.Equal(t, "Bearer", method.Scheme)
+				assert.Nil(t, method.TokenFn)
+				assert.NotNil(t, method.FromCtx)
+			},
+		},
+		{
+			name: "Invalid bearer auth config returns nil",
+			bearerAuth: &BearerTokenAuthentication{
+				FilePath:         "",
+				AllowFromContext: false,
+			},
+			expectError: false,
+			expectNil:   true,
+			validate: func(t *testing.T, method *auth.Method) {
+				assert.Nil(t, method)
+			},
+		},
+	}
+
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+			method, err := initBearerAuth(tc.bearerAuth, logger)
+			if tc.expectError {
+				require.Error(t, err)
+				assert.Nil(t, method)
+			} else {
+				require.NoError(t, err)
+				if tc.expectNil {
+					assert.Nil(t, method)
+				} else {
+					tc.validate(t, method)
+				}
+			}
+		})
+	}
+}
+
+// Test the time-injectable function for advanced scenarios
+func TestInitBearerAuthWithTime(t *testing.T) {
+	logger := zap.NewNop()
+	tempDir := t.TempDir()
+
+	tests := []struct {
+		name               string
+		reloadInterval     time.Duration
+		timeAdvance        time.Duration
+		expectedFinalToken string
+		description        string
+	}{
+		{
+			name:               "Normal reloading behavior",
+			reloadInterval:     50 * time.Millisecond,
+			timeAdvance:        60 * time.Millisecond,
+			expectedFinalToken: "updated-token",
+			description:        "Should return updated token after cache expires",
+		},
+		{
+			name:               "Zero interval disables reloading",
+			reloadInterval:     0,
+			timeAdvance:        24 * time.Minute,
+			expectedFinalToken: "initial-token",
+			description:        "Zero interval should completely disable token reloading",
+		},
+	}
+
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+			tokenFile := filepath.Join(tempDir, "reloadable-token-"+tc.name)
+			require.NoError(t, os.WriteFile(tokenFile, []byte("initial-token"), 0o600))
+
+			currentTime := time.Unix(0, 0)
+			timeFn := func() time.Time { return currentTime }
+
+			bearer := &BearerTokenAuthentication{
+				FilePath:       tokenFile,
+				ReloadInterval: tc.reloadInterval,
+			}
+
+			// Initialize with mock time
+			method, err := initBearerAuthWithTime(bearer, logger, timeFn)
+			require.NoError(t, err)
+			require.NotNil(t, method)
+			require.NotNil(t, method.TokenFn)
+
+			// Initial token
+			token := method.TokenFn()
+			assert.Equal(t, "initial-token", token)
+
+			// Update the file
+			err = os.WriteFile(tokenFile, []byte("updated-token"), 0o600)
+			require.NoError(t, err)
+
+			// Advance time and test
+			currentTime = currentTime.Add(tc.timeAdvance)
+			token = method.TokenFn()
+			assert.Equal(t, tc.expectedFinalToken, token, tc.description)
+		})
+	}
+}
+
+// TestInitBearerAuth_FileErrors tests error handling for file operations in initBearerAuth
+func TestInitBearerAuth_FileErrors(t *testing.T) {
+	core, _ := observer.New(zap.WarnLevel)
+	logger := zap.New(core)
+
+	tests := []struct {
+		name          string
+		bearer        *BearerTokenAuthentication
+		expectedError bool
+		expectNil     bool
+		errorContains string
+	}{
+		{
+			name: "Non-existent file path - returns error",
+			bearer: &BearerTokenAuthentication{
+				FilePath:         "/non/existent/path/token.txt",
+				AllowFromContext: false,
+			},
+			expectedError: true,
+			expectNil:     true,
+			errorContains: "failed to get token from file",
+		},
+		{
+			name: "Non-existent file with context enabled - still returns error",
+			bearer: &BearerTokenAuthentication{
+				FilePath:         "/non/existent/path/token.txt",
+				AllowFromContext: true,
+				ReloadInterval:   5 * time.Second,
+			},
+			expectedError: true,
+			expectNil:     true,
+			errorContains: "failed to get token from file",
+		},
+		{
+			name: "Empty file path with context disabled - returns nil",
+			bearer: &BearerTokenAuthentication{
+				FilePath:         "",
+				AllowFromContext: false,
+			},
+			expectedError: false,
+			expectNil:     true,
+		},
+		{
+			name: "Directory instead of file - returns error",
+			bearer: &BearerTokenAuthentication{
+				FilePath:         "/tmp",
+				AllowFromContext: false,
+			},
+			expectedError: true,
+			expectNil:     true,
+			errorContains: "failed to get token from file",
+		},
+	}
+
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+			method, err := initBearerAuth(tc.bearer, logger)
+			if tc.expectedError {
+				require.Error(t, err)
+				assert.Contains(t, err.Error(), tc.errorContains)
+				assert.Nil(t, method)
+			} else {
+				require.NoError(t, err)
+				if tc.expectNil {
+					assert.Nil(t, method)
+				} else {
+					assert.NotNil(t, method)
+				}
+			}
+		})
+	}
+}
diff --git a/internal/storage/elasticsearch/config/config_test.go b/internal/storage/elasticsearch/config/config_test.go
index 47986ce1..f0d9fd6e 100644
--- a/internal/storage/elasticsearch/config/config_test.go
+++ b/internal/storage/elasticsearch/config/config_test.go
@@ -610,7 +610,6 @@ func TestTagKeysAsFields(t *testing.T) {
 
 	pwdFile := filepath.Join(t.TempDir(), "pwd")
 	require.NoError(t, os.WriteFile(pwdFile, []byte(pwd1), 0o600))
-
 	tests := []struct {
 		name         string
 		config       *Configuration
@@ -835,7 +834,6 @@ func TestHandleBulkAfterCallback_ErrorMetricsEmitted(t *testing.T) {
 		sm:     sm,
 		logger: logger,
 	}
-
 	bcb.invoke(batchID, fakeRequests, response, assert.AnError)
 
 	mf.AssertCounterMetrics(t,
@@ -878,7 +876,6 @@ func TestHandleBulkAfterCallback_MissingStartTime(t *testing.T) {
 		sm:     sm,
 		logger: logger,
 	}
-
 	bcb.invoke(batchID, fakeRequests, response, assert.AnError)
 
 	mf.AssertCounterMetrics(t,
@@ -899,9 +896,7 @@ func TestHandleBulkAfterCallback_MissingStartTime(t *testing.T) {
 
 func TestGetConfigOptions(t *testing.T) {
 	tmpDir := t.TempDir()
-	apiKeyFile := filepath.Join(tmpDir, "apikey")
 	bearerTokenFile := filepath.Join(tmpDir, "bearertoken")
-	os.WriteFile(apiKeyFile, []byte("test-api-key"), 0o600)
 	os.WriteFile(bearerTokenFile, []byte("file-bearer-token"), 0o600)
 
 	tests := []struct {
@@ -1225,6 +1220,29 @@ func TestGetHTTPRoundTripper(t *testing.T) {
 				assert.True(t, transport.TLSClientConfig.InsecureSkipVerify)
 			},
 		},
+		{
+			name: "Bearer auth not applicable (empty config)",
+			cfg: &Configuration{
+				TLS: configtls.ClientConfig{Insecure: true},
+				Authentication: Authentication{
+					BearerTokenAuthentication: configoptional.Some(BearerTokenAuthentication{
+						FilePath:         "",    // Empty
+						AllowFromContext: false, // Disabled
+					}),
+				},
+			},
+			ctx: context.Background(),
+			validate: func(t *testing.T, rt http.RoundTripper) {
+				assert.NotNil(t, rt)
+				// Should be plain transport since auth is not applicable
+				_, ok := rt.(*auth.RoundTripper)
+				assert.False(t, ok, "Should not be an auth round tripper when config is not applicable")
+
+				transport, ok := rt.(*http.Transport)
+				assert.True(t, ok, "Should be plain http.Transport")
+				assert.True(t, transport.TLSClientConfig.InsecureSkipVerify)
+			},
+		},
 		{
 			name: "Secure mode with bearer token from file",
 			cfg: &Configuration{
@@ -1238,7 +1256,10 @@ func TestGetHTTPRoundTripper(t *testing.T) {
 				assert.NotNil(t, rt)
 				authRT, ok := rt.(*auth.RoundTripper)
 				require.True(t, ok, "Should be an auth round tripper")
-				assert.Equal(t, "file-bearer-token", authRT.StaticToken)
+				require.Len(t, authRT.Auths, 1)
+				assert.Equal(t, "Bearer", authRT.Auths[0].Scheme)
+				assert.NotNil(t, authRT.Auths[0].TokenFn)
+				assert.Equal(t, "file-bearer-token", authRT.Auths[0].TokenFn())
 			},
 		},
 		{
@@ -1254,7 +1275,10 @@ func TestGetHTTPRoundTripper(t *testing.T) {
 				assert.NotNil(t, rt)
 				authRT, ok := rt.(*auth.RoundTripper)
 				require.True(t, ok, "Should be an auth round tripper")
-				assert.True(t, authRT.OverrideFromCtx)
+				require.Len(t, authRT.Auths, 1)
+				assert.Equal(t, "Bearer", authRT.Auths[0].Scheme)
+				assert.NotNil(t, authRT.Auths[0].FromCtx)
+
 				transport, ok := authRT.Transport.(*http.Transport)
 				require.True(t, ok)
 				assert.True(t, transport.TLSClientConfig.InsecureSkipVerify)
@@ -1306,6 +1330,7 @@ func TestLoadTokenFromFile(t *testing.T) {
 		const token = "test-token"
 		tokenFile := filepath.Join(t.TempDir(), "token")
 		require.NoError(t, os.WriteFile(tokenFile, []byte(token), 0o600))
+
 		loadedToken, err := loadTokenFromFile(tokenFile)
 		require.NoError(t, err)
 		assert.Equal(t, token, loadedToken)
@@ -1327,7 +1352,6 @@ func TestBulkCallbackInvoke_NilResponse(t *testing.T) {
 		sm:     sm,
 		logger: logger,
 	}
-
 	bcb.invoke(1, []elastic.BulkableRequest{nil}, nil, assert.AnError)
 
 	mf.AssertCounterMetrics(t,
diff --git a/internal/storage/metricstore/prometheus/metricstore/reader_test.go b/internal/storage/metricstore/prometheus/metricstore/reader_test.go
index 5245a2ea..5d5c59b1 100644
--- a/internal/storage/metricstore/prometheus/metricstore/reader_test.go
+++ b/internal/storage/metricstore/prometheus/metricstore/reader_test.go
@@ -731,19 +731,43 @@ func (s *fakePromServer) getAuth() string {
 
 func TestGetRoundTripperTLSConfig(t *testing.T) {
 	for _, tc := range []struct {
-		name       string
-		tlsEnabled bool
+		name      string
+		tlsConfig configtls.ClientConfig
+		wantErr   bool
 	}{
-		{"tls tlsEnabled", true},
-		{"tls disabled", false},
+		{
+			name: "tls enabled with cert verification",
+			tlsConfig: configtls.ClientConfig{
+				Insecure: false,
+			},
+		},
+		{
+			name: "tls enabled insecure",
+			tlsConfig: configtls.ClientConfig{
+				Insecure: true, // Skip verify
+			},
+		},
+		{
+			name: "invalid tls config",
+			tlsConfig: configtls.ClientConfig{
+				Config: configtls.Config{
+					CAFile: "foo",
+				},
+			},
+			wantErr: true,
+		},
 	} {
 		t.Run(tc.name, func(t *testing.T) {
 			config := &config.Configuration{
 				ConnectTimeout:           9 * time.Millisecond,
-				TLS:                      configtls.ClientConfig{},
+				TLS:                      tc.tlsConfig,
 				TokenOverrideFromContext: true,
 			}
 			rt, err := getHTTPRoundTripper(config)
+			if tc.wantErr {
+				require.Error(t, err)
+				return
+			}
 			require.NoError(t, err)
 
 			server := newFakePromServer(t)