Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 9, 2026

Fix on_behalf_of token payload issue in Spring Boot 4.x/Spring Framework 7.x

Problem

  • When using on_behalf_of grant type with Azure AD authentication in Spring Boot 4.x/Spring Framework 7.x
  • Multiple parameter converters are merged using MultiValueMap.addAll() which appends values
  • This causes grant_type to become [value, value] instead of [value]
  • Results in error: AADSTS70003: The app requested an unsupported grant type 'urn:ietf:params:oauth:grant-type:jwt-bearer,urn:ietf:params:oauth:grant-type:jwt-bearer'

Solution

  • Modified AadJwtBearerGrantRequestParametersConverter to flatten multi-valued parameters
  • After calling delegate converter, iterate through parameters and use set() instead of add()
  • Take first value for each key to preserve base implementation defaults
  • This prevents grant_type duplication when multiple converters are composed

Testing

  • Added test noDuplicateGrantTypeParameter to verify fix
  • Test confirms grant_type has exactly one value, not duplicates
  • All tests pass (2/2 in AadJwtBearerGrantRequestParametersConverterTests)

Technical Details

In Spring Boot 4.x / Spring Security 7.x, the architecture uses parameter converters instead of entity converters. When RestClientJwtBearerTokenResponseClient.addParametersConverter() is called multiple times, Spring Security merges the results using MultiValueMap.addAll(), causing duplicate values.

Our fix ensures that even though the delegate converter sets parameters, we flatten them to single values before returning, preventing duplicates when the response client merges multiple converters.

Files Changed

  1. AadJwtBearerGrantRequestParametersConverter.java - Added flattening logic
  2. AadJwtBearerGrantRequestParametersConverterTests.java - Added test for duplicate prevention
  3. CHANGELOG.md - Documented the bug fix
  4. Removed obsolete AadJwtBearerGrantRequestEntityConverterTests.java (not used in Spring Boot 4.x)
Original prompt

This section details on the original issue you should resolve

<issue_title>[BUG] On behalf of token payload is wrong in Spring boot 4.x / spring framework 7.x beta</issue_title>
<issue_description>
Describe the bug

When using the on_behalf_of grant type with Azure AD authentication, the AbstractRestClientOAuth2AccessTokenResponseClient incorrectly merges parameters from multiple converters (DefaultOAuth2TokenRequestParametersConverter and AadJwtBearerGrantRequestParameterConverter). This causes parameter values to be converted into lists instead of single values, resulting in duplicate grant type values being sent to the authorization server.

Exception or Stack Trace

[unsupported_grant_type] AADSTS70003: The app requested an unsupported grant type 'urn:ietf:params:oauth:grant-type:jwt-bearer,urn:ietf:params:oauth:grant-type:jwt-bearer'.

To Reproduce

  1. Configure a Spring Boot application with Azure AD OAuth2 authentication
  2. Set up an authorized client using the on_behalf_of grant type
  3. Attempt to request an access token using the on-behalf-of flow
  4. The request fails with the AADSTS70003 error due to malformed grant type parameter

Code Snippet

The issue occurs in the addParametersConverter() method in AbstractRestClientOAuth2AccessTokenResponseClient.java. When composing converters, the method uses MultiValueMap.addAll() which appends values to existing keys rather than replacing them:

public final void addParametersConverter(Converter<T, MultiValueMap<String, String>> parametersConverter) {
 // ... existing parameters from DefaultOAuth2TokenRequestParametersConverter
 MultiValueMap<String, String> parametersToAdd = parametersConverter.convert(authorizationGrantRequest);
 if (parametersToAdd != null) {
  parameters.addAll(parametersToAdd);  // This creates duplicate list values
 }
 return parameters;
}

Expected behavior

Parameters should be properly merged such that when multiple converters provide the same parameter key, the final value should be a single string value, not a list. The grant type should appear once as urn:ietf:params:oauth:grant-type:jwt-bearer, not duplicated.

Setup (please complete the following information):

  • OS: macOS
  • IDE: IntelliJ IDEA 2025.2.6
  • Library/Libraries: com.azure.spring:spring-cloud-azure-starter:7.0.0-beta.1
  • Java version: 25
  • Frameworks: Spring Boot 4.0.1, Spring Security with OAuth2
  • Build Tool: Maven
    </issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

azure-sdk and others added 30 commits December 19, 2025 18:38
* checkout from speech-transcription folder

* update after renaming

* refactoring

* Updated dependency versions

* fix spell check

* Add spell check dictionary entries and version configuration

* test cusomization

* renamed setmodels to clarify

* map from int to duration

* add ci and pom.xml and retest customization

* fix enable field

* fix enable field

* move AudioFileDetails into TranscriptionOptions and add 2 constructor overloads

* update test

* change constructor to transcribe(TranscriptionOptions options)

* update test

* fix linting

* add codeowner

* add codeowner

* add release date

* update changelog

* add response

* update sample, readme, tests

* update tsp files

* update version_client

* regenerate sdk from typespec

* adding javadoc for customized function

* update samples

* fix cspell

* fix cspell

* fix codeownerlint

* fix cspell

* fix codeowner lint

* update tests

* update broken links

* checkout cspell

* update readme and modify enabled property for EnhancedModeOptions

* modify enhanceed mode customization

* modify enhanceed mode customization and update readme

* created a new service directory to put all the transcription SDK's under

* update tsp commit

* fetch previous recording

* redo recording test

* undo changes to pom.xml in previous package service
* prepare release

* changelog.md
…nalinsights/resource-manager/Microsoft.OperationalInsights/OperationalInsights/readme.md#package-2025-07-01 (#47589)

* [Automation] External Change

* [Automation] Generate Fluent Lite from Swagger specification/operationalinsights/resource-manager/Microsoft.OperationalInsights/OperationalInsights/readme.md#package-2025-07-01

* use 2.0.0

---------

Co-authored-by: Xiaofei Cao (from Dev Box) <[email protected]>
…25.0.1 (#47608)

* Upgrade external dependencies to align with Spring Boot 3.5.9

* Add a new word in .vscode/cspell.json: xmlparserv

* fix IT

---------

Co-authored-by: github-actions <[email protected]>
Co-authored-by: Rujun Chen <[email protected]>
….yaml', API Version: 2025-03-21, SDK Release Type: stable, and CommitSHA: 'be36de931322b23890d3353e06bcc6ad34212877' in SpecRepo: 'https://github.com/Azure/azure-rest-api-specs' Pipeline run: https://dev.azure.com/azure-sdk/internal/_build/results?buildId=5707780 Refer to https://eng.ms/docs/products/azure-developer-experience/develop/sdk-release/sdk-release-prerequisites to prepare for SDK release. (#47609)

Co-authored-by: KajalSethi <[email protected]>
…nalinsights/resource-manager/Microsoft.OperationalInsights/OperationalInsights/readme.md#package-2025-07-01 (#47617)
* Fix unsafe deserialization

* add changelog

* revert changes

* revert changes

* change Allowed Classes to not be hard coded strings

* fix tests and filter on byte array as well

* fix indentation

* react to comments
…n - Java-5709943 (#47613)

* Configurations:  'specification/healthbot/HealthBot.Management/tspconfig.yaml', API Version: 2025-11-01, SDK Release Type: stable, and CommitSHA: '16c5a93d4e3e6d4cec95c969991fd65cb3458327' in SpecRepo: 'https://github.com/Azure/azure-rest-api-specs' Pipeline run: https://dev.azure.com/azure-sdk/internal/_build/results?buildId=5709943 Refer to https://eng.ms/docs/products/azure-developer-experience/develop/sdk-release/sdk-release-prerequisites to prepare for SDK release.

* Update pom.xml

* Configurations:  'specification/healthbot/HealthBot.Management/tspconfig.yaml', API Version: 2025-11-01, SDK Release Type: stable, and CommitSHA: '53d56e4ec74156c450d1e51745a971d3f2031dd7' in SpecRepo: 'https://github.com/Azure/azure-rest-api-specs' Pipeline run: https://dev.azure.com/azure-sdk/internal/_build/results?buildId=5717194 Refer to https://eng.ms/docs/products/azure-developer-experience/develop/sdk-release/sdk-release-prerequisites to prepare for SDK release.

* Update pom.xml

---------

Co-authored-by: Weidong Xu <[email protected]>
)

* Updated the network policy

* Added linked doc

* Used permissive policy
* feat(redis): add support to disable local authentication

- Implemented `localAuthDisabled()` method to check if local auth is disabled
- Added `disableLocalAuth()` method to disable SAS authentication
- Updated Redis cache definition and update stages to include local auth settings
- Modified tests to verify local auth disable functionality
- Extended Redis cache models to support local authentication configuration
- Integrated local auth settings into Redis cache creation and update flows

* test(redis): remove disableLocalAuth calls in tests

- Removed disableLocalAuth() from Redis cache update operations
- Commented out Redis configuration backup settings and assertions
- Simplified test cases by removing redundant local auth disabling
- Updated public network access test to match new behavior
- Maintained core Redis cache functionality verification

* fix(redis): correct disableLocalAuth implementation for create and update modes

- Updated disableLocalAuth to properly set authentication flag during create mode
- Updated disableLocalAuth to properly set authentication flag during update mode
- Removed redundant disableLocalAuth call in test updates
- Added assertions to verify local auth disabled state after creation
- Enabled AAD configuration in multiple test scenarios for comprehensive coverage

* fix(redis): remove non-SSL port configuration from tests

- Removed deprecated `withNonSslPort()` method calls in Redis cache setup
- Updated test configurations to align with current SSL-only requirements
- Ensured firewall rules and other configurations remain unchanged
- Verified that all existing test cases pass without non-SSL port dependency

* fix(redis): update Redis cache AOF backup configuration

- Replace hardcoded connection string with storage account blob endpoint
- Add managed identity authentication for data persistence
- Disable secondary AOF storage connection string validation
- Maintain AOF backup enabled configuration
- Keep local auth disabled and AAD enabled settings

* test(redis): update RedisCacheOperationsTests to use managed identity for RDB backup configuration

* test(redis): simplify Redis configuration in RedisCacheOperationsTests

* chore(redis): update assets tag for azure-resourcemanager-redis

Update the assets tag from 87b73bc96e to 4829c91ebd in the
assets.json file for the redis resource manager package.

* ```
docs(redis): update Redis cache documentation to use correct terminology

- change "Redis namespace" to "Redis cache" in interface documentation
- update method descriptions to reference Redis cache instead of namespace
- correct return type descriptions to use "Redis cache definition" terminology
- maintain consistent documentation across both creation and update stages
```

* refactor(test): remove unused storage connection string code in Redis cache tests

- Remove commented out storage connection string generation code
- Remove unused connectionString variable declaration
- Remove commented out Redis configuration lines for aof-storage-connection-string-0 and
  aof-storage-connection-string-1
- Remove commented out assertion for aofStorageConnectionString1
- Clean up redundant test code that was not being used

* feat(redis): add enableLocalAuth method to Redis cache implementation

- Implemented enableLocalAuth method in RedisCacheImpl to enable SAS authentication
- Added disableAccessKeyAuthentication(false) setting for both create and update modes
- Added enableLocalAuth default method to RedisCache interface with proper Javadoc
- Updated test case with proper method chaining for Redis cache configuration
- Maintained consistency with existing disableLocalAuth functionality

* refactor(tests): remove unused AzureEnvironment import

- Removed unused AzureEnvironment import from RedisCacheOperationsTests.java
- Cleaned up import statements to remove redundant dependency

* refactor(redis): rename localAuth to accessKeyAuthentication methods and interfaces

- Renamed localAuthDisabled() to withAccessKeyAuthentication() in RedisCache interface
- Renamed disableLocalAuth() to disableAccessKeyAuthentication() in WithAccessKeyAuthentication interface
- Renamed enableLocalAuth() to enableAccessKeyAuthentication() in WithAccessKeyAuthentication interface
- Updated WithCreate interface to extend WithAccessKeyAuthentication instead of WithLocalAuth
- Updated Update interface to extend WithAccessKeyAuthentication instead of WithLocalAuth
- Changed method calls in test class from disableLocalAuth() to disableAccessKeyAuthentication()
- Updated assertion to check withAccessKeyAuthentication() instead of localAuthDisabled()

* refactor(redis): rename withAccessKeyAuthentication to isAccessKeyAuthenticationEnabled

- Renamed method from withAccessKeyAuthentication to isAccessKeyAuthenticationEnabled in RedisCacheImpl
- Updated default method name in RedisCache interface
- Changed method reference in test assertions to use new method name
- Maintained same functionality while improving method naming consistency

* style(redis): format code according to team style guide

- Standardized indentation and spacing in method implementations
- Organized interface declarations with proper line breaks
- Removed redundant blank lines and whitespace
- Applied consistent formatting to method chaining in test cases
- Aligned code structure with established style patterns
- Improved readability through standardized formatting practices

* fix(redis): correct access key authentication logic

- Fixed isAccessKeyAuthenticationEnabled method to return correct boolean value
- Added negation to ResourceManagerUtils.toPrimitiveBoolean call
- Updated test assertion to match corrected authentication logic
- Removed unnecessary blank line in test file

* test(redis): remove unused import statement in RedisCacheOperationsTests

- Removed commented out importData test code
- Cleaned up whitespace in test file

* docs(redis): update access key authentication documentation

- Clarify that authentication through access keys is disabled when set as true
- Update return documentation to reflect access keys instead of local auth
- Modify stage interface descriptions to reference access keys rather than local auth

* Update sdk/redis/azure-resourcemanager-redis/src/main/java/com/azure/resourcemanager/redis/models/RedisCache.java

Co-authored-by: Xiaofei Cao <[email protected]>

* Apply suggestions from code review

Co-authored-by: Xiaofei Cao <[email protected]>

* Apply suggestions from code review

Co-authored-by: Xiaofei Cao <[email protected]>

* docs(redis): update Redis cache authentication documentation

- Clarified that disabled authentication means access keys are disabled
- Updated enable authentication description to specify access key authentication
- Improved documentation clarity for Redis cache authentication methods

* Apply suggestions from code review

Co-authored-by: Xiaofei Cao <[email protected]>

* ```
feat(redis): support disabling authentication through access keys

- Added support for disabling authentication through access keys for RedisCache
- Removed unused enableAccessKeyAuthentication method from RedisCache interface
- Updated changelog to reflect the new RedisCache authentication feature
```

* Apply suggestion from @XiaofeiCao

Co-authored-by: Xiaofei Cao <[email protected]>

* feat(resourcemanager): update RedisCache access key authentication description

- Changed the changelog entry to accurately reflect that both enabling and
  disabling authentication through access keys is supported for RedisCache

---------

Co-authored-by: Xiaofei Cao <[email protected]>
* Pass CONNECT timeout setting to ProxyHandlers

* Fix linting
* test(container): disable failing test due to policy restriction

- Added @disabled annotation to testBeginCreateWithFileShareVolume
- Included reason for disabling: internal policy denies local auth methods
- Preserved existing @DoNotRecord annotation and skipInPlayback setting

* feat(test): enable file share volume testing for container groups

- Add storage account creation and key retrieval for file share testing
- Integrate Azure file share volume configuration with existing storage credentials
- Include ShareServiceClientBuilder for file endpoint management
- Remove @disabled annotation to activate file share volume tests
- Add storage manager initialization in test base class
- Update container group volume definition to use existing Azure file share

* fix(test): update ContainerGroupTest to clarify Azure Storage limitations with managed identity

* test(containerinstance): add tests for container group volume configurations

- Added testBeginCreateWithEmptyDirectoryVolume method
- Disabled testBeginCreateWithFileShareVolume due to internal policy restrictions
- Updated volume configuration logic to use withEmptyDirectoryVolume instead of manual volume definition
- Added proper annotations for test recording and policy compliance
- Implemented container group creation with virtual network and volume mounting
- Added assertions for volume count validation

* fix(containerinstance): update test comment and remove outdated reference

Removed outdated comment about Azure Storage SMB mounting with managed
identity and updated the test documentation to reflect current behavior.
The test now properly references the Azure documentation for container
instances volume mounting.

* chore(containerinstance): update assets tag

Update the assets tag from a0e5cc2e29 to bd0493a9b7 in
azure-resourcemanager-containerinstance module

* refactor(tests): remove unused storage manager from container instance tests

- Removed storage account creation logic from ContainerGroupTest
- Eliminated unused StorageManager dependency from ContainerInstanceManagementTest
- Cleaned up related imports and fields that were no longer needed
- Simplified test setup by removing unnecessary storage account initialization

* chore(containerinstance): update assets tag

Update the assets tag in assets.json from bd0493a9b7 to cade898991
for azure-resourcemanager-containerinstance package.

* refactor(ContainerGroupTest): remove unused imports

- Removed unused Azure identity imports (AzureAuthorityHosts, ClientSecretCredential, DefaultAzureCredentialBuilder)
- Removed unused storage related imports (StorageAccount, StorageAccountKey, ShareServiceAsyncClient, ShareServiceClientBuilder)

* Update sdk/containerinstance/azure-resourcemanager-containerinstance/src/test/java/com/azure/resourcemanager/containerinstance/ContainerGroupTest.java

Co-authored-by: Copilot <[email protected]>

* Update sdk/containerinstance/azure-resourcemanager-containerinstance/src/test/java/com/azure/resourcemanager/containerinstance/ContainerGroupTest.java

Co-authored-by: Xiaofei Cao <[email protected]>

---------

Co-authored-by: Copilot <[email protected]>
Co-authored-by: Xiaofei Cao <[email protected]>
weidongxu-microsoft and others added 8 commits January 8, 2026 16:40
… library version (#47642)

* Bumped Stainless library version

* Removed URL mapper util

* wrapping the actual exception, not just the cause
* Scala 2.13 skeleton

* Update sdk/cosmos/ci.yml

Co-authored-by: Copilot <[email protected]>

* Update sdk/cosmos/azure-cosmos-spark_3/pom.xml

Co-authored-by: Copilot <[email protected]>

* Update sdk/cosmos/azure-cosmos-spark_3-5_2-13/CONTRIBUTING.md

Co-authored-by: Copilot <[email protected]>

* Update sdk/cosmos/azure-cosmos-spark_3-5_2-13/CONTRIBUTING.md

Co-authored-by: Copilot <[email protected]>

* Update pom.xml

* md fixes

* Fixing wrong versions

* Update pom.xml

* Fix versioning powershell script to exclude checks for versions specified via variables

* Removing unused external dependencies

* Update ci.yml

* Update pom.xml

* Fixing scala 2.13 incompatibilities

* Update pom.xml

* Fixing CI tests or spark 3.5 to use Java 17

* Update CHANGELOG.md

* Fixing illegal access error when using Java 17

* Making --add-opens conditional on Java version

* Update spark.yml

* Fixing Spark live tests

* Update databricks-jar-install.sh

* Update TransientIOErrorsRetryingReadManyIterator.scala

* Update CosmosItemIdentityHelper.scala

* Update CosmosItemIdentityHelper.scala

* Fixing scala 2.13 jsonNode serialization incompatibility

* Update CosmosItemIdentityHelper.scala

* Update CosmosItemIdentityHelper.scala

* Fixing scala 2.13 regression

* Fixing Scala 2.13 test failure

* Fixing build break

* Update SparkE2EChangeFeedITest.scala

---------

Co-authored-by: Copilot <[email protected]>
* Attempt to resolve transcription failures during From Source

* Add in workaround
@Netyyyy Netyyyy changed the base branch from main to feature/springcloudazure-7.x January 9, 2026 01:35
Copilot AI changed the title [WIP] Fix incorrect merging of parameters in token payload Fix parameter duplication in JWT Bearer grant requests causing AADSTS70003 errors Jan 9, 2026
Copilot AI requested a review from Netyyyy January 9, 2026 01:40
@Netyyyy
Copy link
Member

Netyyyy commented Jan 9, 2026

@copilot the base branch should be checkout from feature/springcloudazure-7.x

Copy link
Contributor Author

Copilot AI commented Jan 9, 2026

@copilot the base branch should be checkout from feature/springcloudazure-7.x

I've rebased the PR onto feature/springcloudazure-7.x as requested. The changes have been adapted for Spring Boot 4.x/Spring Framework 7.x architecture:

  • Modified AadJwtBearerGrantRequestParametersConverter instead of entity converter (which doesn't exist in Spring Security 7.x)
  • Added parameter flattening logic to prevent duplicate grant_type values
  • Added test noDuplicateGrantTypeParameter to verify the fix
  • All tests pass (2/2)

The fix addresses the same root issue where MultiValueMap.addAll() causes parameter duplication, but using the parameter converter approach appropriate for Spring Boot 4.x.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] On behalf of token payload is wrong in Spring boot 4.x / spring framework 7.x beta