diff --git a/doc-snippets/configuration/src/main/java/otel/OtlpAuthenticationConfig.java b/doc-snippets/configuration/src/main/java/otel/OtlpAuthenticationConfig.java new file mode 100644 index 0000000000..769880616a --- /dev/null +++ b/doc-snippets/configuration/src/main/java/otel/OtlpAuthenticationConfig.java @@ -0,0 +1,89 @@ +package otel; + +import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter; +import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter; +import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter; +import java.time.Duration; +import java.time.Instant; +import java.util.Collections; +import java.util.Map; +import java.util.function.Supplier; + +public class OtlpAuthenticationConfig { + public static void staticAuthenticationHeader(String endpoint) { + // If the OTLP destination accepts a static, long-lived authentication header like an API key, + // set it as a header. + // This reads the API key from the OTLP_API_KEY env var to avoid hard coding the secret in + // source code. + String apiKeyHeaderName = "api-key"; + String apiKeyHeaderValue = System.getenv("OTLP_API_KEY"); + + // Initialize OTLP Span, Metric, and LogRecord exporters using a similar pattern + OtlpHttpSpanExporter spanExporter = + OtlpHttpSpanExporter.builder() + .setEndpoint(endpoint) + .addHeader(apiKeyHeaderName, apiKeyHeaderValue) + .build(); + OtlpHttpMetricExporter metricExporter = + OtlpHttpMetricExporter.builder() + .setEndpoint(endpoint) + .addHeader(apiKeyHeaderName, apiKeyHeaderValue) + .build(); + OtlpHttpLogRecordExporter logRecordExporter = + OtlpHttpLogRecordExporter.builder() + .setEndpoint(endpoint) + .addHeader(apiKeyHeaderName, apiKeyHeaderValue) + .build(); + } + + public static void dynamicAuthenticationHeader(String endpoint) { + // If the OTLP destination requires a dynamic authentication header, such as a JWT which needs + // to be periodically refreshed, use a header supplier. + // Here we implement a simple supplier which adds a header of the form "Authorization: Bearer + // ", where is fetched from refreshBearerToken every 10 minutes. + String username = System.getenv("OTLP_USERNAME"); + String password = System.getenv("OTLP_PASSWORD"); + Supplier> supplier = + new AuthHeaderSupplier(() -> refreshToken(username, password), Duration.ofMinutes(10)); + + // Initialize OTLP Span, Metric, and LogRecord exporters using a similar pattern + OtlpHttpSpanExporter spanExporter = + OtlpHttpSpanExporter.builder().setEndpoint(endpoint).setHeaders(supplier).build(); + OtlpHttpMetricExporter metricExporter = + OtlpHttpMetricExporter.builder().setEndpoint(endpoint).setHeaders(supplier).build(); + OtlpHttpLogRecordExporter logRecordExporter = + OtlpHttpLogRecordExporter.builder().setEndpoint(endpoint).setHeaders(supplier).build(); + } + + private static class AuthHeaderSupplier implements Supplier> { + private final Supplier tokenRefresher; + private final Duration tokenRefreshInterval; + private Instant refreshedAt = Instant.ofEpochMilli(0); + private String currentTokenValue; + + private AuthHeaderSupplier(Supplier tokenRefresher, Duration tokenRefreshInterval) { + this.tokenRefresher = tokenRefresher; + this.tokenRefreshInterval = tokenRefreshInterval; + } + + @Override + public Map get() { + return Collections.singletonMap("Authorization", "Bearer " + getToken()); + } + + private synchronized String getToken() { + Instant now = Instant.now(); + if (currentTokenValue == null || now.isAfter(refreshedAt.plus(tokenRefreshInterval))) { + currentTokenValue = tokenRefresher.get(); + refreshedAt = now; + } + return currentTokenValue; + } + } + + private static String refreshToken(String username, String password) { + // For a production scenario, this would be replaced with out-of-band request to exchange + // username / password for bearer token. + return "abc123"; + } +}