[improve][client] Implement tls_client_auth for AuthenticationOAuth2#25538
[improve][client] Implement tls_client_auth for AuthenticationOAuth2#25538izumo27 wants to merge 8 commits intoapache:masterfrom
Conversation
lhotari
left a comment
There was a problem hiding this comment.
Thanks for the contribution! I added some review comments.
fabfb85 to
587959e
Compare
587959e to
0509796
Compare
There was a problem hiding this comment.
Pull request overview
This PR extends Pulsar’s OAuth2 client-credentials authentication to support RFC 8705 tls_client_auth (mutual TLS client authentication) alongside the existing client_secret_post mechanism.
Changes:
- Added a new
TlsClientAuthFlowandTokenEndpointAuthMethodto support mTLS-based token exchange. - Extended OAuth2 configuration and factory builder APIs to select token endpoint auth method and provide TLS cert/key inputs.
- Updated
TokenClientrequest construction plus added/updated tests for both auth methods.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/protocol/TokenEndpointAuthMethod.java | Introduces enum for token endpoint auth methods and parsing. |
| pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/protocol/ClientCredentialsExchangeRequest.java | Adds authMethod field to drive token request formatting. |
| pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/protocol/TokenClient.java | Conditionally includes client_secret based on auth method. |
| pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/TlsClientAuthFlow.java | New flow implementation for tls_client_auth. |
| pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/FlowBase.java | Adds TLS client cert configuration and periodic SSL context refresh scheduling. |
| pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/ClientCredentialsFlow.java | Wires auth method selection and passes TLS settings into FlowBase. |
| pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationOAuth2.java | Adds config param to select token endpoint auth method and chooses the appropriate flow. |
| pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationFactoryOAuth2.java | Extends builder to configure mTLS client auth inputs and flow selection. |
| pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/protocol/TokenClientTest.java | Updates/adds tests for request construction and exchange under both auth methods. |
| pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/TlsClientAuthFlowTest.java | Adds test for defaulting clientId when omitted in mTLS flow. |
| pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/OAuth2MockHttpClient.java | Adds test helper to mock SSL factory + async HTTP client construction. |
| pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationOAuth2Test.java | Adds tests validating config-driven flow selection for mTLS. |
| pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationFactoryOAuth2Test.java | Adds builder tests for mTLS flow selection and required parameter validation. |
| return; | ||
| } | ||
| sslRefreshScheduler = Executors.newSingleThreadScheduledExecutor( | ||
| new ExecutorProvider.ExtendedThreadFactory("oauth2-tls-cert-refresher")); |
| bodyMap.put("grant_type", "client_credentials"); | ||
| bodyMap.put("client_id", req.getClientId()); | ||
| bodyMap.put("client_secret", req.getClientSecret()); | ||
| if (req.getAuthMethod() == TokenEndpointAuthMethod.CLIENT_SECRET_POST) { |
| long autoCertRefreshSeconds = getParameterDurationToSeconds(CONFIG_PARAM_AUTO_CERT_REFRESH_DURATION, | ||
| autoCertRefreshDuration, DEFAULT_AUTO_CERT_REFRESH_DURATION); | ||
| scheduleSslContextRefreshIfEnabled(autoCertRefreshSeconds); |
| return value; | ||
| } | ||
|
|
||
| public static TokenEndpointAuthMethod fromValue(String value) { |
There was a problem hiding this comment.
Pull request overview
Adds support for OAuth2 tls_client_auth (RFC 8705) in the Pulsar Java client’s AuthenticationOAuth2 client-credentials flow, enabling mutual-TLS authentication to the token endpoint in addition to the existing client_secret_post.
Changes:
- Introduces
TokenEndpointAuthMethodand routes OAuth2 client-credentials configuration to eitherClientCredentialsFlow(client secret) or newTlsClientAuthFlow(mTLS). - Extends OAuth2 flow HTTP client setup to support client cert/key and periodic SSL context refresh.
- Adds/updates unit tests for TLS client auth configuration and token exchange behavior.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationFactoryOAuth2.java | Extends builder to select token endpoint auth method and configure TLS cert/key + refresh interval. |
| pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationOAuth2.java | Adds config parameter to select token endpoint auth method and instantiates the correct flow. |
| pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/ClientCredentialsFlow.java | Passes auth method into token exchange request; plumbs TLS cert/key + refresh duration parameters. |
| pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/FlowBase.java | Adds mTLS HTTP client configuration and scheduled SSL context refresh support. |
| pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/TlsClientAuthFlow.java | New flow implementing client-credentials token exchange using TLS client authentication. |
| pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/protocol/ClientCredentialsExchangeRequest.java | Adds token_endpoint_auth_method field to token exchange request model. |
| pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/protocol/TokenClient.java | Omits client_secret in the request body for non-client_secret_post methods. |
| pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/protocol/TokenEndpointAuthMethod.java | New enum defining supported token endpoint authentication methods. |
| pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationFactoryOAuth2Test.java | Adds coverage for builder selection of TLS flow and required TLS parameters. |
| pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationOAuth2Test.java | Adds coverage for configure() selecting TLS flow; uses SSL factory/http client construction mocking. |
| pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/OAuth2MockHttpClient.java | New test utility to mock SSL factory and async HTTP client construction. |
| pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/TlsClientAuthFlowTest.java | New test for defaulting clientId when not provided. |
| pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/protocol/TokenClientTest.java | Adds TLS-client-auth exchange tests and updates existing tests to set auth method. |
| OAuth2MockHttpClient.withMockedSslFactory(() -> { | ||
| TlsClientAuthFlow flow = TlsClientAuthFlow.fromParameters(params); | ||
| Field clientIdField = flow.getClass().getDeclaredField("clientId"); | ||
| clientIdField.setAccessible(true); | ||
| assertEquals((String) clientIdField.get(flow), "pulsar-client"); | ||
| flow.close(); |
| @JsonProperty("token_endpoint_auth_method") | ||
| private TokenEndpointAuthMethod authMethod; |
| String buildClientCredentialsBody(ClientCredentialsExchangeRequest req) { | ||
| Map<String, String> bodyMap = new TreeMap<>(); | ||
| bodyMap.put("grant_type", "client_credentials"); | ||
| bodyMap.put("client_id", req.getClientId()); | ||
| bodyMap.put("client_secret", req.getClientSecret()); | ||
| if (req.getAuthMethod() == TokenEndpointAuthMethod.CLIENT_SECRET_POST) { | ||
| bodyMap.put("client_secret", req.getClientSecret()); | ||
| } | ||
| // Only set audience and scope if they are non-empty. |
|
|
||
| protected static final Duration DEFAULT_CONNECT_TIMEOUT = Duration.ofSeconds(10); | ||
| protected static final Duration DEFAULT_READ_TIMEOUT = Duration.ofSeconds(30); | ||
| protected static final Duration DEFAULT_AUTO_CERT_REFRESH_DURATION = Duration.ofSeconds(300); |
| * @param tokenEndpointAuthMethod the token endpoint auth method | ||
| * @return the builder | ||
| */ | ||
| public ClientCredentialsBuilder tokenEndpointAuthMethod(TokenEndpointAuthMethod tokenEndpointAuthMethod) { | ||
| this.tokenEndpointAuthMethod = tokenEndpointAuthMethod; |
|
@lhotari PTAL, thanks. |
Motivation
Currently, the Client Credentials Flow in AuthenticationOAuth2 supports only authentication using
client_secret.This PR adds
tls_client_authusing a certificate.https://datatracker.ietf.org/doc/rfc8705/
Modifications
Added
TlsClientAuthFlowclass for the flow using a certificate.client_idis treated as an optional parameter for users.client_idis required by the RFC, it may not be necessary in cases such as Athenz. Allowing it to be optional as a client input provides greater flexibility. As long as the pulsar client includesclient_idinternally, it remains compliant with the RFC.Since a certificate is only required when obtaining a token, a different HTTP client is used than the one used for retrieving metadata.https://github.com/apache/pulsar/pull/25538/changes#diff-979a00edf42b23b8e525310f703a0ac1203538ed742f907bfd658af53f8b49f2R141-R146Verifying this change
This change added tests and can be verified as follows:
Does this pull request potentially affect one of the following parts: