Skip to content

Commit bc46337

Browse files
Feat: Add withHttpClient support (#234)
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
1 parent a852db3 commit bc46337

4 files changed

Lines changed: 367 additions & 10 deletions

File tree

src/main/java/com/auth0/AuthenticationController.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.auth0;
22

33
import com.auth0.jwk.JwkProvider;
4+
import com.auth0.net.client.Auth0HttpClient;
45
import com.google.common.annotations.VisibleForTesting;
56
import org.apache.commons.lang3.Validate;
67

@@ -73,6 +74,7 @@ public static class Builder {
7374
private final String clientSecret;
7475
private String responseType;
7576
private JwkProvider jwkProvider;
77+
private Auth0HttpClient httpClient;
7678
private Integer clockSkew;
7779
private Integer authenticationMaxAge;
7880
private boolean useLegacySameSiteCookie;
@@ -179,6 +181,39 @@ public Builder withJwkProvider(JwkProvider jwkProvider) {
179181
return this;
180182
}
181183

184+
/**
185+
* Sets a custom {@link Auth0HttpClient} to use for all HTTP requests made by this library
186+
* (token exchange, PAR, etc.). Use this to configure timeouts, proxies, or other HTTP settings.
187+
*
188+
* <p><strong>Note:</strong> When a custom {@code Auth0HttpClient} is provided, the
189+
* {@link AuthenticationController#setLoggingEnabled(boolean)} and
190+
* {@link AuthenticationController#doNotSendTelemetry()} settings will have no effect,
191+
* as those are configured at the HTTP client level. You should configure logging and
192+
* telemetry directly on the client instance before passing it here.</p>
193+
*
194+
* <pre>{@code
195+
* Auth0HttpClient httpClient = DefaultHttpClient.newBuilder()
196+
* .withConnectTimeout(10)
197+
* .withReadTimeout(10)
198+
* .telemetryEnabled(false)
199+
* .withLogging(new LoggingOptions(LoggingOptions.LogLevel.BODY))
200+
* .build();
201+
*
202+
* AuthenticationController controller = AuthenticationController
203+
* .newBuilder(domain, clientId, clientSecret)
204+
* .withHttpClient(httpClient)
205+
* .build();
206+
* }</pre>
207+
*
208+
* @param httpClient a configured {@link Auth0HttpClient} instance.
209+
* @return this same builder instance.
210+
*/
211+
public Builder withHttpClient(Auth0HttpClient httpClient) {
212+
Validate.notNull(httpClient, "httpClient must not be null");
213+
this.httpClient = httpClient;
214+
return this;
215+
}
216+
182217
/**
183218
* Sets the clock-skew or leeway value to use in the ID Token verification. The value must be in seconds.
184219
* Defaults to 60 seconds.
@@ -267,6 +302,9 @@ public AuthenticationController build() throws UnsupportedOperationException {
267302
if (jwkProvider != null) {
268303
builder.withJwkProvider(jwkProvider);
269304
}
305+
if (httpClient != null) {
306+
builder.withHttpClient(httpClient);
307+
}
270308

271309
return new AuthenticationController(builder.build());
272310
}

src/main/java/com/auth0/RequestProcessor.java

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import com.auth0.jwk.JwkException;
1212
import com.auth0.jwk.JwkProvider;
1313
import com.auth0.jwk.UrlJwkProvider;
14+
import com.auth0.net.client.Auth0HttpClient;
1415
import com.auth0.net.client.DefaultHttpClient;
1516
import com.auth0.utils.tokens.IdTokenVerifier;
1617
import com.auth0.utils.tokens.SignatureVerifier;
@@ -50,6 +51,7 @@ class RequestProcessor {
5051
private final String clientId;
5152
private final String clientSecret;
5253
private final JwkProvider jwkProvider;
54+
private Auth0HttpClient httpClient;
5355

5456
private final Integer clockSkew;
5557
private final Integer authenticationMaxAge;
@@ -71,6 +73,7 @@ static class Builder {
7173
private final String clientSecret;
7274

7375
private JwkProvider jwkProvider;
76+
private Auth0HttpClient httpClient;
7477
private boolean useLegacySameSiteCookie = true;
7578
private Integer clockSkew;
7679
private Integer authenticationMaxAge;
@@ -93,6 +96,11 @@ Builder withJwkProvider(JwkProvider jwkProvider) {
9396
return this;
9497
}
9598

99+
Builder withHttpClient(Auth0HttpClient httpClient) {
100+
this.httpClient = httpClient;
101+
return this;
102+
}
103+
96104
public Builder withClockSkew(Integer clockSkew) {
97105
this.clockSkew = clockSkew;
98106
return this;
@@ -125,20 +133,21 @@ Builder withInvitation(String invitation) {
125133

126134
RequestProcessor build() {
127135
return new RequestProcessor(domainProvider, responseType, clientId, clientSecret,
128-
jwkProvider, useLegacySameSiteCookie, clockSkew, authenticationMaxAge,
136+
jwkProvider, httpClient, useLegacySameSiteCookie, clockSkew, authenticationMaxAge,
129137
organization, invitation, cookiePath);
130138
}
131139
}
132140

133141
private RequestProcessor(DomainProvider domainProvider, String responseType, String clientId,
134-
String clientSecret, JwkProvider jwkProvider,
142+
String clientSecret, JwkProvider jwkProvider, Auth0HttpClient httpClient,
135143
boolean useLegacySameSiteCookie, Integer clockSkew, Integer authenticationMaxAge,
136144
String organization, String invitation, String cookiePath) {
137145
this.domainProvider = domainProvider;
138146
this.responseType = responseType;
139147
this.clientId = clientId;
140148
this.clientSecret = clientSecret;
141149
this.jwkProvider = jwkProvider;
150+
this.httpClient = httpClient;
142151
this.useLegacySameSiteCookie = useLegacySameSiteCookie;
143152
this.clockSkew = clockSkew;
144153
this.authenticationMaxAge = authenticationMaxAge;
@@ -156,18 +165,23 @@ void doNotSendTelemetry() {
156165
}
157166

158167
AuthAPI createClientForDomain(String domain) {
159-
DefaultHttpClient.Builder httpBuilder = DefaultHttpClient.newBuilder()
160-
.telemetryEnabled(!telemetryDisabled);
161-
162-
if (loggingEnabled) {
163-
httpBuilder.withLogging(new LoggingOptions(LoggingOptions.LogLevel.BODY));
164-
}
165-
166168
return AuthAPI.newBuilder(domain, clientId, clientSecret)
167-
.withHttpClient(httpBuilder.build())
169+
.withHttpClient(getHttpClient())
168170
.build();
169171
}
170172

173+
private Auth0HttpClient getHttpClient() {
174+
if (this.httpClient == null) {
175+
DefaultHttpClient.Builder httpBuilder = DefaultHttpClient.newBuilder()
176+
.telemetryEnabled(!telemetryDisabled);
177+
if (loggingEnabled) {
178+
httpBuilder.withLogging(new LoggingOptions(LoggingOptions.LogLevel.BODY));
179+
}
180+
this.httpClient = httpBuilder.build();
181+
}
182+
return this.httpClient;
183+
}
184+
171185
/**
172186
* Pre builds an Auth0 Authorize Url with the given redirect URI, state and nonce parameters.
173187
*

src/test/java/com/auth0/AuthenticationControllerTest.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.auth0;
22

33
import com.auth0.jwk.JwkProvider;
4+
import com.auth0.net.client.Auth0HttpClient;
45
import org.junit.jupiter.api.BeforeEach;
56
import org.junit.jupiter.api.Test;
67
import org.mockito.Mock;
@@ -31,6 +32,8 @@ public class AuthenticationControllerTest {
3132
@Mock
3233
private DomainResolver mockDomainResolver;
3334
@Mock
35+
private Auth0HttpClient mockHttpClient;
36+
@Mock
3437
private Tokens mockTokens;
3538

3639
private HttpServletRequest request;
@@ -82,6 +85,7 @@ public void shouldConfigureBuilderWithAllOptions() {
8285
AuthenticationController controller = AuthenticationController.newBuilder(DOMAIN, CLIENT_ID, CLIENT_SECRET)
8386
.withResponseType("id_token token")
8487
.withJwkProvider(mockJwkProvider)
88+
.withHttpClient(mockHttpClient)
8589
.withClockSkew(120)
8690
.withAuthenticationMaxAge(3600)
8791
.withLegacySameSiteCookie(false)
@@ -131,6 +135,7 @@ public void shouldValidateNullParameters() {
131135
assertThrows(NullPointerException.class, () -> builder.withDomain(null));
132136
assertThrows(NullPointerException.class, () -> builder.withResponseType(null));
133137
assertThrows(NullPointerException.class, () -> builder.withJwkProvider(null));
138+
assertThrows(NullPointerException.class, () -> builder.withHttpClient(null));
134139
assertThrows(NullPointerException.class, () -> builder.withClockSkew(null));
135140
assertThrows(NullPointerException.class, () -> builder.withAuthenticationMaxAge(null));
136141
assertThrows(NullPointerException.class, () -> builder.withOrganization(null));
@@ -421,4 +426,43 @@ public void shouldHandleImplicitGrantResponseType() {
421426

422427
assertThat(controller, is(notNullValue()));
423428
}
429+
430+
// --- HttpClient Configuration Tests ---
431+
432+
@Test
433+
public void shouldBuildWithCustomHttpClient() {
434+
AuthenticationController controller = AuthenticationController.newBuilder(DOMAIN, CLIENT_ID, CLIENT_SECRET)
435+
.withHttpClient(mockHttpClient)
436+
.build();
437+
438+
assertThat(controller, is(notNullValue()));
439+
assertThat(controller.getRequestProcessor(), is(notNullValue()));
440+
}
441+
442+
@Test
443+
public void shouldBuildWithCustomHttpClientAndJwkProvider() {
444+
AuthenticationController controller = AuthenticationController.newBuilder(DOMAIN, CLIENT_ID, CLIENT_SECRET)
445+
.withHttpClient(mockHttpClient)
446+
.withJwkProvider(mockJwkProvider)
447+
.build();
448+
449+
assertThat(controller, is(notNullValue()));
450+
}
451+
452+
@Test
453+
public void shouldBuildWithCustomHttpClientAndDomainResolver() {
454+
AuthenticationController controller = AuthenticationController
455+
.newBuilder(mockDomainResolver, CLIENT_ID, CLIENT_SECRET)
456+
.withHttpClient(mockHttpClient)
457+
.build();
458+
459+
assertThat(controller, is(notNullValue()));
460+
}
461+
462+
@Test
463+
public void shouldThrowExceptionWhenHttpClientIsNull() {
464+
AuthenticationController.Builder builder = AuthenticationController.newBuilder(DOMAIN, CLIENT_ID, CLIENT_SECRET);
465+
466+
assertThrows(NullPointerException.class, () -> builder.withHttpClient(null));
467+
}
424468
}

0 commit comments

Comments
 (0)