Skip to content

Bearertokenauth extension doesn't honor token auth with custom headers unless the header is specified in the Canonical format. #45697

@vanugrah

Description

@vanugrah

Component(s)

extension/bearertokenauth

What happened?

Description

The bearertokenauthextension fails to authenticate HTTP requests when configured with a custom header (e.g., X-API-Key) instead of the default Authorization header. This affects HTTP-based receivers like faroreceiver, prometheusreceiver, otlpreceiver (HTTP), and others that use the bearer token authenticator.

The root cause is that Go's HTTP server automatically canonicalizes all header names using http.CanonicalHeaderKey() (e.g., X-API-KeyX-Api-Key), but the extension's authentication logic doesn't account for this canonicalization when looking up custom headers. The current implementation attempts to look for the custom header in the header map with two specific keys; one in lowercase form of the provided custom header and the second using the exact string as specified in the config.

Steps to Reproduce

Here is a docker-compose example illustrating this behavior. Both the docker-compose.yml and otel-config.yaml config files have been attached.

  1. Download the following files:
  1. Spin up the docker-compose environment with the collector and test client by running docker-compose up. An OTLP HTTP receiver is configured with the bearertokenauth extension using the custom header X-API-KEY and token value secret-token-12345. The test client runs cURL commands to test the following cases:
  1. Header specified in lowercase: x-api-key
  2. Header specified in uppercase: X-API-KEY
  3. Header specified in mixed case: X-API-Key
  4. Header specified in the canonical form: X-Api-Key
  5. Header specified in the canonical form but with an incorrect token
  6. Missing custom header

Expected Result

The request should be authenticated successfully and processed by the receiver for test cases 1-4.
The request should be rejected for test cases 5 and 6.

Actual Result

The collector returns 401 Unauthorized for all test cases even though the correct token is provided for cases 1-4. The authentication fails because:

  1. Client sends the header: X-API-Key: secret-token-12345 (example mixed case)
  2. Go's HTTP server canonicalizes it to: X-Api-Key: secret-token-12345
  3. Extension checks for: x-api-key (lowercase) and X-API-Key (exact case)
  4. Extension misses: X-Api-Key (canonical form that's actually in the header map)
  5. Authentication fails → 401 error

Note: This only affects custom headers. The default Authorization header works correctly because it's already being looked up using the canonical form.

Workaround

The current workaround is to specify the custom header string in the config file specifically in the canonical form such that when the exact case check occurs on L228 it is compared to the canonical form of the header stored in the header map. Here is the same docker-compose example illustrating the work around:

We are currently using this work around and its fine for the time being - however ideally from a user perspective it should be acceptable to specify custom header string in any format given headers are case insensitive.

Collector version

v0.144.0 (latest docker image)

Environment information

OpenTelemetry Collector configuration

extensions:
  bearertokenauth:
    # Custom header instead of default "Authorization"
    header: "X-API-KEY"
    token: "secret-token-12345"
    scheme: ""
  health_check:
    endpoint: 0.0.0.0:13133
receivers:
  # Using OTLP HTTP receiver to demonstrate the bug
  otlp:
    protocols:
      http:
        endpoint: 0.0.0.0:12347
        auth:
          authenticator: bearertokenauth
exporters:
  debug:
    verbosity: detailed
service:
  extensions: [bearertokenauth, health_check]
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [debug]
    metrics:
      receivers: [otlp]
      exporters: [debug]
    logs:
      receivers: [otlp]
      exporters: [debug]
  telemetry:
    logs:
      level: info

Log output

Additional context

No response

Tip

React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it. Learn more here.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions