Skip to content

Commit dd52cc5

Browse files
gopalldbclaude
andauthored
Add configurable OAuthWebServerTimeout connection property (#1226)
## Summary - Adds a new `OAuthWebServerTimeout` connection property to configure the OAuth browser authentication timeout for U2M (user-to-machine) flows - Replaces the hardcoded 1-hour timeout in `ClientConfigurator.setupU2MConfig()` with the configurable value - Default is **120 seconds**, matching Simba driver parity ## Changes | File | Change | |------|--------| | `DatabricksJdbcUrlParams.java` | New `OAUTH_WEB_SERVER_TIMEOUT` enum entry (default: `"120"`) | | `IDatabricksConnectionContext.java` | Added `getOAuthWebServerTimeout()` interface method | | `DatabricksConnectionContext.java` | Implemented the getter | | `ClientConfigurator.java` | Replaced `Duration.ofHours(1)` with `Duration.ofSeconds(connectionContext.getOAuthWebServerTimeout())` | | `DatabricksConnectionContextTest.java` | Tests for default (120) and custom (300) values | | `ClientConfiguratorTest.java` | Updated 9 browser auth tests to mock the new method | ## Test plan - [x] `DatabricksConnectionContextTest#testOAuthWebServerTimeoutDefault` — verifies default 120s - [x] `DatabricksConnectionContextTest#testOAuthWebServerTimeoutCustom` — verifies custom value via URL param - [x] All 25 `ClientConfiguratorTest` tests pass with the configurable timeout wired through 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 40efd22 commit dd52cc5

7 files changed

Lines changed: 38 additions & 2 deletions

File tree

NEXT_CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## [Unreleased]
44

55
### Added
6+
- Added connection property `OAuthWebServerTimeout` to configure the OAuth browser authentication timeout for U2M (user-to-machine) flows, and also updated hardcoded 1-hour timeout to default 120 seconds timeout.
67

78
### Updated
89

src/main/java/com/databricks/jdbc/api/impl/DatabricksConnectionContext.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,12 @@ public List<Integer> getOAuth2RedirectUrlPorts() {
810810
}
811811
}
812812

813+
/** {@inheritDoc} */
814+
@Override
815+
public int getOAuthWebServerTimeout() {
816+
return Integer.parseInt(getParameter(DatabricksJdbcUrlParams.OAUTH_WEB_SERVER_TIMEOUT));
817+
}
818+
813819
@Override
814820
public Boolean getUseEmptyMetadata() {
815821
String param = getParameter(DatabricksJdbcUrlParams.USE_EMPTY_METADATA);

src/main/java/com/databricks/jdbc/api/internal/IDatabricksConnectionContext.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,9 @@ public interface IDatabricksConnectionContext {
234234
/** Returns the list of OAuth2 redirect URL ports used for OAuth authentication. */
235235
List<Integer> getOAuth2RedirectUrlPorts();
236236

237+
/** Returns the OAuth browser authentication timeout in seconds for U2M authentication. */
238+
int getOAuthWebServerTimeout();
239+
237240
String getGcpAuthType() throws DatabricksParsingException;
238241

239242
String getGoogleServiceAccount();

src/main/java/com/databricks/jdbc/common/DatabricksJdbcUrlParams.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ public enum DatabricksJdbcUrlParams {
3535
OAUTH_REFRESH_TOKEN("Auth_RefreshToken", "OAuth2 Refresh Token"),
3636
OAUTH_REFRESH_TOKEN_2("OAuthRefreshToken", "OAuth2 Refresh Token"), // Same as OAUTH_REFRESH_TOKEN
3737
OAUTH_REDIRECT_URL_PORT("OAuth2RedirectUrlPort", "OAuth2 Redirect URL port", "8020"),
38+
OAUTH_WEB_SERVER_TIMEOUT(
39+
"OAuthWebServerTimeout", "OAuth browser authentication timeout in seconds", "120"),
3840
PWD("pwd", "Password (used when AUTH_MECH = 3)", true),
3941
POLL_INTERVAL("asyncexecpollinterval", "Async execution poll interval", "200"),
4042
HTTP_PATH("httppath", "HTTP path", true),

src/main/java/com/databricks/jdbc/dbclient/impl/common/ClientConfigurator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ public void setupU2MConfig() throws DatabricksParsingException {
206206
.setHost(host)
207207
.setClientId(clientId)
208208
.setOAuthBrowserAuthTimeout(
209-
Duration.ofHours(1)) // TODO : add a browser timeout connection config
209+
Duration.ofSeconds(connectionContext.getOAuthWebServerTimeout()))
210210
.setClientSecret(connectionContext.getClientSecret())
211211
.setOAuthRedirectUrl(redirectUrl);
212212

src/test/java/com/databricks/jdbc/api/impl/DatabricksConnectionContextTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,4 +1342,19 @@ public void testGetThriftMaxBatchesInMemoryInvalidFallback() throws DatabricksSQ
13421342
DatabricksConnectionContext.parse(TestConstants.VALID_URL_1, props);
13431343
assertEquals(3, ctx.getThriftMaxBatchesInMemory()); // Should fall back to default
13441344
}
1345+
1346+
@Test
1347+
public void testOAuthWebServerTimeoutDefault() throws DatabricksSQLException {
1348+
IDatabricksConnectionContext connectionContext =
1349+
DatabricksConnectionContext.parse(TestConstants.VALID_URL_1, properties);
1350+
assertEquals(120, connectionContext.getOAuthWebServerTimeout());
1351+
}
1352+
1353+
@Test
1354+
public void testOAuthWebServerTimeoutCustom() throws DatabricksSQLException {
1355+
IDatabricksConnectionContext connectionContext =
1356+
DatabricksConnectionContext.parse(
1357+
TestConstants.VALID_URL_1 + ";OAuthWebServerTimeout=300", properties);
1358+
assertEquals(300, connectionContext.getOAuthWebServerTimeout());
1359+
}
13451360
}

src/test/java/com/databricks/jdbc/dbclient/impl/common/ClientConfiguratorTest.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ void getWorkspaceClient_OAuthWithBrowserBasedAuthentication_AuthenticatesCorrect
191191
when(mockContext.getOAuth2RedirectUrlPorts()).thenReturn(List.of(8020));
192192
when(mockContext.getHttpMaxConnectionsPerRoute()).thenReturn(100);
193193
when(mockContext.getDisableOauthRefreshToken()).thenReturn(true);
194+
when(mockContext.getOAuthWebServerTimeout()).thenReturn(120);
194195
configurator = new ClientConfigurator(mockContext);
195196
WorkspaceClient client = configurator.getWorkspaceClient();
196197
assertNotNull(client);
@@ -217,6 +218,7 @@ void getWorkspaceClient_OAuthWithBrowserBasedAuthentication_ScopesExcludeOffline
217218
when(mockContext.getOAuth2RedirectUrlPorts()).thenReturn(List.of(8030));
218219
when(mockContext.getHttpMaxConnectionsPerRoute()).thenReturn(100);
219220
when(mockContext.getDisableOauthRefreshToken()).thenReturn(true);
221+
when(mockContext.getOAuthWebServerTimeout()).thenReturn(120);
220222

221223
configurator = new ClientConfigurator(mockContext);
222224
DatabricksConfig config = configurator.getDatabricksConfig();
@@ -246,6 +248,7 @@ void getWorkspaceClient_OAuthWithBrowserBasedAuthentication_ScopesExcludeOffline
246248
when(mockContext.getOAuth2RedirectUrlPorts()).thenReturn(List.of(8020));
247249
when(mockContext.getHttpMaxConnectionsPerRoute()).thenReturn(100);
248250
when(mockContext.getDisableOauthRefreshToken()).thenReturn(true);
251+
when(mockContext.getOAuthWebServerTimeout()).thenReturn(120);
249252
configurator = new ClientConfigurator(mockContext);
250253
WorkspaceClient client = configurator.getWorkspaceClient();
251254
assertNotNull(client);
@@ -491,6 +494,7 @@ void getWorkspaceClient_OAuthWithBrowserBasedAuthentication_SetsCustomRedirectUr
491494
when(mockContext.getHttpConnectionPoolSize()).thenReturn(100);
492495
when(mockContext.getHttpMaxConnectionsPerRoute()).thenReturn(100);
493496
when(mockContext.getDisableOauthRefreshToken()).thenReturn(true);
497+
when(mockContext.getOAuthWebServerTimeout()).thenReturn(120);
494498

495499
configurator = new ClientConfigurator(mockContext);
496500
WorkspaceClient client = configurator.getWorkspaceClient();
@@ -501,7 +505,7 @@ void getWorkspaceClient_OAuthWithBrowserBasedAuthentication_SetsCustomRedirectUr
501505
assertEquals("browser-client-id", config.getClientId());
502506
assertEquals("browser-client-secret", config.getClientSecret());
503507
assertEquals(List.of("scope1", "scope2"), config.getScopes());
504-
assertEquals(Duration.ofHours(1), config.getOAuthBrowserAuthTimeout());
508+
assertEquals(Duration.ofSeconds(120), config.getOAuthBrowserAuthTimeout());
505509
assertEquals("http://localhost:" + testPort, config.getOAuthRedirectUrl());
506510
assertEquals(DatabricksJdbcConstants.U2M_AUTH_TYPE, config.getAuthType());
507511
}
@@ -522,6 +526,7 @@ void testSetupU2MConfig_WithTokenCache()
522526
when(mockContext.getHttpMaxConnectionsPerRoute()).thenReturn(100);
523527
when(mockContext.getDisableOauthRefreshToken()).thenReturn(true);
524528
when(mockContext.isTokenFederationEnabled()).thenReturn(true);
529+
when(mockContext.getOAuthWebServerTimeout()).thenReturn(120);
525530

526531
configurator = new ClientConfigurator(mockContext);
527532
WorkspaceClient client = configurator.getWorkspaceClient();
@@ -556,6 +561,7 @@ void testSetupU2MConfig_WithTokenCacheNoPassphrase()
556561
when(mockContext.getTokenCachePassPhrase()).thenReturn(null);
557562
when(mockContext.getHttpMaxConnectionsPerRoute()).thenReturn(100);
558563
when(mockContext.getDisableOauthRefreshToken()).thenReturn(true);
564+
when(mockContext.getOAuthWebServerTimeout()).thenReturn(120);
559565

560566
assertThrows(DatabricksException.class, () -> new ClientConfigurator(mockContext));
561567
}
@@ -575,6 +581,7 @@ void testSetupU2MConfig_WithoutTokenCache()
575581
when(mockContext.getHttpMaxConnectionsPerRoute()).thenReturn(100);
576582
when(mockContext.getDisableOauthRefreshToken()).thenReturn(true);
577583
when(mockContext.isTokenFederationEnabled()).thenReturn(true);
584+
when(mockContext.getOAuthWebServerTimeout()).thenReturn(120);
578585

579586
configurator = new ClientConfigurator(mockContext);
580587
WorkspaceClient client = configurator.getWorkspaceClient();
@@ -686,6 +693,7 @@ void testAuthScope_U2MWithCustomScope()
686693
when(mockContext.getOAuth2RedirectUrlPorts()).thenReturn(List.of(8020));
687694
when(mockContext.getHttpMaxConnectionsPerRoute()).thenReturn(100);
688695
when(mockContext.getDisableOauthRefreshToken()).thenReturn(true);
696+
when(mockContext.getOAuthWebServerTimeout()).thenReturn(120);
689697

690698
configurator = new ClientConfigurator(mockContext);
691699
DatabricksConfig config = configurator.getDatabricksConfig();
@@ -710,6 +718,7 @@ void testAuthScope_U2MWithDefaultScopes()
710718
when(mockContext.getOAuth2RedirectUrlPorts()).thenReturn(List.of(8020));
711719
when(mockContext.getHttpMaxConnectionsPerRoute()).thenReturn(100);
712720
when(mockContext.getDisableOauthRefreshToken()).thenReturn(false);
721+
when(mockContext.getOAuthWebServerTimeout()).thenReturn(120);
713722

714723
configurator = new ClientConfigurator(mockContext);
715724
DatabricksConfig config = configurator.getDatabricksConfig();

0 commit comments

Comments
 (0)