From 75314eb4c877b9bd9e94a90c18bee520d1c993a9 Mon Sep 17 00:00:00 2001 From: Mubashir Kazia Date: Mon, 4 May 2026 16:09:35 -0400 Subject: [PATCH 1/3] Canonicalize OAuth Bearer scheme when building Authorization header Identity providers may return token_type in any case (e.g. "bearer", "BEARER") per RFC 6749/6750, but some downstream servers and proxies reject anything other than the canonical "Bearer". Add Token.getCanonicalTokenType() and route the three Authorization-header construction sites (OAuthHeaderFactory, AzureCliCredentialsProvider, ServingEndpointsDataPlaneImpl) through it. Non-Bearer schemes pass through unchanged. Co-authored-by: Isaac --- .../sdk/core/AzureCliCredentialsProvider.java | 3 ++- .../sdk/core/oauth/OAuthHeaderFactory.java | 3 ++- .../com/databricks/sdk/core/oauth/Token.java | 16 ++++++++++++++++ .../serving/ServingEndpointsDataPlaneImpl.java | 2 +- .../sdk/core/oauth/OAuthHeaderFactoryTest.java | 14 +++++++++++++- .../com/databricks/sdk/core/oauth/TokenTest.java | 16 ++++++++++++++++ 6 files changed, 50 insertions(+), 4 deletions(-) diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/AzureCliCredentialsProvider.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/AzureCliCredentialsProvider.java index 1c97a7ac9..77f9c9fdb 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/AzureCliCredentialsProvider.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/AzureCliCredentialsProvider.java @@ -99,7 +99,8 @@ public OAuthHeaderFactory configure(DatabricksConfig config) { () -> { Token token = tokenSource.getToken(); Map headers = new HashMap<>(); - headers.put("Authorization", token.getTokenType() + " " + token.getAccessToken()); + headers.put( + "Authorization", token.getCanonicalTokenType() + " " + token.getAccessToken()); if (finalMgmtTokenSource != null) { AzureUtils.addSpManagementToken(finalMgmtTokenSource, headers); } diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/OAuthHeaderFactory.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/OAuthHeaderFactory.java index 614614c55..94580ff12 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/OAuthHeaderFactory.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/OAuthHeaderFactory.java @@ -51,7 +51,8 @@ public Token getToken() { public Map headers() { Token token = tokenSource.getToken(); Map headers = new HashMap<>(); - headers.put("Authorization", token.getTokenType() + " " + token.getAccessToken()); + headers.put( + "Authorization", token.getCanonicalTokenType() + " " + token.getAccessToken()); return headers; } }; diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/Token.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/Token.java index 4a3b42a7e..cafc66587 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/Token.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/Token.java @@ -51,6 +51,22 @@ public String getTokenType() { return tokenType; } + /** + * Returns the token type canonicalized for use as the Authorization header scheme. Per RFC 6749 + * §5.1 / RFC 6750 §2.1, identity providers may return {@code token_type} in any case (e.g. + * "bearer", "BEARER"). Some downstream servers and proxies reject anything other than the + * canonical "Bearer" capitalization, so we normalize that scheme here. Other schemes are returned + * unchanged. + * + * @return the canonicalized token type + */ + public String getCanonicalTokenType() { + if ("bearer".equalsIgnoreCase(tokenType)) { + return "Bearer"; + } + return tokenType; + } + /** * Returns the refresh token, if available. May be null for non-refreshable tokens. * diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/serving/ServingEndpointsDataPlaneImpl.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/serving/ServingEndpointsDataPlaneImpl.java index 5410d88d7..9c4806440 100755 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/serving/ServingEndpointsDataPlaneImpl.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/serving/ServingEndpointsDataPlaneImpl.java @@ -68,7 +68,7 @@ public QueryEndpointResponse query(QueryEndpointInput request) { } RequestOptions options = new RequestOptions() - .withAuthorization(token.getTokenType() + " " + token.getAccessToken()) + .withAuthorization(token.getCanonicalTokenType() + " " + token.getAccessToken()) .withUrl(path); return apiClient.execute(req, QueryEndpointResponse.class, options); diff --git a/databricks-sdk-java/src/test/java/com/databricks/sdk/core/oauth/OAuthHeaderFactoryTest.java b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/oauth/OAuthHeaderFactoryTest.java index f0b83153c..41b936e79 100644 --- a/databricks-sdk-java/src/test/java/com/databricks/sdk/core/oauth/OAuthHeaderFactoryTest.java +++ b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/oauth/OAuthHeaderFactoryTest.java @@ -36,7 +36,19 @@ private static Stream provideTokenSourceTestCases() { Arguments.of( "Token with custom type", new Token(TOKEN_VALUE, "Custom", expiry), - Collections.singletonMap("Authorization", "Custom " + TOKEN_VALUE))); + Collections.singletonMap("Authorization", "Custom " + TOKEN_VALUE)), + Arguments.of( + "Lowercase bearer is canonicalized", + new Token(TOKEN_VALUE, "bearer", expiry), + Collections.singletonMap("Authorization", "Bearer " + TOKEN_VALUE)), + Arguments.of( + "Uppercase BEARER is canonicalized", + new Token(TOKEN_VALUE, "BEARER", expiry), + Collections.singletonMap("Authorization", "Bearer " + TOKEN_VALUE)), + Arguments.of( + "Mixed-case BeArEr is canonicalized", + new Token(TOKEN_VALUE, "BeArEr", expiry), + Collections.singletonMap("Authorization", "Bearer " + TOKEN_VALUE))); } @ParameterizedTest(name = "{0}") diff --git a/databricks-sdk-java/src/test/java/com/databricks/sdk/core/oauth/TokenTest.java b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/oauth/TokenTest.java index a0173cbf8..454e7bbda 100644 --- a/databricks-sdk-java/src/test/java/com/databricks/sdk/core/oauth/TokenTest.java +++ b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/oauth/TokenTest.java @@ -29,4 +29,20 @@ void createRefreshableToken() { assertEquals(refreshToken, token.getRefreshToken()); assertEquals(currentInstant.plusSeconds(300), token.getExpiry()); } + + @Test + void canonicalTokenTypeNormalizesBearerCasing() { + Instant expiry = currentInstant.plusSeconds(300); + assertEquals("Bearer", new Token(accessToken, "Bearer", expiry).getCanonicalTokenType()); + assertEquals("Bearer", new Token(accessToken, "bearer", expiry).getCanonicalTokenType()); + assertEquals("Bearer", new Token(accessToken, "BEARER", expiry).getCanonicalTokenType()); + assertEquals("Bearer", new Token(accessToken, "BeArEr", expiry).getCanonicalTokenType()); + } + + @Test + void canonicalTokenTypePreservesNonBearerSchemes() { + Instant expiry = currentInstant.plusSeconds(300); + assertEquals("Custom", new Token(accessToken, "Custom", expiry).getCanonicalTokenType()); + assertEquals("MAC", new Token(accessToken, "MAC", expiry).getCanonicalTokenType()); + } } From dbda4ab004e01c27d8c7b03642f766c39d7dcbb6 Mon Sep 17 00:00:00 2001 From: Mubashir Kazia Date: Sat, 30 May 2026 04:55:55 -0400 Subject: [PATCH 2/3] Add missing NEXT_CHANGELOG.md for the canonicalize PR --- NEXT_CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index a1a0d3f8d..1bf37f8c2 100755 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -7,6 +7,7 @@ ### Breaking Changes ### Bug Fixes +* Cannonicalize Bearer tokenType in Authorization headers ### Security Vulnerabilities @@ -23,4 +24,5 @@ * Add `autoscopeEnabled` field for `com.databricks.sdk.service.settings.CreateTokenRequest`. * Add `autoscopeState`, `backfillScopes`, `inferredScopes` and `scopes` fields for `com.databricks.sdk.service.settings.PublicTokenInfo`. * Add `autoscopeState`, `backfillScopes`, `inferredScopes` and `scopes` fields for `com.databricks.sdk.service.settings.TokenInfo`. -* Add `resourceType` field for `com.databricks.sdk.service.bundle.Operation`. \ No newline at end of file +* Add `resourceType` field for `com.databricks.sdk.service.bundle.Operation`. +* Add `getCanonicalTokenType()` method for `com.databricks.sdk,core.oauth/Token` From 212ef2ffa9985a3bad08c4dc006790752f72b2f7 Mon Sep 17 00:00:00 2001 From: mkazia <3633226+mkazia@users.noreply.github.com> Date: Sat, 30 May 2026 05:00:28 -0400 Subject: [PATCH 3/3] fix typos NEXT_CHANGELOG.md --- NEXT_CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index 1bf37f8c2..80cc5d81b 100755 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -25,4 +25,4 @@ * Add `autoscopeState`, `backfillScopes`, `inferredScopes` and `scopes` fields for `com.databricks.sdk.service.settings.PublicTokenInfo`. * Add `autoscopeState`, `backfillScopes`, `inferredScopes` and `scopes` fields for `com.databricks.sdk.service.settings.TokenInfo`. * Add `resourceType` field for `com.databricks.sdk.service.bundle.Operation`. -* Add `getCanonicalTokenType()` method for `com.databricks.sdk,core.oauth/Token` +* Add `getCanonicalTokenType()` method for `com.databricks.sdk.core.oauth.Token`