Skip to content

Commit 5d10d37

Browse files
authored
[PECOBLR-1192] Upgrade SDK + provide an option to disable token refresh (#1076)
## Description - Currently if user provides their own custom scope, sdk adds an `offline_access` scope. This caused OAuth in custom IDPs to break. Very recently sdk [added an option to change this](https://github.com/databricks/databricks-sdk-java/releases/tag/v0.67.1). This PR incorporates the option into OSS JDBC too. ## Testing - Added extensive unit tests ## Additional Notes to the Reviewer <!-- Share any additional context or insights that may help the reviewer understand the changes better. This could include challenges faced, limitations, or compromises made during the development process. Also, mention any areas of the code that you would like the reviewer to focus on specifically. -->
1 parent a332cd6 commit 5d10d37

10 files changed

Lines changed: 82 additions & 3 deletions

File tree

NEXT_CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
- Added support for geospatial data types.
77
- Added support for telemetry log levels, which can be controlled via the connection parameter `TelemetryLogLevel`. This allows users to configure the verbosity of telemetry logging from OFF to TRACE.
88
- Added full support for JDBC transaction control methods in Databricks. Transaction support in Databricks is currently available as a Private Preview. The `IgnoreTransactions` connection parameter can be set to `1` to disable or no-op transaction control methods.
9+
- Added a new config attribute `DisableOauthRefreshToken` to control whether refresh tokens are requested in OAuth exchanges. By default, the driver does not include the `offline_access` scope. If `offline_access` is explicitly provided by the user, it is preserved and not removed.
910

1011
### Updated
11-
- Updated sdk version from 0.65.0 to 0.67.3
12+
- Updated sdk version from 0.65.0 to 0.69.0
1213

1314
### Fixed
1415
- Fixed SQL syntax error when LIKE queries contain empty ESCAPE clauses.

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
<httpclient.version>4.5.14</httpclient.version>
5757
<commons-configuration.version>2.10.1</commons-configuration.version>
5858
<commons-io.version>2.14.0</commons-io.version>
59-
<databricks-sdk.version>0.67.3</databricks-sdk.version>
59+
<databricks-sdk.version>0.69.0</databricks-sdk.version>
6060
<maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version>
6161
<sql-logic-test.version>0.3</sql-logic-test.version>
6262
<lz4-compression.version>1.8.0</lz4-compression.version>

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,4 +1087,9 @@ public TelemetryLogLevel getTelemetryLogLevel() {
10871087
public boolean isSeaSyncMetadataEnabled() {
10881088
return getParameter(DatabricksJdbcUrlParams.ENABLE_SEA_SYNC_METADATA).equals("1");
10891089
}
1090+
1091+
@Override
1092+
public boolean getDisableOauthRefreshToken() {
1093+
return getParameter(DatabricksJdbcUrlParams.DISABLE_OAUTH_REFRESH_TOKEN, "1").equals("1");
1094+
}
10901095
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,4 +401,7 @@ public interface IDatabricksConnectionContext {
401401
* synchronous metadata requests in SEA mode
402402
*/
403403
boolean isSeaSyncMetadataEnabled();
404+
405+
/** Returns whether OAuth refresh tokens should be disabled (omit offline_access by default). */
406+
boolean getDisableOauthRefreshToken();
404407
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@ public enum DatabricksJdbcUrlParams {
175175
ENABLE_SEA_SYNC_METADATA(
176176
"EnableSeaSyncMetadata",
177177
"Enable x-databricks-sea-can-run-fully-sync header for synchronous metadata requests in SEA mode",
178+
"1"),
179+
DISABLE_OAUTH_REFRESH_TOKEN(
180+
"DisableOauthRefreshToken",
181+
"Disable requesting OAuth refresh tokens (omit offline_access unless explicitly provided)",
178182
"1");
179183

180184
private final String paramName;

src/main/java/com/databricks/jdbc/common/util/DatabricksDriverPropertyUtil.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public class DatabricksDriverPropertyUtil {
3737
DatabricksJdbcUrlParams.SOCKET_TIMEOUT,
3838
DatabricksJdbcUrlParams.ENABLE_TOKEN_CACHE,
3939
DatabricksJdbcUrlParams.ENABLE_SQL_VALIDATION_FOR_IS_VALID,
40+
DatabricksJdbcUrlParams.DISABLE_OAUTH_REFRESH_TOKEN,
4041
DatabricksJdbcUrlParams.ENABLE_MULTIPLE_CATALOG_SUPPORT,
4142
DatabricksJdbcUrlParams.IGNORE_TRANSACTIONS);
4243

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public ClientConfigurator(IDatabricksConnectionContext connectionContext)
4848
throws DatabricksSSLException {
4949
this.connectionContext = connectionContext;
5050
this.databricksConfig = new DatabricksConfig();
51+
databricksConfig.setDisableOauthRefreshToken(connectionContext.getDisableOauthRefreshToken());
5152
CommonsHttpClient.Builder httpClientBuilder = new CommonsHttpClient.Builder();
5253
httpClientBuilder.withTimeoutSeconds(connectionContext.getSocketTimeout());
5354
setupProxyConfig(httpClientBuilder);

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -815,4 +815,40 @@ public void testEnableMultipleCatalogSupport() throws DatabricksSQLException {
815815
DatabricksConnectionContext.parse(TestConstants.VALID_URL_1, propsWithInvalidParam);
816816
assertFalse(connectionContext.getEnableMultipleCatalogSupport());
817817
}
818+
819+
@Test
820+
public void testDisableOauthRefreshTokenParam() throws DatabricksSQLException {
821+
// Default should be true (offline_access not requested by default)
822+
DatabricksConnectionContext ctx =
823+
(DatabricksConnectionContext)
824+
DatabricksConnectionContext.parse(TestConstants.VALID_URL_1, properties);
825+
assertTrue(ctx.getDisableOauthRefreshToken());
826+
827+
// Explicitly disable = 0 via URL
828+
String urlWithDisableOff =
829+
"jdbc:databricks://sample-host.cloud.databricks.com:9999/default;AuthMech=3;"
830+
+ "httpPath=/sql/1.0/warehouses/9999999999999999;DisableOauthRefreshToken=0";
831+
ctx =
832+
(DatabricksConnectionContext)
833+
DatabricksConnectionContext.parse(urlWithDisableOff, properties);
834+
assertFalse(ctx.getDisableOauthRefreshToken());
835+
836+
// Explicitly enable = 1 via URL
837+
String urlWithDisableOn =
838+
"jdbc:databricks://sample-host.cloud.databricks.com:9999/default;AuthMech=3;"
839+
+ "httpPath=/sql/1.0/warehouses/9999999999999999;DisableOauthRefreshToken=1";
840+
ctx =
841+
(DatabricksConnectionContext)
842+
DatabricksConnectionContext.parse(urlWithDisableOn, properties);
843+
assertTrue(ctx.getDisableOauthRefreshToken());
844+
845+
// Via Properties
846+
Properties props = new Properties();
847+
props.setProperty("password", "passwd");
848+
props.setProperty("DisableOauthRefreshToken", "0");
849+
ctx =
850+
(DatabricksConnectionContext)
851+
DatabricksConnectionContext.parse(TestConstants.VALID_URL_1, props);
852+
assertFalse(ctx.getDisableOauthRefreshToken());
853+
}
818854
}

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ void getWorkspaceClient_OAuthWithBrowserBasedAuthentication_AuthenticatesCorrect
188188
when(mockContext.getHttpConnectionPoolSize()).thenReturn(100);
189189
when(mockContext.getOAuth2RedirectUrlPorts()).thenReturn(List.of(8020));
190190
when(mockContext.getHttpMaxConnectionsPerRoute()).thenReturn(100);
191+
when(mockContext.getDisableOauthRefreshToken()).thenReturn(true);
191192
configurator = new ClientConfigurator(mockContext);
192193
WorkspaceClient client = configurator.getWorkspaceClient();
193194
assertNotNull(client);
@@ -201,6 +202,28 @@ void getWorkspaceClient_OAuthWithBrowserBasedAuthentication_AuthenticatesCorrect
201202
assertEquals(DatabricksJdbcConstants.U2M_AUTH_TYPE, config.getAuthType());
202203
}
203204

205+
@Test
206+
void getWorkspaceClient_OAuthWithBrowserBasedAuthentication_ScopesExcludeOfflineAccess()
207+
throws DatabricksParsingException, DatabricksSSLException {
208+
when(mockContext.getAuthMech()).thenReturn(AuthMech.OAUTH);
209+
when(mockContext.getAuthFlow()).thenReturn(AuthFlow.BROWSER_BASED_AUTHENTICATION);
210+
when(mockContext.getHostForOAuth()).thenReturn("https://oauth-browser.databricks.com");
211+
when(mockContext.getClientId()).thenReturn("browser-client-id");
212+
when(mockContext.getClientSecret()).thenReturn("browser-client-secret");
213+
when(mockContext.getOAuthScopesForU2M()).thenReturn(List.of("scope.read", "scope.write"));
214+
when(mockContext.getHttpConnectionPoolSize()).thenReturn(100);
215+
when(mockContext.getOAuth2RedirectUrlPorts()).thenReturn(List.of(8030));
216+
when(mockContext.getHttpMaxConnectionsPerRoute()).thenReturn(100);
217+
when(mockContext.getDisableOauthRefreshToken()).thenReturn(true);
218+
219+
configurator = new ClientConfigurator(mockContext);
220+
DatabricksConfig config = configurator.getDatabricksConfig();
221+
222+
// Driver disables refresh tokens for OAuth, so SDK must not include offline_access
223+
assertTrue(config.getDisableOauthRefreshToken());
224+
assertFalse(config.getScopes().contains("offline_access"));
225+
}
226+
204227
@Test
205228
void
206229
getWorkspaceClient_OAuthWithBrowserBasedAuthentication_WithDiscoveryURL_AuthenticatesCorrectly()
@@ -217,6 +240,7 @@ void getWorkspaceClient_OAuthWithBrowserBasedAuthentication_AuthenticatesCorrect
217240
when(mockContext.getHttpConnectionPoolSize()).thenReturn(100);
218241
when(mockContext.getOAuth2RedirectUrlPorts()).thenReturn(List.of(8020));
219242
when(mockContext.getHttpMaxConnectionsPerRoute()).thenReturn(100);
243+
when(mockContext.getDisableOauthRefreshToken()).thenReturn(true);
220244
configurator = new ClientConfigurator(mockContext);
221245
WorkspaceClient client = configurator.getWorkspaceClient();
222246
assertNotNull(client);
@@ -461,6 +485,7 @@ void getWorkspaceClient_OAuthWithBrowserBasedAuthentication_SetsCustomRedirectUr
461485
when(mockContext.getOAuth2RedirectUrlPorts()).thenReturn(List.of(testPort));
462486
when(mockContext.getHttpConnectionPoolSize()).thenReturn(100);
463487
when(mockContext.getHttpMaxConnectionsPerRoute()).thenReturn(100);
488+
when(mockContext.getDisableOauthRefreshToken()).thenReturn(true);
464489

465490
configurator = new ClientConfigurator(mockContext);
466491
WorkspaceClient client = configurator.getWorkspaceClient();
@@ -490,6 +515,7 @@ void testSetupU2MConfig_WithTokenCache()
490515
when(mockContext.isTokenCacheEnabled()).thenReturn(true);
491516
when(mockContext.getTokenCachePassPhrase()).thenReturn("testPassphrase");
492517
when(mockContext.getHttpMaxConnectionsPerRoute()).thenReturn(100);
518+
when(mockContext.getDisableOauthRefreshToken()).thenReturn(true);
493519

494520
configurator = new ClientConfigurator(mockContext);
495521
WorkspaceClient client = configurator.getWorkspaceClient();
@@ -522,6 +548,7 @@ void testSetupU2MConfig_WithTokenCacheNoPassphrase() throws DatabricksParsingExc
522548
when(mockContext.isTokenCacheEnabled()).thenReturn(true);
523549
when(mockContext.getTokenCachePassPhrase()).thenReturn(null);
524550
when(mockContext.getHttpMaxConnectionsPerRoute()).thenReturn(100);
551+
when(mockContext.getDisableOauthRefreshToken()).thenReturn(true);
525552

526553
assertThrows(DatabricksException.class, () -> new ClientConfigurator(mockContext));
527554
}
@@ -539,6 +566,7 @@ void testSetupU2MConfig_WithoutTokenCache()
539566
when(mockContext.getOAuth2RedirectUrlPorts()).thenReturn(List.of(8020));
540567
when(mockContext.isTokenCacheEnabled()).thenReturn(false);
541568
when(mockContext.getHttpMaxConnectionsPerRoute()).thenReturn(100);
569+
when(mockContext.getDisableOauthRefreshToken()).thenReturn(true);
542570

543571
configurator = new ClientConfigurator(mockContext);
544572
WorkspaceClient client = configurator.getWorkspaceClient();

thin_public_pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
<dependency>
4343
<groupId>com.databricks</groupId>
4444
<artifactId>databricks-sdk-java</artifactId>
45-
<version>0.67.3</version>
45+
<version>0.69.0</version>
4646
</dependency>
4747

4848
<!-- Apache Commons -->

0 commit comments

Comments
 (0)