diff --git a/.vscode/cspell.json b/.vscode/cspell.json
index bd85726cce1c..1ce3f3ab3379 100644
--- a/.vscode/cspell.json
+++ b/.vscode/cspell.json
@@ -1245,6 +1245,7 @@
"words": [
"aadb",
"AADB",
+ "AADSTS",
"amqps",
"Authoritys",
"autoconfiguation",
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/CHANGELOG.md b/sdk/spring/spring-cloud-azure-autoconfigure/CHANGELOG.md
index 24ab5d428261..aa281c5af28a 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/CHANGELOG.md
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/CHANGELOG.md
@@ -8,6 +8,8 @@
### Bugs Fixed
+- Fixed OAuth2 JWT Bearer grant request parameter duplication issue where `grant_type` was being duplicated when using the on-behalf-of flow, causing `AADSTS70003: unsupported_grant_type` error. [#47657](https://github.com/Azure/azure-sdk-for-java/issues/47657)
+
### Other Changes
## 7.0.0-beta.1 (2025-12-23)
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/security/AadJwtBearerGrantRequestParametersConverter.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/security/AadJwtBearerGrantRequestParametersConverter.java
index 234e2c8f2a02..fc201915ddc4 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/security/AadJwtBearerGrantRequestParametersConverter.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/security/AadJwtBearerGrantRequestParametersConverter.java
@@ -4,12 +4,16 @@
package com.azure.spring.cloud.autoconfigure.implementation.aad.security;
import org.springframework.core.convert.converter.Converter;
-import org.springframework.security.oauth2.client.endpoint.DefaultOAuth2TokenRequestParametersConverter;
import org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest;
+import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
/**
* This is a special JWT Bearer flow implementation for Microsoft identify platform.
+ * This converter only adds the Azure-specific parameter "requested_token_use" with value "on_behalf_of".
+ * The standard OAuth2 parameters (grant_type, assertion, client_id, etc.) are added by Spring Security's
+ * DefaultOAuth2TokenRequestParametersConverter, which is automatically included when using
+ * RestClientJwtBearerTokenResponseClient.
*
* @since 7.0.0
* @see OAuth 2.0 On-Behalf-Of
@@ -17,12 +21,9 @@
public class AadJwtBearerGrantRequestParametersConverter
implements Converter> {
- private final DefaultOAuth2TokenRequestParametersConverter delegate =
- new DefaultOAuth2TokenRequestParametersConverter<>();
-
@Override
public MultiValueMap convert(JwtBearerGrantRequest jwtBearerGrantRequest) {
- MultiValueMap parameters = delegate.convert(jwtBearerGrantRequest);
+ MultiValueMap parameters = new LinkedMultiValueMap<>();
parameters.add("requested_token_use", "on_behalf_of");
return parameters;
}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/security/AadJwtBearerGrantRequestEntityConverterTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/security/AadJwtBearerGrantRequestEntityConverterTests.java
index bdeb4fe16707..53d5ed97b57f 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/security/AadJwtBearerGrantRequestEntityConverterTests.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/security/AadJwtBearerGrantRequestEntityConverterTests.java
@@ -40,5 +40,11 @@ void requestedTokenUseParameter() {
Assertions.assertNotNull(parameters);
assertTrue(parameters.containsKey("requested_token_use"));
assertEquals("on_behalf_of", parameters.getFirst("requested_token_use"));
+ // Verify that the converter does not add grant_type or other standard OAuth2 parameters
+ // to avoid duplication when composed with DefaultOAuth2TokenRequestParametersConverter
+ Assertions.assertFalse(parameters.containsKey("grant_type"),
+ "Converter should not add grant_type to avoid duplication");
+ assertEquals(1, parameters.size(),
+ "Converter should only add the Azure-specific parameter");
}
}