From e7b7eb29e20255f56d04614a334ad4c6f9dbeb00 Mon Sep 17 00:00:00 2001 From: Hector Castejon Diaz Date: Tue, 10 Feb 2026 11:39:14 +0000 Subject: [PATCH 1/7] Refactor: Extract OIDC endpoint fetching to reusable method Extract the logic for fetching OIDC endpoints from the well-known endpoint into a helper method fetchOidcEndpointsFromWellKnown(). This method is now reused for both the Azure client secret authentication case and the default case, reducing code duplication. Co-Authored-By: Claude Sonnet 4.5 --- .../databricks/sdk/core/DatabricksConfig.java | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/DatabricksConfig.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/DatabricksConfig.java index 35127219c..95c189897 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/DatabricksConfig.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/DatabricksConfig.java @@ -787,6 +787,21 @@ private OpenIDConnectEndpoints getUnifiedOidcEndpoints(String accountId) throws return new OpenIDConnectEndpoints(prefix + "/v1/token", prefix + "/v1/authorize"); } + private OpenIDConnectEndpoints fetchOidcEndpointsFromWellKnown() throws IOException { + ApiClient apiClient = + new ApiClient.Builder() + .withHttpClient(getHttpClient()) + .withGetHostFunc(v -> getHost()) + .build(); + try { + return apiClient.execute( + new Request("GET", "/oidc/.well-known/oauth-authorization-server"), + OpenIDConnectEndpoints.class); + } catch (IOException e) { + throw new DatabricksException("IO error: " + e.getMessage(), e); + } + } + private OpenIDConnectEndpoints fetchDefaultOidcEndpoints() throws IOException { if (getHost() == null) { return null; @@ -796,23 +811,16 @@ private OpenIDConnectEndpoints fetchDefaultOidcEndpoints() throws IOException { if (getHostType() == HostType.UNIFIED) { return getUnifiedOidcEndpoints(getAccountId()); } + + if (isAzure() && getAzureClientId() != null) { + return fetchOidcEndpointsFromWellKnown(); + } if (isAccountClient() && getAccountId() != null) { String prefix = getHost() + "/oidc/accounts/" + getAccountId(); return new OpenIDConnectEndpoints(prefix + "/v1/token", prefix + "/v1/authorize"); } - ApiClient apiClient = - new ApiClient.Builder() - .withHttpClient(getHttpClient()) - .withGetHostFunc(v -> getHost()) - .build(); - try { - return apiClient.execute( - new Request("GET", "/oidc/.well-known/oauth-authorization-server"), - OpenIDConnectEndpoints.class); - } catch (IOException e) { - throw new DatabricksException("IO error: " + e.getMessage(), e); - } + return fetchOidcEndpointsFromWellKnown(); } @Override From bec5ae871cc22156f40a12f995a0c71c47d482fe Mon Sep 17 00:00:00 2001 From: Hector Castejon Diaz Date: Tue, 10 Feb 2026 12:31:57 +0000 Subject: [PATCH 2/7] more fixes --- .../databricks/sdk/core/DatabricksConfig.java | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/DatabricksConfig.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/DatabricksConfig.java index 95c189897..35127219c 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/DatabricksConfig.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/DatabricksConfig.java @@ -787,21 +787,6 @@ private OpenIDConnectEndpoints getUnifiedOidcEndpoints(String accountId) throws return new OpenIDConnectEndpoints(prefix + "/v1/token", prefix + "/v1/authorize"); } - private OpenIDConnectEndpoints fetchOidcEndpointsFromWellKnown() throws IOException { - ApiClient apiClient = - new ApiClient.Builder() - .withHttpClient(getHttpClient()) - .withGetHostFunc(v -> getHost()) - .build(); - try { - return apiClient.execute( - new Request("GET", "/oidc/.well-known/oauth-authorization-server"), - OpenIDConnectEndpoints.class); - } catch (IOException e) { - throw new DatabricksException("IO error: " + e.getMessage(), e); - } - } - private OpenIDConnectEndpoints fetchDefaultOidcEndpoints() throws IOException { if (getHost() == null) { return null; @@ -811,16 +796,23 @@ private OpenIDConnectEndpoints fetchDefaultOidcEndpoints() throws IOException { if (getHostType() == HostType.UNIFIED) { return getUnifiedOidcEndpoints(getAccountId()); } - - if (isAzure() && getAzureClientId() != null) { - return fetchOidcEndpointsFromWellKnown(); - } if (isAccountClient() && getAccountId() != null) { String prefix = getHost() + "/oidc/accounts/" + getAccountId(); return new OpenIDConnectEndpoints(prefix + "/v1/token", prefix + "/v1/authorize"); } - return fetchOidcEndpointsFromWellKnown(); + ApiClient apiClient = + new ApiClient.Builder() + .withHttpClient(getHttpClient()) + .withGetHostFunc(v -> getHost()) + .build(); + try { + return apiClient.execute( + new Request("GET", "/oidc/.well-known/oauth-authorization-server"), + OpenIDConnectEndpoints.class); + } catch (IOException e) { + throw new DatabricksException("IO error: " + e.getMessage(), e); + } } @Override From 192d315f23ca47a20906745aab92169688d550ac Mon Sep 17 00:00:00 2001 From: Hector Castejon Diaz Date: Tue, 10 Feb 2026 14:07:44 +0000 Subject: [PATCH 3/7] Fix test: Add missing HTTP client and OIDC endpoints mock The cacheWithInvalidTokensTest was failing because: 1. The config didn't have an HTTP client set 2. The config needed mocked OIDC endpoints This fix adds: - HTTP client to the test config - Spy on config to inject mocked OIDC endpoints - Import for OpenIDConnectEndpoints class Co-Authored-By: Claude Sonnet 4.5 --- ...xternalBrowserCredentialsProviderTest.java | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/databricks-sdk-java/src/test/java/com/databricks/sdk/core/oauth/ExternalBrowserCredentialsProviderTest.java b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/oauth/ExternalBrowserCredentialsProviderTest.java index dbd1cba9f..5a3b1044d 100644 --- a/databricks-sdk-java/src/test/java/com/databricks/sdk/core/oauth/ExternalBrowserCredentialsProviderTest.java +++ b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/oauth/ExternalBrowserCredentialsProviderTest.java @@ -7,6 +7,7 @@ import com.databricks.sdk.core.DatabricksException; import com.databricks.sdk.core.FixtureServer; import com.databricks.sdk.core.HeaderFactory; +import com.databricks.sdk.core.OpenIDConnectEndpoints; import com.databricks.sdk.core.commons.CommonsHttpClient; import com.databricks.sdk.core.http.HttpClient; import com.databricks.sdk.core.http.Request; @@ -303,7 +304,8 @@ void cacheWithValidRefreshableTokenTest() throws IOException { any(DatabricksConfig.class), any(String.class), any(String.class), - any(TokenCache.class)); + any(TokenCache.class), + any(OpenIDConnectEndpoints.class)); // Verify token was NOT saved back to cache (we're using the cached one as-is). Mockito.verify(mockTokenCache, Mockito.never()).save(any(Token.class)); @@ -363,7 +365,7 @@ void cacheWithValidNonRefreshableTokenTest() throws IOException { // Verify performBrowserAuth was NOT called. Mockito.verify(provider, Mockito.never()) - .performBrowserAuth(any(DatabricksConfig.class), any(), any(), any(TokenCache.class)); + .performBrowserAuth(any(DatabricksConfig.class), any(), any(), any(TokenCache.class), any(OpenIDConnectEndpoints.class)); // Verify no token was saved (we're using the cached one as-is). Mockito.verify(mockTokenCache, Mockito.never()).save(any(Token.class)); @@ -430,7 +432,9 @@ void cacheWithInvalidAccessTokenValidRefreshTest() throws IOException { any(DatabricksConfig.class), any(String.class), any(String.class), - any(TokenCache.class)); + any(TokenCache.class), + any(OpenIDConnectEndpoints.class) + ); // Verify token was saved back to cache Mockito.verify(mockTokenCache, Mockito.times(1)).save(any(Token.class)); @@ -508,7 +512,7 @@ void cacheWithInvalidAccessTokenRefreshFailingTest() throws IOException { Mockito.spy(new ExternalBrowserCredentialsProvider(mockTokenCache)); Mockito.doReturn(cachedTokenSource) .when(provider) - .performBrowserAuth(any(DatabricksConfig.class), any(), any(), any(TokenCache.class)); + .performBrowserAuth(any(DatabricksConfig.class), any(), any(), any(TokenCache.class), any(OpenIDConnectEndpoints.class)); // Spy on the config to inject the endpoints DatabricksConfig spyConfig = Mockito.spy(config); @@ -527,7 +531,7 @@ void cacheWithInvalidAccessTokenRefreshFailingTest() throws IOException { // Verify performBrowserAuth was called since refresh failed Mockito.verify(provider, Mockito.times(1)) - .performBrowserAuth(any(DatabricksConfig.class), any(), any(), any(TokenCache.class)); + .performBrowserAuth(any(DatabricksConfig.class), any(), any(), any(TokenCache.class), any(OpenIDConnectEndpoints.class)); // Verify token was saved after browser auth (for the new token) Mockito.verify(mockTokenCache, Mockito.times(1)).save(any(Token.class)); @@ -572,17 +576,26 @@ void cacheWithInvalidTokensTest() throws IOException { new DatabricksConfig() .setAuthType("external-browser") .setHost("https://test.databricks.com") - .setClientId("test-client-id"); + .setClientId("test-client-id") + .setHttpClient(mockHttpClient); // Create our provider and mock the browser auth method ExternalBrowserCredentialsProvider provider = Mockito.spy(new ExternalBrowserCredentialsProvider(mockTokenCache)); Mockito.doReturn(cachedTokenSource) .when(provider) - .performBrowserAuth(any(DatabricksConfig.class), any(), any(), any(TokenCache.class)); + .performBrowserAuth(any(DatabricksConfig.class), any(), any(), any(TokenCache.class), any(OpenIDConnectEndpoints.class)); + + // Spy on the config to inject the endpoints + OpenIDConnectEndpoints endpoints = + new OpenIDConnectEndpoints( + "https://test.databricks.com/oidc/v1/token", + "https://test.databricks.com/oidc/v1/authorize"); + DatabricksConfig spyConfig = Mockito.spy(config); + Mockito.doReturn(endpoints).when(spyConfig).getOidcEndpoints(); // Configure provider - HeaderFactory headerFactory = provider.configure(config); + HeaderFactory headerFactory = provider.configure(spyConfig); assertNotNull(headerFactory); // Verify headers contain the browser auth token (fallback) Map headers = headerFactory.headers(); @@ -593,7 +606,7 @@ void cacheWithInvalidTokensTest() throws IOException { // Verify performBrowserAuth was called since we had an invalid token Mockito.verify(provider, Mockito.times(1)) - .performBrowserAuth(any(DatabricksConfig.class), any(), any(), any(TokenCache.class)); + .performBrowserAuth(any(DatabricksConfig.class), any(), any(), any(TokenCache.class), any(OpenIDConnectEndpoints.class)); // Verify token was saved after browser auth (for the new token) Mockito.verify(mockTokenCache, Mockito.times(1)).save(any(Token.class)); @@ -609,7 +622,7 @@ void doNotAddOfflineAccessScopeWhenDisableOauthRefreshTokenIsTrue() { .setScopes(Arrays.asList("my-test-scope")); ExternalBrowserCredentialsProvider provider = new ExternalBrowserCredentialsProvider(); - List scopes = provider.getScopes(config); + List scopes = provider.getScopes(config, null); assertEquals(1, scopes.size()); assertTrue(scopes.contains("my-test-scope")); @@ -625,7 +638,7 @@ void doNotRemoveUserProvidedScopesWhenDisableOauthRefreshTokenIsTrue() { .setScopes(Arrays.asList("my-test-scope", "offline_access")); ExternalBrowserCredentialsProvider provider = new ExternalBrowserCredentialsProvider(); - List scopes = provider.getScopes(config); + List scopes = provider.getScopes(config, null); assertEquals(2, scopes.size()); assertTrue(scopes.contains("offline_access")); @@ -641,7 +654,7 @@ void addOfflineAccessScopeWhenDisableOauthRefreshTokenIsFalse() { .setScopes(Arrays.asList("my-test-scope")); ExternalBrowserCredentialsProvider provider = new ExternalBrowserCredentialsProvider(); - List scopes = provider.getScopes(config); + List scopes = provider.getScopes(config, null); assertEquals(2, scopes.size()); assertTrue(scopes.contains("offline_access")); From d37c1c50ab4c1e242d803ca6390666dcf692ff78 Mon Sep 17 00:00:00 2001 From: Hector Castejon Diaz Date: Tue, 10 Feb 2026 14:10:43 +0000 Subject: [PATCH 4/7] Refactor OIDC endpoint resolution in ExternalBrowserCredentialsProvider Move OIDC endpoint resolution logic to a utility method in OAuthClientUtils. This allows proper error handling and avoids null pointer exceptions when OIDC endpoints cannot be resolved. Changes: - Add resolveOidcEndpoints() utility method in OAuthClientUtils - Update ExternalBrowserCredentialsProvider to use the utility method - Pass OpenIDConnectEndpoints explicitly to helper methods - Add proper error handling for OIDC endpoint resolution failures Co-Authored-By: Claude Sonnet 4.5 --- .../ExternalBrowserCredentialsProvider.java | 21 +++++++++----- .../sdk/core/oauth/OAuthClientUtils.java | 29 +++++++++++++++++++ 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/ExternalBrowserCredentialsProvider.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/ExternalBrowserCredentialsProvider.java index 2050fe5af..df7294871 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/ExternalBrowserCredentialsProvider.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/ExternalBrowserCredentialsProvider.java @@ -58,6 +58,13 @@ public OAuthHeaderFactory configure(DatabricksConfig config) { // Use the utility class to resolve client ID and client secret String clientId = OAuthClientUtils.resolveClientId(config); String clientSecret = OAuthClientUtils.resolveClientSecret(config); + OpenIDConnectEndpoints oidcEndpoints = null; + try { + oidcEndpoints = OAuthClientUtils.resolveOidcEndpoints(config); + } catch (IOException e) { + LOGGER.error("Failed to resolve OIDC endpoints: {}", e.getMessage()); + return null; + } try { if (tokenCache == null) { @@ -78,7 +85,7 @@ public OAuthHeaderFactory configure(DatabricksConfig config) { new SessionCredentialsTokenSource( cachedToken, config.getHttpClient(), - config.getOidcEndpoints().getTokenEndpoint(), + oidcEndpoints.getTokenEndpoint(), clientId, clientSecret, Optional.of(config.getEffectiveOAuthRedirectUrl()), @@ -100,7 +107,7 @@ public OAuthHeaderFactory configure(DatabricksConfig config) { // If no cached token or refresh failed, perform browser auth CachedTokenSource cachedTokenSource = - performBrowserAuth(config, clientId, clientSecret, tokenCache); + performBrowserAuth(config, clientId, clientSecret, tokenCache, oidcEndpoints); tokenCache.save(cachedTokenSource.getToken()); return OAuthHeaderFactory.fromTokenSource(cachedTokenSource); } catch (IOException | DatabricksException e) { @@ -109,7 +116,7 @@ public OAuthHeaderFactory configure(DatabricksConfig config) { } } - protected List getScopes(DatabricksConfig config) { + protected List getScopes(DatabricksConfig config, OpenIDConnectEndpoints oidcEndpoints) { // Get user-provided scopes and add required default scopes. Set scopes = new HashSet<>(config.getScopes()); // Requesting a refresh token is most of the time the right thing to do from a @@ -125,7 +132,7 @@ protected List getScopes(DatabricksConfig config) { } CachedTokenSource performBrowserAuth( - DatabricksConfig config, String clientId, String clientSecret, TokenCache tokenCache) + DatabricksConfig config, String clientId, String clientSecret, TokenCache tokenCache, OpenIDConnectEndpoints oidcEndpoints) throws IOException { LOGGER.debug("Performing browser authentication"); @@ -138,8 +145,8 @@ CachedTokenSource performBrowserAuth( .withAccountId(config.getAccountId()) .withRedirectUrl(config.getEffectiveOAuthRedirectUrl()) .withBrowserTimeout(config.getOAuthBrowserAuthTimeout()) - .withScopes(getScopes(config)) - .withOpenIDConnectEndpoints(config.getOidcEndpoints()) + .withScopes(getScopes(config, oidcEndpoints)) + .withOpenIDConnectEndpoints(oidcEndpoints) .build(); Consent consent = client.initiateConsent(); @@ -151,7 +158,7 @@ CachedTokenSource performBrowserAuth( new SessionCredentialsTokenSource( token, config.getHttpClient(), - config.getOidcEndpoints().getTokenEndpoint(), + oidcEndpoints.getTokenEndpoint(), config.getClientId(), config.getClientSecret(), Optional.ofNullable(config.getEffectiveOAuthRedirectUrl()), diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/OAuthClientUtils.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/OAuthClientUtils.java index 5908eff79..2507c4534 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/OAuthClientUtils.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/OAuthClientUtils.java @@ -1,6 +1,9 @@ package com.databricks.sdk.core.oauth; import com.databricks.sdk.core.DatabricksConfig; +import com.databricks.sdk.core.http.Request; +import com.databricks.sdk.core.http.Response; +import java.io.IOException; /** Utility methods for OAuth client credentials resolution. */ public class OAuthClientUtils { @@ -39,4 +42,30 @@ public static String resolveClientSecret(DatabricksConfig config) { } return null; } + + /** + * Resolves the OAuth OIDC endpoints from the configuration. Prioritizes regular OIDC endpoints, then Azure OIDC endpoints. + * If the client ID and client secret are provided, the OIDC endpoints are fetched from the discovery URL. + * If the Azure client ID and client secret are provided, the OIDC endpoints are fetched from the Azure AD endpoint. + * If no client ID and client secret are provided, the OIDC endpoints are fetched from the default OIDC endpoints. + * @param config The Databricks configuration + * @return The resolved OIDC endpoints + * @throws IOException if the OIDC endpoints cannot be resolved + */ + public static OpenIDConnectEndpoints resolveOidcEndpoints(DatabricksConfig config) throws IOException { + if (config.getClientId() != null && config.getClientSecret() != null) { + return config.getOidcEndpoints(); + } else if (config.getAzureClientId() != null && config.getAzureClientSecret() != null) { + Request request = new Request("GET", config.getHost() + "/oidc/oauth2/v2.0/authorize"); + request.setRedirectionBehavior(false); + Response resp = config.getHttpClient().execute(request); + String realAuthUrl = resp.getFirstHeader("location"); + if (realAuthUrl == null) { + return null; + } + return new OpenIDConnectEndpoints( + realAuthUrl.replaceAll("/authorize", "/token"), realAuthUrl); + } + return config.getOidcEndpoints(); + } } From f485df5d0a186ed4d8805a26a6b0b336a940d11f Mon Sep 17 00:00:00 2001 From: Hector Castejon Diaz Date: Tue, 10 Feb 2026 14:18:26 +0000 Subject: [PATCH 5/7] Add unit test for external browser auth with Azure client ID Add test to verify that ExternalBrowserCredentialsProvider correctly resolves and passes Azure OIDC endpoints to the OAuthClient when using Azure client ID authentication. The test: - Mocks external browser authentication flow with Azure credentials - Captures the OpenIDConnectEndpoints passed to performBrowserAuth - Verifies the endpoints match the expected Azure OIDC URLs Co-Authored-By: Claude Sonnet 4.5 --- ...xternalBrowserCredentialsProviderTest.java | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/databricks-sdk-java/src/test/java/com/databricks/sdk/core/oauth/ExternalBrowserCredentialsProviderTest.java b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/oauth/ExternalBrowserCredentialsProviderTest.java index 5a3b1044d..81d98c4df 100644 --- a/databricks-sdk-java/src/test/java/com/databricks/sdk/core/oauth/ExternalBrowserCredentialsProviderTest.java +++ b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/oauth/ExternalBrowserCredentialsProviderTest.java @@ -660,4 +660,92 @@ void addOfflineAccessScopeWhenDisableOauthRefreshTokenIsFalse() { assertTrue(scopes.contains("offline_access")); assertTrue(scopes.contains("my-test-scope")); } + + @Test + void externalBrowserAuthWithAzureClientIdTest() throws IOException { + // Create mock HTTP client + HttpClient mockHttpClient = Mockito.mock(HttpClient.class); + + // Mock token cache + TokenCache mockTokenCache = Mockito.mock(TokenCache.class); + Mockito.doReturn(null).when(mockTokenCache).load(); + + // Create valid token for browser auth + Token browserAuthToken = + new Token( + "azure_access_token", + "Bearer", + "azure_refresh_token", + Instant.now().plusSeconds(3600)); + + // Create token source + SessionCredentialsTokenSource browserAuthTokenSource = + new SessionCredentialsTokenSource( + browserAuthToken, + mockHttpClient, + "https://test.azuredatabricks.net/oidc/v1/token", + "test-azure-client-id", + null, + Optional.empty(), + Optional.empty()); + + CachedTokenSource cachedTokenSource = + new CachedTokenSource.Builder(browserAuthTokenSource).setToken(browserAuthToken).build(); + + // Create Azure config with Azure client ID + DatabricksConfig config = + new DatabricksConfig() + .setAuthType("external-browser") + .setHost("https://test.azuredatabricks.net") + .setAzureClientId("test-azure-client-id") + .setHttpClient(mockHttpClient); + + // Create provider and mock browser auth + ExternalBrowserCredentialsProvider provider = + Mockito.spy(new ExternalBrowserCredentialsProvider(mockTokenCache)); + Mockito.doReturn(cachedTokenSource) + .when(provider) + .performBrowserAuth( + any(DatabricksConfig.class), + any(), + any(), + any(TokenCache.class), + any(OpenIDConnectEndpoints.class)); + + // Spy on config to inject OIDC endpoints + OpenIDConnectEndpoints endpoints = + new OpenIDConnectEndpoints( + "https://test.azuredatabricks.net/oidc/v1/token", + "https://test.azuredatabricks.net/oidc/v1/authorize"); + DatabricksConfig spyConfig = Mockito.spy(config); + Mockito.doReturn(endpoints).when(spyConfig).getOidcEndpoints(); + + // Configure provider + HeaderFactory headerFactory = provider.configure(spyConfig); + assertNotNull(headerFactory); + + // Verify headers contain the Azure token + Map headers = headerFactory.headers(); + assertEquals("Bearer azure_access_token", headers.get("Authorization")); + + // Capture and verify the OpenIDConnectEndpoints passed to performBrowserAuth + ArgumentCaptor endpointsCaptor = + ArgumentCaptor.forClass(OpenIDConnectEndpoints.class); + Mockito.verify(provider, Mockito.times(1)) + .performBrowserAuth( + any(DatabricksConfig.class), + any(), + any(), + any(TokenCache.class), + endpointsCaptor.capture()); + + // Verify the captured endpoints match what we expect for Azure + OpenIDConnectEndpoints capturedEndpoints = endpointsCaptor.getValue(); + assertNotNull(capturedEndpoints); + assertEquals("https://test.azuredatabricks.net/oidc/v1/token", capturedEndpoints.getTokenEndpoint()); + assertEquals("https://test.azuredatabricks.net/oidc/v1/authorize", capturedEndpoints.getAuthorizationEndpoint()); + + // Verify token was saved + Mockito.verify(mockTokenCache, Mockito.times(1)).save(any(Token.class)); + } } From 5e1cca759013b9a62de728fdca47cb2f7c17ae08 Mon Sep 17 00:00:00 2001 From: Hector Castejon Diaz Date: Tue, 10 Feb 2026 14:38:53 +0000 Subject: [PATCH 6/7] fmt --- .../ExternalBrowserCredentialsProvider.java | 8 ++- .../sdk/core/oauth/OAuthClientUtils.java | 33 ++++++------ ...xternalBrowserCredentialsProviderTest.java | 50 ++++++++++++++----- 3 files changed, 61 insertions(+), 30 deletions(-) diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/ExternalBrowserCredentialsProvider.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/ExternalBrowserCredentialsProvider.java index df7294871..1d8b48dac 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/ExternalBrowserCredentialsProvider.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/ExternalBrowserCredentialsProvider.java @@ -59,7 +59,7 @@ public OAuthHeaderFactory configure(DatabricksConfig config) { String clientId = OAuthClientUtils.resolveClientId(config); String clientSecret = OAuthClientUtils.resolveClientSecret(config); OpenIDConnectEndpoints oidcEndpoints = null; - try { + try { oidcEndpoints = OAuthClientUtils.resolveOidcEndpoints(config); } catch (IOException e) { LOGGER.error("Failed to resolve OIDC endpoints: {}", e.getMessage()); @@ -132,7 +132,11 @@ protected List getScopes(DatabricksConfig config, OpenIDConnectEndpoints } CachedTokenSource performBrowserAuth( - DatabricksConfig config, String clientId, String clientSecret, TokenCache tokenCache, OpenIDConnectEndpoints oidcEndpoints) + DatabricksConfig config, + String clientId, + String clientSecret, + TokenCache tokenCache, + OpenIDConnectEndpoints oidcEndpoints) throws IOException { LOGGER.debug("Performing browser authentication"); diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/OAuthClientUtils.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/OAuthClientUtils.java index 2507c4534..2af37d96e 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/OAuthClientUtils.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/OAuthClientUtils.java @@ -44,27 +44,30 @@ public static String resolveClientSecret(DatabricksConfig config) { } /** - * Resolves the OAuth OIDC endpoints from the configuration. Prioritizes regular OIDC endpoints, then Azure OIDC endpoints. - * If the client ID and client secret are provided, the OIDC endpoints are fetched from the discovery URL. - * If the Azure client ID and client secret are provided, the OIDC endpoints are fetched from the Azure AD endpoint. - * If no client ID and client secret are provided, the OIDC endpoints are fetched from the default OIDC endpoints. + * Resolves the OAuth OIDC endpoints from the configuration. Prioritizes regular OIDC endpoints, + * then Azure OIDC endpoints. If the client ID and client secret are provided, the OIDC endpoints + * are fetched from the discovery URL. If the Azure client ID and client secret are provided, the + * OIDC endpoints are fetched from the Azure AD endpoint. If no client ID and client secret are + * provided, the OIDC endpoints are fetched from the default OIDC endpoints. + * * @param config The Databricks configuration * @return The resolved OIDC endpoints * @throws IOException if the OIDC endpoints cannot be resolved */ - public static OpenIDConnectEndpoints resolveOidcEndpoints(DatabricksConfig config) throws IOException { + public static OpenIDConnectEndpoints resolveOidcEndpoints(DatabricksConfig config) + throws IOException { if (config.getClientId() != null && config.getClientSecret() != null) { return config.getOidcEndpoints(); - } else if (config.getAzureClientId() != null && config.getAzureClientSecret() != null) { - Request request = new Request("GET", config.getHost() + "/oidc/oauth2/v2.0/authorize"); - request.setRedirectionBehavior(false); - Response resp = config.getHttpClient().execute(request); - String realAuthUrl = resp.getFirstHeader("location"); - if (realAuthUrl == null) { - return null; - } - return new OpenIDConnectEndpoints( - realAuthUrl.replaceAll("/authorize", "/token"), realAuthUrl); + } else if (config.getAzureClientId() != null && config.getAzureClientSecret() != null) { + Request request = new Request("GET", config.getHost() + "/oidc/oauth2/v2.0/authorize"); + request.setRedirectionBehavior(false); + Response resp = config.getHttpClient().execute(request); + String realAuthUrl = resp.getFirstHeader("location"); + if (realAuthUrl == null) { + return null; + } + return new OpenIDConnectEndpoints( + realAuthUrl.replaceAll("/authorize", "/token"), realAuthUrl); } return config.getOidcEndpoints(); } diff --git a/databricks-sdk-java/src/test/java/com/databricks/sdk/core/oauth/ExternalBrowserCredentialsProviderTest.java b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/oauth/ExternalBrowserCredentialsProviderTest.java index 81d98c4df..e3cbbd0f7 100644 --- a/databricks-sdk-java/src/test/java/com/databricks/sdk/core/oauth/ExternalBrowserCredentialsProviderTest.java +++ b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/oauth/ExternalBrowserCredentialsProviderTest.java @@ -365,7 +365,12 @@ void cacheWithValidNonRefreshableTokenTest() throws IOException { // Verify performBrowserAuth was NOT called. Mockito.verify(provider, Mockito.never()) - .performBrowserAuth(any(DatabricksConfig.class), any(), any(), any(TokenCache.class), any(OpenIDConnectEndpoints.class)); + .performBrowserAuth( + any(DatabricksConfig.class), + any(), + any(), + any(TokenCache.class), + any(OpenIDConnectEndpoints.class)); // Verify no token was saved (we're using the cached one as-is). Mockito.verify(mockTokenCache, Mockito.never()).save(any(Token.class)); @@ -433,8 +438,7 @@ void cacheWithInvalidAccessTokenValidRefreshTest() throws IOException { any(String.class), any(String.class), any(TokenCache.class), - any(OpenIDConnectEndpoints.class) - ); + any(OpenIDConnectEndpoints.class)); // Verify token was saved back to cache Mockito.verify(mockTokenCache, Mockito.times(1)).save(any(Token.class)); @@ -512,7 +516,12 @@ void cacheWithInvalidAccessTokenRefreshFailingTest() throws IOException { Mockito.spy(new ExternalBrowserCredentialsProvider(mockTokenCache)); Mockito.doReturn(cachedTokenSource) .when(provider) - .performBrowserAuth(any(DatabricksConfig.class), any(), any(), any(TokenCache.class), any(OpenIDConnectEndpoints.class)); + .performBrowserAuth( + any(DatabricksConfig.class), + any(), + any(), + any(TokenCache.class), + any(OpenIDConnectEndpoints.class)); // Spy on the config to inject the endpoints DatabricksConfig spyConfig = Mockito.spy(config); @@ -531,7 +540,12 @@ void cacheWithInvalidAccessTokenRefreshFailingTest() throws IOException { // Verify performBrowserAuth was called since refresh failed Mockito.verify(provider, Mockito.times(1)) - .performBrowserAuth(any(DatabricksConfig.class), any(), any(), any(TokenCache.class), any(OpenIDConnectEndpoints.class)); + .performBrowserAuth( + any(DatabricksConfig.class), + any(), + any(), + any(TokenCache.class), + any(OpenIDConnectEndpoints.class)); // Verify token was saved after browser auth (for the new token) Mockito.verify(mockTokenCache, Mockito.times(1)).save(any(Token.class)); @@ -584,7 +598,12 @@ void cacheWithInvalidTokensTest() throws IOException { Mockito.spy(new ExternalBrowserCredentialsProvider(mockTokenCache)); Mockito.doReturn(cachedTokenSource) .when(provider) - .performBrowserAuth(any(DatabricksConfig.class), any(), any(), any(TokenCache.class), any(OpenIDConnectEndpoints.class)); + .performBrowserAuth( + any(DatabricksConfig.class), + any(), + any(), + any(TokenCache.class), + any(OpenIDConnectEndpoints.class)); // Spy on the config to inject the endpoints OpenIDConnectEndpoints endpoints = @@ -606,7 +625,12 @@ void cacheWithInvalidTokensTest() throws IOException { // Verify performBrowserAuth was called since we had an invalid token Mockito.verify(provider, Mockito.times(1)) - .performBrowserAuth(any(DatabricksConfig.class), any(), any(), any(TokenCache.class), any(OpenIDConnectEndpoints.class)); + .performBrowserAuth( + any(DatabricksConfig.class), + any(), + any(), + any(TokenCache.class), + any(OpenIDConnectEndpoints.class)); // Verify token was saved after browser auth (for the new token) Mockito.verify(mockTokenCache, Mockito.times(1)).save(any(Token.class)); @@ -673,10 +697,7 @@ void externalBrowserAuthWithAzureClientIdTest() throws IOException { // Create valid token for browser auth Token browserAuthToken = new Token( - "azure_access_token", - "Bearer", - "azure_refresh_token", - Instant.now().plusSeconds(3600)); + "azure_access_token", "Bearer", "azure_refresh_token", Instant.now().plusSeconds(3600)); // Create token source SessionCredentialsTokenSource browserAuthTokenSource = @@ -742,8 +763,11 @@ void externalBrowserAuthWithAzureClientIdTest() throws IOException { // Verify the captured endpoints match what we expect for Azure OpenIDConnectEndpoints capturedEndpoints = endpointsCaptor.getValue(); assertNotNull(capturedEndpoints); - assertEquals("https://test.azuredatabricks.net/oidc/v1/token", capturedEndpoints.getTokenEndpoint()); - assertEquals("https://test.azuredatabricks.net/oidc/v1/authorize", capturedEndpoints.getAuthorizationEndpoint()); + assertEquals( + "https://test.azuredatabricks.net/oidc/v1/token", capturedEndpoints.getTokenEndpoint()); + assertEquals( + "https://test.azuredatabricks.net/oidc/v1/authorize", + capturedEndpoints.getAuthorizationEndpoint()); // Verify token was saved Mockito.verify(mockTokenCache, Mockito.times(1)).save(any(Token.class)); From 4f8fb38de101ba503230cf98a498b513a190669f Mon Sep 17 00:00:00 2001 From: Hector Castejon Diaz Date: Tue, 10 Feb 2026 14:46:38 +0000 Subject: [PATCH 7/7] import --- .../sdk/core/oauth/ExternalBrowserCredentialsProviderTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/databricks-sdk-java/src/test/java/com/databricks/sdk/core/oauth/ExternalBrowserCredentialsProviderTest.java b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/oauth/ExternalBrowserCredentialsProviderTest.java index e3cbbd0f7..14f3bb25d 100644 --- a/databricks-sdk-java/src/test/java/com/databricks/sdk/core/oauth/ExternalBrowserCredentialsProviderTest.java +++ b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/oauth/ExternalBrowserCredentialsProviderTest.java @@ -7,7 +7,6 @@ import com.databricks.sdk.core.DatabricksException; import com.databricks.sdk.core.FixtureServer; import com.databricks.sdk.core.HeaderFactory; -import com.databricks.sdk.core.OpenIDConnectEndpoints; import com.databricks.sdk.core.commons.CommonsHttpClient; import com.databricks.sdk.core.http.HttpClient; import com.databricks.sdk.core.http.Request;