From a8bf37eaae5c99546d95570cadf22686897e4847 Mon Sep 17 00:00:00 2001 From: Rujun Chen Date: Mon, 4 May 2026 12:35:01 +0800 Subject: [PATCH 01/27] Fix AAD issuer and audience validation defaults --- .../AadResourceServerConfiguration.java | 14 ++++- .../aad/filter/AadAuthenticationFilter.java | 4 +- .../AadResourceServerConfigurationTests.java | 39 ++++++++++++ .../filter/AadAuthenticationFilterTests.java | 62 +++++++++++++++++++ 4 files changed, 116 insertions(+), 3 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java index 94b43b0a0a45..8753120ff384 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java @@ -8,6 +8,7 @@ import com.azure.spring.cloud.autoconfigure.implementation.aad.configuration.properties.AadResourceServerProperties; import com.azure.spring.cloud.autoconfigure.implementation.aad.security.constants.AadJwtClaimNames; import com.azure.spring.cloud.autoconfigure.implementation.aad.security.jwt.AadJwtIssuerValidator; +import com.azure.spring.cloud.autoconfigure.implementation.aad.security.jwt.AadTrustedIssuerRepository; import com.azure.spring.cloud.autoconfigure.implementation.aad.security.properties.AadAuthorizationServerEndpoints; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; @@ -64,6 +65,7 @@ JwtDecoder jwtDecoder(AadAuthenticationProperties aadAuthenticationProperties) { List> createDefaultValidator(AadAuthenticationProperties aadAuthenticationProperties) { List> validators = new ArrayList<>(); List validAudiences = new ArrayList<>(); + String tenantId = aadAuthenticationProperties.getProfile().getTenantId(); if (StringUtils.hasText(aadAuthenticationProperties.getAppIdUri())) { validAudiences.add(aadAuthenticationProperties.getAppIdUri()); } @@ -73,11 +75,21 @@ List> createDefaultValidator(AadAuthenticationProperti if (!validAudiences.isEmpty()) { validators.add(new JwtClaimValidator>(AadJwtClaimNames.AUD, validAudiences::containsAll)); } - validators.add(new AadJwtIssuerValidator()); + if (isMultiTenantsApplication(tenantId)) { + validators.add(new AadJwtIssuerValidator()); + } else { + validators.add(new AadJwtIssuerValidator(new AadTrustedIssuerRepository(tenantId))); + } validators.add(new JwtTimestampValidator()); return validators; } + private boolean isMultiTenantsApplication(String tenantId) { + return "common".equalsIgnoreCase(tenantId) + || "organizations".equalsIgnoreCase(tenantId) + || "consumers".equalsIgnoreCase(tenantId); + } + @EnableWebSecurity @EnableMethodSecurity @ConditionalOnDefaultWebSecurity diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/filter/AadAuthenticationFilter.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/filter/AadAuthenticationFilter.java index d1d55f186b2f..27de595c9908 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/filter/AadAuthenticationFilter.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/filter/AadAuthenticationFilter.java @@ -69,7 +69,7 @@ public AadAuthenticationFilter(AadAuthenticationProperties aadAuthenticationProp endpoints, aadAuthenticationProperties, resourceRetriever, - false + true ), restTemplateBuilder ); @@ -97,7 +97,7 @@ public AadAuthenticationFilter(AadAuthenticationProperties aadAuthenticationProp endpoints, aadAuthenticationProperties, resourceRetriever, - false, + true, jwkSetCache ), restTemplateBuilder diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java index 53902ba4260e..87f5d2a6c57e 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java @@ -4,6 +4,7 @@ import com.azure.identity.extensions.implementation.template.AzureAuthenticationTemplate; import com.azure.spring.cloud.autoconfigure.implementation.aad.configuration.properties.AadAuthenticationProperties; +import com.azure.spring.cloud.autoconfigure.implementation.aad.security.jwt.AadJwtIssuerValidator; import com.azure.spring.cloud.autoconfigure.implementation.aad.security.AadResourceServerHttpSecurityConfigurer; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; import com.nimbusds.jwt.proc.JWTClaimsSetAwareJWSKeySelector; @@ -94,6 +95,44 @@ void testExistAudienceDefaultValidator() { }); } + @Test + void testSingleTenantUsesTrustedIssuerRepository() { + resourceServerContextRunner() + .withPropertyValues("spring.cloud.azure.active-directory.enabled=true") + .run(context -> { + AadAuthenticationProperties properties = context.getBean(AadAuthenticationProperties.class); + AadResourceServerConfiguration bean = context.getBean(AadResourceServerConfiguration.class); + List> defaultValidator = bean.createDefaultValidator(properties); + + AadJwtIssuerValidator issuerValidator = (AadJwtIssuerValidator) defaultValidator.stream() + .filter(AadJwtIssuerValidator.class::isInstance) + .findFirst() + .orElseThrow(() -> new IllegalStateException("AadJwtIssuerValidator not found")); + + assertThat(ReflectionTestUtils.getField(issuerValidator, "trustedIssuerRepo")).isNotNull(); + }); + } + + @Test + void testMultiTenantUsesPrefixIssuerValidation() { + resourceServerRunner() + .withPropertyValues("spring.cloud.azure.active-directory.enabled=true", + "spring.cloud.azure.active-directory.profile.tenant-id=common", + "spring.cloud.azure.active-directory.app-id-uri=fake-app-id-uri") + .run(context -> { + AadAuthenticationProperties properties = context.getBean(AadAuthenticationProperties.class); + AadResourceServerConfiguration bean = context.getBean(AadResourceServerConfiguration.class); + List> defaultValidator = bean.createDefaultValidator(properties); + + AadJwtIssuerValidator issuerValidator = (AadJwtIssuerValidator) defaultValidator.stream() + .filter(AadJwtIssuerValidator.class::isInstance) + .findFirst() + .orElseThrow(() -> new IllegalStateException("AadJwtIssuerValidator not found")); + + assertThat(ReflectionTestUtils.getField(issuerValidator, "trustedIssuerRepo")).isNull(); + }); + } + @Test void testResourceServerHttpSecurityConfigured() { resourceServerContextRunner() diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/filter/AadAuthenticationFilterTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/filter/AadAuthenticationFilterTests.java index 6a2e629fcf7c..e46f0fdd5082 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/filter/AadAuthenticationFilterTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/filter/AadAuthenticationFilterTests.java @@ -10,7 +10,9 @@ import com.azure.spring.cloud.autoconfigure.implementation.aad.security.properties.AadAuthorizationServerEndpoints; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.jwk.source.JWKSetCache; import com.nimbusds.jose.proc.BadJOSEException; +import com.nimbusds.jose.util.ResourceRetriever; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; @@ -38,6 +40,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import org.springframework.test.util.ReflectionTestUtils; class AadAuthenticationFilterTests { private static final String TOKEN = "dummy-token"; @@ -146,4 +149,63 @@ void testAlreadyAuthenticated() throws ServletException, IOException, ParseExcep verify(userPrincipalManager, times(0)).buildUserPrincipal(TOKEN); } + @Test + void testConstructorEnableExplicitAudienceCheck() { + AadAuthenticationProperties properties = mock(AadAuthenticationProperties.class); + AadCredentialProperties credentialProperties = new AadCredentialProperties(); + credentialProperties.setClientId("fake-client-id"); + credentialProperties.setClientSecret("fake-client-secret"); + when(properties.getCredential()).thenReturn(credentialProperties); + AadProfileProperties profileProperties = new AadProfileProperties(); + profileProperties.setTenantId("fake-tenant-id"); + when(properties.getProfile()).thenReturn(profileProperties); + + AadAuthorizationServerEndpoints endpoints = mock(AadAuthorizationServerEndpoints.class); + when(endpoints.getJwkSetEndpoint()).thenReturn("file://dummy"); + ResourceRetriever resourceRetriever = url -> null; + + AadAuthenticationFilter testFilter = new AadAuthenticationFilter( + properties, + endpoints, + resourceRetriever, + new RestTemplateBuilder() + ); + + UserPrincipalManager principalManager = (UserPrincipalManager) ReflectionTestUtils.getField(testFilter, + "userPrincipalManager"); + assertThat(principalManager).isNotNull(); + assertThat(ReflectionTestUtils.getField(principalManager, "explicitAudienceCheck")).isEqualTo(true); + } + + @Test + @SuppressWarnings("deprecation") + void testConstructorWithJwkSetCacheEnableExplicitAudienceCheck() { + AadAuthenticationProperties properties = mock(AadAuthenticationProperties.class); + AadCredentialProperties credentialProperties = new AadCredentialProperties(); + credentialProperties.setClientId("fake-client-id"); + credentialProperties.setClientSecret("fake-client-secret"); + when(properties.getCredential()).thenReturn(credentialProperties); + AadProfileProperties profileProperties = new AadProfileProperties(); + profileProperties.setTenantId("fake-tenant-id"); + when(properties.getProfile()).thenReturn(profileProperties); + + AadAuthorizationServerEndpoints endpoints = mock(AadAuthorizationServerEndpoints.class); + when(endpoints.getJwkSetEndpoint()).thenReturn("file://dummy"); + ResourceRetriever resourceRetriever = url -> null; + JWKSetCache jwkSetCache = mock(JWKSetCache.class); + + AadAuthenticationFilter testFilter = new AadAuthenticationFilter( + properties, + endpoints, + resourceRetriever, + jwkSetCache, + new RestTemplateBuilder() + ); + + UserPrincipalManager principalManager = (UserPrincipalManager) ReflectionTestUtils.getField(testFilter, + "userPrincipalManager"); + assertThat(principalManager).isNotNull(); + assertThat(ReflectionTestUtils.getField(principalManager, "explicitAudienceCheck")).isEqualTo(true); + } + } From a88077e0ec7cfc59d6079ee601d607e30f9d0e1d Mon Sep 17 00:00:00 2001 From: Rujun Chen Date: Mon, 4 May 2026 12:38:58 +0800 Subject: [PATCH 02/27] Update change log --- sdk/spring/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/spring/CHANGELOG.md b/sdk/spring/CHANGELOG.md index 13c2b3c2b539..fd303b28f3fc 100644 --- a/sdk/spring/CHANGELOG.md +++ b/sdk/spring/CHANGELOG.md @@ -8,6 +8,7 @@ This section includes changes in `spring-cloud-azure-autoconfigure` module. #### Bugs Fixed - Fixed JDBC/Azure Database and Redis passwordless connection scope defaulting using the wrong `azure.scopes` value for Azure China and Azure US Government when `spring.cloud.azure.profile.cloud-type` is set to `azure_china` or `azure_us_government`. The scopes are now correctly derived from the merged cloud type. ([#47096](https://github.com/Azure/azure-sdk-for-java/issues/47096)) +- Hardened AAD token validation defaults in `spring-cloud-azure-autoconfigure`: resource server issuer validation now enforces tenant-aware trusted issuers for single-tenant configurations, and `AadAuthenticationFilter` now enables explicit audience validation by default. [49032](https://github.com/Azure/azure-sdk-for-java/pull/49033) ### Spring Cloud Azure Stream Binder Service Bus This section includes changes in `spring-cloud-azure-stream-binder-servicebus` module. From 3d2b4b46d681fc11e1abb1a4a4d396bd3f3f4784 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 May 2026 13:47:53 +0000 Subject: [PATCH 03/27] Address review comments: fix import order, centralize isMultiTenantsApplication, fix changelog link Agent-Logs-Url: https://github.com/Azure/azure-sdk-for-java/sessions/ac6fd8b6-366e-4630-bcaf-fee4ad6e62c3 Co-authored-by: rujche <171773178+rujche@users.noreply.github.com> --- sdk/spring/CHANGELOG.md | 2 +- .../aad/configuration/AadResourceServerConfiguration.java | 8 +------- .../properties/AadAuthenticationProperties.java | 2 +- .../aad/filter/AadAuthenticationFilterTests.java | 2 +- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/sdk/spring/CHANGELOG.md b/sdk/spring/CHANGELOG.md index fd303b28f3fc..43f64cc755a0 100644 --- a/sdk/spring/CHANGELOG.md +++ b/sdk/spring/CHANGELOG.md @@ -8,7 +8,7 @@ This section includes changes in `spring-cloud-azure-autoconfigure` module. #### Bugs Fixed - Fixed JDBC/Azure Database and Redis passwordless connection scope defaulting using the wrong `azure.scopes` value for Azure China and Azure US Government when `spring.cloud.azure.profile.cloud-type` is set to `azure_china` or `azure_us_government`. The scopes are now correctly derived from the merged cloud type. ([#47096](https://github.com/Azure/azure-sdk-for-java/issues/47096)) -- Hardened AAD token validation defaults in `spring-cloud-azure-autoconfigure`: resource server issuer validation now enforces tenant-aware trusted issuers for single-tenant configurations, and `AadAuthenticationFilter` now enables explicit audience validation by default. [49032](https://github.com/Azure/azure-sdk-for-java/pull/49033) +- Hardened AAD token validation defaults in `spring-cloud-azure-autoconfigure`: resource server issuer validation now enforces tenant-aware trusted issuers for single-tenant configurations, and `AadAuthenticationFilter` now enables explicit audience validation by default. ([#49033](https://github.com/Azure/azure-sdk-for-java/pull/49033)) ### Spring Cloud Azure Stream Binder Service Bus This section includes changes in `spring-cloud-azure-stream-binder-servicebus` module. diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java index 8753120ff384..3ee25e295fd2 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java @@ -75,7 +75,7 @@ List> createDefaultValidator(AadAuthenticationProperti if (!validAudiences.isEmpty()) { validators.add(new JwtClaimValidator>(AadJwtClaimNames.AUD, validAudiences::containsAll)); } - if (isMultiTenantsApplication(tenantId)) { + if (AadAuthenticationProperties.isMultiTenantsApplication(tenantId)) { validators.add(new AadJwtIssuerValidator()); } else { validators.add(new AadJwtIssuerValidator(new AadTrustedIssuerRepository(tenantId))); @@ -84,12 +84,6 @@ List> createDefaultValidator(AadAuthenticationProperti return validators; } - private boolean isMultiTenantsApplication(String tenantId) { - return "common".equalsIgnoreCase(tenantId) - || "organizations".equalsIgnoreCase(tenantId) - || "consumers".equalsIgnoreCase(tenantId); - } - @EnableWebSecurity @EnableMethodSecurity @ConditionalOnDefaultWebSecurity diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/properties/AadAuthenticationProperties.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/properties/AadAuthenticationProperties.java index fcf14eecf569..5625be9a5fb4 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/properties/AadAuthenticationProperties.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/properties/AadAuthenticationProperties.java @@ -525,7 +525,7 @@ private AuthorizationGrantType decideDefaultGrantTypeFromApplicationType(String return grantType; } - private boolean isMultiTenantsApplication(String tenantId) { + public static boolean isMultiTenantsApplication(String tenantId) { return "common".equals(tenantId) || "organizations".equals(tenantId) || "consumers".equals(tenantId); } } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/filter/AadAuthenticationFilterTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/filter/AadAuthenticationFilterTests.java index e46f0fdd5082..6b2c9cb5ea68 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/filter/AadAuthenticationFilterTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/filter/AadAuthenticationFilterTests.java @@ -28,6 +28,7 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.test.util.ReflectionTestUtils; import java.io.IOException; import java.text.ParseException; @@ -40,7 +41,6 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import org.springframework.test.util.ReflectionTestUtils; class AadAuthenticationFilterTests { private static final String TOKEN = "dummy-token"; From fc1af23d6eacc97c5042bcab5f4eeff1260f3243 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 01:31:49 +0000 Subject: [PATCH 04/27] Revert isMultiTenantsApplication to private: not intended as public API Agent-Logs-Url: https://github.com/Azure/azure-sdk-for-java/sessions/bf7036ec-7fdf-46d5-8050-b72f537332d4 Co-authored-by: rujche <171773178+rujche@users.noreply.github.com> --- .../aad/configuration/AadResourceServerConfiguration.java | 6 +++++- .../properties/AadAuthenticationProperties.java | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java index 3ee25e295fd2..b70e5395c067 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java @@ -75,7 +75,7 @@ List> createDefaultValidator(AadAuthenticationProperti if (!validAudiences.isEmpty()) { validators.add(new JwtClaimValidator>(AadJwtClaimNames.AUD, validAudiences::containsAll)); } - if (AadAuthenticationProperties.isMultiTenantsApplication(tenantId)) { + if (isMultiTenantsApplication(tenantId)) { validators.add(new AadJwtIssuerValidator()); } else { validators.add(new AadJwtIssuerValidator(new AadTrustedIssuerRepository(tenantId))); @@ -84,6 +84,10 @@ List> createDefaultValidator(AadAuthenticationProperti return validators; } + private static boolean isMultiTenantsApplication(String tenantId) { + return "common".equals(tenantId) || "organizations".equals(tenantId) || "consumers".equals(tenantId); + } + @EnableWebSecurity @EnableMethodSecurity @ConditionalOnDefaultWebSecurity diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/properties/AadAuthenticationProperties.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/properties/AadAuthenticationProperties.java index 5625be9a5fb4..a08a2e6ca720 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/properties/AadAuthenticationProperties.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/properties/AadAuthenticationProperties.java @@ -525,7 +525,7 @@ private AuthorizationGrantType decideDefaultGrantTypeFromApplicationType(String return grantType; } - public static boolean isMultiTenantsApplication(String tenantId) { + private static boolean isMultiTenantsApplication(String tenantId) { return "common".equals(tenantId) || "organizations".equals(tenantId) || "consumers".equals(tenantId); } } From 5261f670758ce7772bc7415187836cf77c889752 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 02:22:35 +0000 Subject: [PATCH 05/27] Revert static modifier from isMultiTenantsApplication in AadAuthenticationProperties Agent-Logs-Url: https://github.com/Azure/azure-sdk-for-java/sessions/f49e9a93-8331-49f4-b8f0-2f78dbb7c32a Co-authored-by: rujche <171773178+rujche@users.noreply.github.com> --- .../configuration/properties/AadAuthenticationProperties.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/properties/AadAuthenticationProperties.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/properties/AadAuthenticationProperties.java index a08a2e6ca720..fcf14eecf569 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/properties/AadAuthenticationProperties.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/properties/AadAuthenticationProperties.java @@ -525,7 +525,7 @@ private AuthorizationGrantType decideDefaultGrantTypeFromApplicationType(String return grantType; } - private static boolean isMultiTenantsApplication(String tenantId) { + private boolean isMultiTenantsApplication(String tenantId) { return "common".equals(tenantId) || "organizations".equals(tenantId) || "consumers".equals(tenantId); } } From 50ff6deaabc96c7d67bc2a04243182c3861687b9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 05:31:50 +0000 Subject: [PATCH 06/27] Fix grammar and spacing in UserPrincipalManager audience-mismatch error message Agent-Logs-Url: https://github.com/Azure/azure-sdk-for-java/sessions/239ed9d3-e532-4064-ab4d-b407dee617d0 Co-authored-by: rujche <171773178+rujche@users.noreply.github.com> --- .../implementation/aad/filter/UserPrincipalManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/filter/UserPrincipalManager.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/filter/UserPrincipalManager.java index 2517661c84fe..16da143c58eb 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/filter/UserPrincipalManager.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/filter/UserPrincipalManager.java @@ -220,7 +220,7 @@ public void verify(JWTClaimsSet claimsSet, SecurityContext ctx) throws BadJWTExc LOGGER.debug("Matched audience: [{}]", matchedAudience.get()); } else { throw new BadJWTException("Invalid token audience. Provided value " + claimsSet.getAudience() - + "does not match neither client-id nor AppIdUri."); + + " does not match either the client-id or AppIdUri."); } } } From 8dbba20bac06d23ba2d5093e79342376b6adb458 Mon Sep 17 00:00:00 2001 From: Rujun Chen Date: Wed, 6 May 2026 15:41:00 +0800 Subject: [PATCH 07/27] Fix critical security vulnerability in AAD Resource Server tenant validation This commit addresses a severe cross-tenant access vulnerability in the AadJwtIssuerValidator that allowed tokens from any Azure AD tenant to be accepted instead of validating against the configured tenant. Changes: 1. Removed unsafe no-arg constructor from AadJwtIssuerValidator 2. Removed insecure issuer prefix-only validation logic 3. Enforced exact issuer matching using AadTrustedIssuerRepository 4. Added startup validation to reject common/organizations/consumers tenant-id 5. Updated AadResourceServerConfiguration to always use tenant-specific validation 6. Added comprehensive unit tests for tenant-id validation 7. Fixed existing tests to include valid tenant-id configuration Security Impact: - Prevents cross-tenant authentication attacks - Enforces tenant isolation for resource servers - Provides clear error messages for misconfigured applications - Blocks deployment with insecure tenant-id configurations Related: MSRC vulnerability report on tenant validation bypass --- .../AadResourceServerConfiguration.java | 18 ++-- .../security/jwt/AadJwtIssuerValidator.java | 26 +----- .../AadResourceServerConfigurationTests.java | 85 ++++++++++++++++++- .../jwt/AadJwtIssuerValidatorTests.java | 26 ------ 4 files changed, 96 insertions(+), 59 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java index b70e5395c067..f6640d1a2f5a 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java @@ -66,6 +66,7 @@ List> createDefaultValidator(AadAuthenticationProperti List> validators = new ArrayList<>(); List validAudiences = new ArrayList<>(); String tenantId = aadAuthenticationProperties.getProfile().getTenantId(); + validateTenantId(tenantId); if (StringUtils.hasText(aadAuthenticationProperties.getAppIdUri())) { validAudiences.add(aadAuthenticationProperties.getAppIdUri()); } @@ -75,17 +76,20 @@ List> createDefaultValidator(AadAuthenticationProperti if (!validAudiences.isEmpty()) { validators.add(new JwtClaimValidator>(AadJwtClaimNames.AUD, validAudiences::containsAll)); } - if (isMultiTenantsApplication(tenantId)) { - validators.add(new AadJwtIssuerValidator()); - } else { - validators.add(new AadJwtIssuerValidator(new AadTrustedIssuerRepository(tenantId))); - } + validators.add(new AadJwtIssuerValidator(new AadTrustedIssuerRepository(tenantId))); validators.add(new JwtTimestampValidator()); return validators; } - private static boolean isMultiTenantsApplication(String tenantId) { - return "common".equals(tenantId) || "organizations".equals(tenantId) || "consumers".equals(tenantId); + private static void validateTenantId(String tenantId) { + if ("common".equalsIgnoreCase(tenantId) + || "organizations".equalsIgnoreCase(tenantId) + || "consumers".equalsIgnoreCase(tenantId)) { + throw new IllegalArgumentException( + "For resource server, 'spring.cloud.azure.active-directory.profile.tenant-id' cannot be set to '" + tenantId + "'. " + + "This configuration would accept tokens from any Azure AD tenant, creating a security vulnerability. " + + "Please configure a specific tenant ID (GUID) to restrict token validation to your organization's tenant only."); + } } @EnableWebSecurity diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/security/jwt/AadJwtIssuerValidator.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/security/jwt/AadJwtIssuerValidator.java index 6aaea487f2e9..c6d7f7602cad 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/security/jwt/AadJwtIssuerValidator.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/security/jwt/AadJwtIssuerValidator.java @@ -18,45 +18,23 @@ */ public class AadJwtIssuerValidator implements OAuth2TokenValidator { - private static final String LOGIN_MICROSOFT_ONLINE_ISSUER = "https://login.microsoftonline.com/"; - - private static final String STS_WINDOWS_ISSUER = "https://sts.windows.net/"; - - private static final String STS_CHINA_CLOUD_API_ISSUER = "https://sts.chinacloudapi.cn/"; - private final JwtClaimValidator validator; private final AadTrustedIssuerRepository trustedIssuerRepo; - /** - * Constructs a {@link AadJwtIssuerValidator} using the provided parameters - */ - public AadJwtIssuerValidator() { - this(null); - } - /** * Constructs a {@link AadJwtIssuerValidator} using the provided parameters * * @param aadTrustedIssuerRepository trusted issuer repository. */ public AadJwtIssuerValidator(AadTrustedIssuerRepository aadTrustedIssuerRepository) { + Assert.notNull(aadTrustedIssuerRepository, "aadTrustedIssuerRepository cannot be null"); this.trustedIssuerRepo = aadTrustedIssuerRepository; this.validator = new JwtClaimValidator<>(AadJwtClaimNames.ISS, trustedIssuerRepoValidIssuer()); } private Predicate trustedIssuerRepoValidIssuer() { - return iss -> { - if (iss == null) { - return false; - } - if (trustedIssuerRepo == null) { - return iss.startsWith(LOGIN_MICROSOFT_ONLINE_ISSUER) - || iss.startsWith(STS_WINDOWS_ISSUER) - || iss.startsWith(STS_CHINA_CLOUD_API_ISSUER); - } - return trustedIssuerRepo.getTrustedIssuers().contains(iss); - }; + return iss -> iss != null && trustedIssuerRepo.getTrustedIssuers().contains(iss); } /** diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java index 87f5d2a6c57e..884cac20606a 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java @@ -114,12 +114,91 @@ void testSingleTenantUsesTrustedIssuerRepository() { } @Test - void testMultiTenantUsesPrefixIssuerValidation() { + void testValidateTenantIdRejectsCommon() { resourceServerRunner() .withPropertyValues("spring.cloud.azure.active-directory.enabled=true", "spring.cloud.azure.active-directory.profile.tenant-id=common", "spring.cloud.azure.active-directory.app-id-uri=fake-app-id-uri") .run(context -> { + assertThat(context).hasFailed(); + assertThat(context.getStartupFailure()) + .hasRootCauseInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("cannot be set to 'common'") + .hasMessageContaining("security vulnerability"); + }); + } + + @Test + void testValidateTenantIdRejectsOrganizations() { + resourceServerRunner() + .withPropertyValues("spring.cloud.azure.active-directory.enabled=true", + "spring.cloud.azure.active-directory.profile.tenant-id=organizations", + "spring.cloud.azure.active-directory.app-id-uri=fake-app-id-uri") + .run(context -> { + assertThat(context).hasFailed(); + assertThat(context.getStartupFailure()) + .hasRootCauseInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("cannot be set to 'organizations'") + .hasMessageContaining("security vulnerability"); + }); + } + + @Test + void testValidateTenantIdRejectsConsumers() { + resourceServerRunner() + .withPropertyValues("spring.cloud.azure.active-directory.enabled=true", + "spring.cloud.azure.active-directory.profile.tenant-id=consumers", + "spring.cloud.azure.active-directory.app-id-uri=fake-app-id-uri") + .run(context -> { + assertThat(context).hasFailed(); + assertThat(context.getStartupFailure()) + .hasRootCauseInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("cannot be set to 'consumers'") + .hasMessageContaining("security vulnerability"); + }); + } + + @Test + void testValidateTenantIdRejectsNull() { + resourceServerRunner() + .withPropertyValues("spring.cloud.azure.active-directory.enabled=true", + "spring.cloud.azure.active-directory.app-id-uri=fake-app-id-uri") + .run(context -> { + // When tenant-id is null, AadAuthenticationProperties.afterPropertiesSet() sets it to "common" + // Then our validation should reject "common" + assertThat(context).hasFailed(); + assertThat(context.getStartupFailure()) + .hasRootCauseInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("cannot be set to 'common'") + .hasMessageContaining("security vulnerability"); + }); + } + + @Test + void testValidateTenantIdRejectsEmptyString() { + resourceServerRunner() + .withPropertyValues("spring.cloud.azure.active-directory.enabled=true", + "spring.cloud.azure.active-directory.profile.tenant-id=", + "spring.cloud.azure.active-directory.app-id-uri=fake-app-id-uri") + .run(context -> { + // When tenant-id is empty string, AadAuthenticationProperties.afterPropertiesSet() sets it to "common" + // Then our validation should reject "common" + assertThat(context).hasFailed(); + assertThat(context.getStartupFailure()) + .hasRootCauseInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("cannot be set to 'common'") + .hasMessageContaining("security vulnerability"); + }); + } + + @Test + void testValidateTenantIdAcceptsValidGuid() { + resourceServerRunner() + .withPropertyValues("spring.cloud.azure.active-directory.enabled=true", + "spring.cloud.azure.active-directory.profile.tenant-id=12345678-1234-1234-1234-123456789012", + "spring.cloud.azure.active-directory.app-id-uri=fake-app-id-uri") + .run(context -> { + assertThat(context).hasNotFailed(); AadAuthenticationProperties properties = context.getBean(AadAuthenticationProperties.class); AadResourceServerConfiguration bean = context.getBean(AadResourceServerConfiguration.class); List> defaultValidator = bean.createDefaultValidator(properties); @@ -129,7 +208,7 @@ void testMultiTenantUsesPrefixIssuerValidation() { .findFirst() .orElseThrow(() -> new IllegalStateException("AadJwtIssuerValidator not found")); - assertThat(ReflectionTestUtils.getField(issuerValidator, "trustedIssuerRepo")).isNull(); + assertThat(ReflectionTestUtils.getField(issuerValidator, "trustedIssuerRepo")).isNotNull(); }); } @@ -192,6 +271,7 @@ void useCustomJwtGrantedAuthoritiesConverter() { void useDefaultWebSecurityConfigurerAdapter() { resourceServerRunner() .withPropertyValues("spring.cloud.azure.active-directory.enabled=true", + "spring.cloud.azure.active-directory.profile.tenant-id=fake-tenant-id", "spring.cloud.azure.active-directory.credential.client-id=fake-client-id" ) .run(context -> { @@ -212,6 +292,7 @@ void useCustomSecurityFilterChain() { .withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO)) .withClassLoader(new FilteredClassLoader(AzureAuthenticationTemplate.class, ClientRegistration.class)) .withPropertyValues("spring.cloud.azure.active-directory.enabled=true", + "spring.cloud.azure.active-directory.profile.tenant-id=fake-tenant-id", "spring.cloud.azure.active-directory.credential.client-id=fake-client-id" ) .run(context -> { diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/security/jwt/AadJwtIssuerValidatorTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/security/jwt/AadJwtIssuerValidatorTests.java index 668958075e3e..71062a5f32ae 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/security/jwt/AadJwtIssuerValidatorTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/security/jwt/AadJwtIssuerValidatorTests.java @@ -20,32 +20,6 @@ class AadJwtIssuerValidatorTests { private final AadTrustedIssuerRepository aadTrustedIssuerRepository = new AadTrustedIssuerRepository("fake-tenant" + "-id"); - @Test - void testNoStructureIssuerSuccessVerify() { - AadProfileProperties profile = new AadProfileProperties(); - profile.setTenantId("fake-tenant-id"); - when(aadAuthenticationProperties.getProfile()).thenReturn(profile); - when(jwt.getClaim(AadJwtClaimNames.ISS)).thenReturn("https://sts.windows.net/fake-tenant-id/v2.0"); - - AadJwtIssuerValidator validator = new AadJwtIssuerValidator(); - OAuth2TokenValidatorResult result = validator.validate(jwt); - assertThat(result).isNotNull(); - assertThat(result.getErrors()).isEmpty(); - } - - @Test - void testNoStructureIssuerFailureVerify() { - AadProfileProperties profile = new AadProfileProperties(); - profile.setTenantId("common"); - when(aadAuthenticationProperties.getProfile()).thenReturn(profile); - when(jwt.getClaim(AadJwtClaimNames.ISS)).thenReturn("https://sts.failure.net/fake-tenant-id/v2.0"); - - AadJwtIssuerValidator validator = new AadJwtIssuerValidator(); - OAuth2TokenValidatorResult result = validator.validate(jwt); - assertThat(result).isNotNull(); - assertThat(result.getErrors()).isNotEmpty(); - } - @Test void testIssuerSuccessVerify() { AadProfileProperties profile = new AadProfileProperties(); From 2a273b55564dc18ca9ce0cc3a1b175bef40f912a Mon Sep 17 00:00:00 2001 From: Rujun Chen Date: Wed, 6 May 2026 15:50:44 +0800 Subject: [PATCH 08/27] Add explicit tenant ID (tid) claim validation Following Microsoft's security best practices, add explicit validation of the 'tid' claim to ensure tokens are from the configured tenant. Changes: 1. Added JwtClaimValidator for TID claim validation 2. Placed TID validation after audience check as per Microsoft docs 3. Added comprehensive comments explaining the validation order 4. Documented why Subject and Actor validation are application-specific 5. Updated tests to reflect new validator count (4 validators with audience) Security Enhancement: - Defense in depth: TID claim provides additional tenant isolation - Follows Microsoft recommendation: 'Always check that the tid in a token matches the tenant ID used to store data with the application' - Complements existing issuer validation for robust tenant verification Reference: https://learn.microsoft.com/en-us/entra/identity-platform/claims-validation#validate-the-tenant --- .../AadResourceServerConfiguration.java | 18 ++++++++++++++++++ .../AadResourceServerConfigurationTests.java | 4 +++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java index f6640d1a2f5a..525272cb9531 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java @@ -63,10 +63,25 @@ JwtDecoder jwtDecoder(AadAuthenticationProperties aadAuthenticationProperties) { } List> createDefaultValidator(AadAuthenticationProperties aadAuthenticationProperties) { + // Validate JWT claims according to Microsoft's security best practices: + // https://learn.microsoft.com/en-us/entra/identity-platform/claims-validation + // + // This method creates validators for the following claims in order: + // 1. aud (Audience) - Ensures the token is intended for this application + // 2. tid (Tenant ID) - Ensures the token is from the configured tenant (critical for tenant isolation) + // 3. iss (Issuer) - Ensures the token is issued by the expected Azure AD tenant endpoint + // 4. exp/nbf (Timestamp) - Ensures the token is not expired or used before its valid time + // + // Note: Subject (sub, oid, roles, groups, wids) and Actor (scp) validation are NOT performed here. + // These are application-specific authorization concerns that vary by endpoint and should be handled + // in business logic using Spring Security's @PreAuthorize, @Secured, or custom authorization logic. + // See: https://learn.microsoft.com/en-us/entra/identity-platform/claims-validation#validate-the-subject + List> validators = new ArrayList<>(); List validAudiences = new ArrayList<>(); String tenantId = aadAuthenticationProperties.getProfile().getTenantId(); validateTenantId(tenantId); + if (StringUtils.hasText(aadAuthenticationProperties.getAppIdUri())) { validAudiences.add(aadAuthenticationProperties.getAppIdUri()); } @@ -76,8 +91,11 @@ List> createDefaultValidator(AadAuthenticationProperti if (!validAudiences.isEmpty()) { validators.add(new JwtClaimValidator>(AadJwtClaimNames.AUD, validAudiences::containsAll)); } + + validators.add(new JwtClaimValidator(AadJwtClaimNames.TID, tenantId::equals)); validators.add(new AadJwtIssuerValidator(new AadTrustedIssuerRepository(tenantId))); validators.add(new JwtTimestampValidator()); + return validators; } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java index 884cac20606a..7e7f9206d001 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java @@ -77,6 +77,7 @@ void testNotAudienceDefaultValidator() { .getBean(AadResourceServerConfiguration.class); List> defaultValidator = bean.createDefaultValidator(properties); assertThat(defaultValidator).isNotNull(); + // ISS + TID + Timestamp validators (no audience) assertThat(defaultValidator).hasSize(3); }); } @@ -91,7 +92,8 @@ void testExistAudienceDefaultValidator() { .getBean(AadResourceServerConfiguration.class); List> defaultValidator = bean.createDefaultValidator(properties); assertThat(defaultValidator).isNotNull(); - assertThat(defaultValidator).hasSize(3); + // AUD + ISS + TID + Timestamp validators + assertThat(defaultValidator).hasSize(4); }); } From 73c3ca44403171512030ddfd0f87c8f845be6cdc Mon Sep 17 00:00:00 2001 From: Rujun Chen Date: Wed, 6 May 2026 15:58:01 +0800 Subject: [PATCH 09/27] Simplify tenant ID validation logic and unify error messages Consolidate tenant-id validation checks into a single condition for better code maintainability and clearer error messaging. Changes: 1. Merged null/empty check with common/organizations/consumers check 2. Unified error message to cover all invalid tenant-id scenarios 3. Updated all 5 validation tests to expect the consolidated error message 4. Fixed test expectation for audience validation count Improvement: - Reduces code duplication and improves readability - Provides a single, comprehensive error message for all invalid cases - Simplifies future maintenance by having one validation point All 15 tests passing. --- .../AadResourceServerConfiguration.java | 5 +++-- .../AadResourceServerConfigurationTests.java | 14 +++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java index 525272cb9531..56ddbb47f41f 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java @@ -100,11 +100,12 @@ List> createDefaultValidator(AadAuthenticationProperti } private static void validateTenantId(String tenantId) { - if ("common".equalsIgnoreCase(tenantId) + if (!StringUtils.hasText(tenantId) + || "common".equalsIgnoreCase(tenantId) || "organizations".equalsIgnoreCase(tenantId) || "consumers".equalsIgnoreCase(tenantId)) { throw new IllegalArgumentException( - "For resource server, 'spring.cloud.azure.active-directory.profile.tenant-id' cannot be set to '" + tenantId + "'. " + "For resource server, 'spring.cloud.azure.active-directory.profile.tenant-id' cannot be null, empty, or set to 'common', 'organizations', or 'consumers'. " + "This configuration would accept tokens from any Azure AD tenant, creating a security vulnerability. " + "Please configure a specific tenant ID (GUID) to restrict token validation to your organization's tenant only."); } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java index 7e7f9206d001..1e1ea83134de 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java @@ -77,8 +77,8 @@ void testNotAudienceDefaultValidator() { .getBean(AadResourceServerConfiguration.class); List> defaultValidator = bean.createDefaultValidator(properties); assertThat(defaultValidator).isNotNull(); - // ISS + TID + Timestamp validators (no audience) - assertThat(defaultValidator).hasSize(3); + // AUD (from app-id-uri) + TID + ISS + Timestamp validators + assertThat(defaultValidator).hasSize(4); }); } @@ -125,7 +125,7 @@ void testValidateTenantIdRejectsCommon() { assertThat(context).hasFailed(); assertThat(context.getStartupFailure()) .hasRootCauseInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("cannot be set to 'common'") + .hasMessageContaining("cannot be null, empty, or set to") .hasMessageContaining("security vulnerability"); }); } @@ -140,7 +140,7 @@ void testValidateTenantIdRejectsOrganizations() { assertThat(context).hasFailed(); assertThat(context.getStartupFailure()) .hasRootCauseInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("cannot be set to 'organizations'") + .hasMessageContaining("cannot be null, empty, or set to") .hasMessageContaining("security vulnerability"); }); } @@ -155,7 +155,7 @@ void testValidateTenantIdRejectsConsumers() { assertThat(context).hasFailed(); assertThat(context.getStartupFailure()) .hasRootCauseInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("cannot be set to 'consumers'") + .hasMessageContaining("cannot be null, empty, or set to") .hasMessageContaining("security vulnerability"); }); } @@ -171,7 +171,7 @@ void testValidateTenantIdRejectsNull() { assertThat(context).hasFailed(); assertThat(context.getStartupFailure()) .hasRootCauseInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("cannot be set to 'common'") + .hasMessageContaining("cannot be null, empty, or set to") .hasMessageContaining("security vulnerability"); }); } @@ -188,7 +188,7 @@ void testValidateTenantIdRejectsEmptyString() { assertThat(context).hasFailed(); assertThat(context.getStartupFailure()) .hasRootCauseInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("cannot be set to 'common'") + .hasMessageContaining("cannot be null, empty, or set to") .hasMessageContaining("security vulnerability"); }); } From 01e3e23c6e930cb40d49647171e94a2a4320f0d0 Mon Sep 17 00:00:00 2001 From: Rujun Chen Date: Wed, 6 May 2026 16:08:22 +0800 Subject: [PATCH 10/27] Update CHANGELOG for AAD resource server security hardening Document the breaking change requiring specific tenant-id configuration for AAD resource server authentication to prevent cross-tenant token acceptance vulnerability. --- sdk/spring/CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sdk/spring/CHANGELOG.md b/sdk/spring/CHANGELOG.md index 43f64cc755a0..1cefe7cebef8 100644 --- a/sdk/spring/CHANGELOG.md +++ b/sdk/spring/CHANGELOG.md @@ -5,10 +5,13 @@ This section includes changes in `spring-cloud-azure-autoconfigure` module. +#### Breaking Changes + +- AAD resource server now requires `spring.cloud.azure.active-directory.profile.tenant-id` to be a specific tenant ID (GUID) (empty string, `common`, `organizations`, `consumers` are invalid). ([#49033](https://github.com/Azure/azure-sdk-for-java/pull/49033)) + #### Bugs Fixed - Fixed JDBC/Azure Database and Redis passwordless connection scope defaulting using the wrong `azure.scopes` value for Azure China and Azure US Government when `spring.cloud.azure.profile.cloud-type` is set to `azure_china` or `azure_us_government`. The scopes are now correctly derived from the merged cloud type. ([#47096](https://github.com/Azure/azure-sdk-for-java/issues/47096)) -- Hardened AAD token validation defaults in `spring-cloud-azure-autoconfigure`: resource server issuer validation now enforces tenant-aware trusted issuers for single-tenant configurations, and `AadAuthenticationFilter` now enables explicit audience validation by default. ([#49033](https://github.com/Azure/azure-sdk-for-java/pull/49033)) ### Spring Cloud Azure Stream Binder Service Bus This section includes changes in `spring-cloud-azure-stream-binder-servicebus` module. From 22ad1b9df72ea0843f549d26c80dad1a57071fce Mon Sep 17 00:00:00 2001 From: Rujun Chen Date: Wed, 6 May 2026 16:30:09 +0800 Subject: [PATCH 11/27] Fix test failures by adding tenant-id configuration Update tests to specify tenant-id or application-type to prevent Resource Server configuration from being inadvertently activated. Changes: 1. AadWebApplicationConfigurationTests - Added application-type=web_application to explicitly configure tests as web application only 2. AadAutoConfigurationServletConditionTests - Added tenant-id=fake-tenant-id for tests using oauthClientAndResourceServerRunner 3. AadOAuth2ClientConfigurationTests - Added tenant-id=fake-tenant-id for testWithRequiredPropertiesSet These fixes address test failures caused by the new tenant-id validation logic that rejects common/organizations/consumers values for resource server configurations. Tests now properly isolate web application scenarios from resource server scenarios. All 1084 tests now pass. --- .../AadAutoConfigurationServletConditionTests.java | 6 ++++-- .../configuration/AadOAuth2ClientConfigurationTests.java | 3 ++- .../configuration/AadWebApplicationConfigurationTests.java | 2 ++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadAutoConfigurationServletConditionTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadAutoConfigurationServletConditionTests.java index ff817f148995..525871593932 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadAutoConfigurationServletConditionTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadAutoConfigurationServletConditionTests.java @@ -19,7 +19,8 @@ void servletApplication() { oauthClientAndResourceServerRunner() .withPropertyValues( "spring.cloud.azure.active-directory.enabled=true", - "spring.cloud.azure.active-directory.credential.client-id=fake-client-id" + "spring.cloud.azure.active-directory.credential.client-id=fake-client-id", + "spring.cloud.azure.active-directory.profile.tenant-id=fake-tenant-id" ) .run(context -> assertThat(context).hasSingleBean(AadAuthenticationProperties.class)); } @@ -29,7 +30,8 @@ void nonServletApplication() { oauthClientAndResourceServerRunner() .withPropertyValues( "spring.cloud.azure.active-directory.enabled=true", - "spring.cloud.azure.active-directory.credential.client-id=fake-client-id" + "spring.cloud.azure.active-directory.credential.client-id=fake-client-id", + "spring.cloud.azure.active-directory.profile.tenant-id=fake-tenant-id" ) .withClassLoader(new FilteredClassLoader(SERVLET_WEB_APPLICATION_CLASS)) .run(context -> assertThat(context).doesNotHaveBean(AadAuthenticationProperties.class)); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadOAuth2ClientConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadOAuth2ClientConfigurationTests.java index a8daec063143..7813ed21249c 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadOAuth2ClientConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadOAuth2ClientConfigurationTests.java @@ -65,7 +65,8 @@ void testWithRequiredPropertiesSet() { oauthClientAndResourceServerRunner() .withPropertyValues( "spring.cloud.azure.active-directory.enabled=true", - "spring.cloud.azure.active-directory.credential.client-id=fake-client-id" + "spring.cloud.azure.active-directory.credential.client-id=fake-client-id", + "spring.cloud.azure.active-directory.profile.tenant-id=fake-tenant-id" ) .run(context -> { assertThat(context).hasSingleBean(AadAuthenticationProperties.class); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadWebApplicationConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadWebApplicationConfigurationTests.java index 59d66b9ee79d..a28f8a94569d 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadWebApplicationConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadWebApplicationConfigurationTests.java @@ -26,6 +26,7 @@ class AadWebApplicationConfigurationTests { void useDefaultSecurityFilterChain() { webApplicationContextRunner() .withPropertyValues("spring.cloud.azure.active-directory.enabled=true", + "spring.cloud.azure.active-directory.application-type=web_application", "spring.cloud.azure.active-directory.credential.client-id=fake-client-id" ) .run(context -> { @@ -45,6 +46,7 @@ void useCustomSecurityFilterChain() { AadAutoConfiguration.class) .withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO)) .withPropertyValues("spring.cloud.azure.active-directory.enabled=true", + "spring.cloud.azure.active-directory.application-type=web_application", "spring.cloud.azure.active-directory.credential.client-id=fake-client-id" ) .run(context -> { From 7c3c5992d8608ba8f746ceddb036a2c6ae5e90be Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 08:50:45 +0000 Subject: [PATCH 12/27] Fix CHANGELOG wording and redundant tests per review feedback Agent-Logs-Url: https://github.com/Azure/azure-sdk-for-java/sessions/7cb64b44-f893-4408-afef-9fc03222fe8d Co-authored-by: rujche <171773178+rujche@users.noreply.github.com> --- sdk/spring/CHANGELOG.md | 3 ++- .../AadResourceServerConfigurationTests.java | 11 ++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/sdk/spring/CHANGELOG.md b/sdk/spring/CHANGELOG.md index 1cefe7cebef8..917a663c33a0 100644 --- a/sdk/spring/CHANGELOG.md +++ b/sdk/spring/CHANGELOG.md @@ -7,7 +7,8 @@ This section includes changes in `spring-cloud-azure-autoconfigure` module. #### Breaking Changes -- AAD resource server now requires `spring.cloud.azure.active-directory.profile.tenant-id` to be a specific tenant ID (GUID) (empty string, `common`, `organizations`, `consumers` are invalid). ([#49033](https://github.com/Azure/azure-sdk-for-java/pull/49033)) +- AAD resource server now requires `spring.cloud.azure.active-directory.profile.tenant-id` to be set to a specific (non-reserved) tenant ID. Empty string, `common`, `organizations`, and `consumers` are no longer accepted and will cause application startup to fail with an `IllegalArgumentException`. ([#49033](https://github.com/Azure/azure-sdk-for-java/pull/49033)) +- `AadJwtIssuerValidator` no-args constructor has been removed. Use `AadJwtIssuerValidator(AadTrustedIssuerRepository)` instead. ([#49033](https://github.com/Azure/azure-sdk-for-java/pull/49033)) #### Bugs Fixed diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java index 1e1ea83134de..fdb40d6ab1f7 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java @@ -69,16 +69,17 @@ void testCreateJwtDecoderByJwkKeySetUri() { @Test void testNotAudienceDefaultValidator() { - resourceServerContextRunner() - .withPropertyValues("spring.cloud.azure.active-directory.enabled=true") + resourceServerRunner() + .withPropertyValues("spring.cloud.azure.active-directory.enabled=true", + "spring.cloud.azure.active-directory.profile.tenant-id=fake-tenant-id") .run(context -> { AadAuthenticationProperties properties = context.getBean(AadAuthenticationProperties.class); AadResourceServerConfiguration bean = context .getBean(AadResourceServerConfiguration.class); List> defaultValidator = bean.createDefaultValidator(properties); assertThat(defaultValidator).isNotNull(); - // AUD (from app-id-uri) + TID + ISS + Timestamp validators - assertThat(defaultValidator).hasSize(4); + // No AUD validator (no app-id-uri or client-id configured) + TID + ISS + Timestamp validators + assertThat(defaultValidator).hasSize(3); }); } @@ -92,7 +93,7 @@ void testExistAudienceDefaultValidator() { .getBean(AadResourceServerConfiguration.class); List> defaultValidator = bean.createDefaultValidator(properties); assertThat(defaultValidator).isNotNull(); - // AUD + ISS + TID + Timestamp validators + // AUD (from app-id-uri) + TID + ISS + Timestamp validators assertThat(defaultValidator).hasSize(4); }); } From a338c2477ca28c04bf5179cacf7d2c947bf5b057 Mon Sep 17 00:00:00 2001 From: Rujun Chen Date: Wed, 6 May 2026 16:56:22 +0800 Subject: [PATCH 13/27] Update changelog --- sdk/spring/CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/spring/CHANGELOG.md b/sdk/spring/CHANGELOG.md index 917a663c33a0..debc9d9411bc 100644 --- a/sdk/spring/CHANGELOG.md +++ b/sdk/spring/CHANGELOG.md @@ -8,7 +8,6 @@ This section includes changes in `spring-cloud-azure-autoconfigure` module. #### Breaking Changes - AAD resource server now requires `spring.cloud.azure.active-directory.profile.tenant-id` to be set to a specific (non-reserved) tenant ID. Empty string, `common`, `organizations`, and `consumers` are no longer accepted and will cause application startup to fail with an `IllegalArgumentException`. ([#49033](https://github.com/Azure/azure-sdk-for-java/pull/49033)) -- `AadJwtIssuerValidator` no-args constructor has been removed. Use `AadJwtIssuerValidator(AadTrustedIssuerRepository)` instead. ([#49033](https://github.com/Azure/azure-sdk-for-java/pull/49033)) #### Bugs Fixed From 940ab78644be9574ab5435936a91dff9767f701d Mon Sep 17 00:00:00 2001 From: Rujun Chen Date: Wed, 6 May 2026 17:07:06 +0800 Subject: [PATCH 14/27] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../aad/configuration/AadResourceServerConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java index 56ddbb47f41f..6c608badd991 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java @@ -107,7 +107,7 @@ private static void validateTenantId(String tenantId) { throw new IllegalArgumentException( "For resource server, 'spring.cloud.azure.active-directory.profile.tenant-id' cannot be null, empty, or set to 'common', 'organizations', or 'consumers'. " + "This configuration would accept tokens from any Azure AD tenant, creating a security vulnerability. " - + "Please configure a specific tenant ID (GUID) to restrict token validation to your organization's tenant only."); + + "Please configure a specific tenant ID to restrict token validation to your organization's tenant only."); } } From aae84ed081bfdd0e9269f7052adbf63c6cc5b8f2 Mon Sep 17 00:00:00 2001 From: Rujun Chen Date: Wed, 6 May 2026 17:08:17 +0800 Subject: [PATCH 15/27] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- sdk/spring/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/spring/CHANGELOG.md b/sdk/spring/CHANGELOG.md index debc9d9411bc..12f907b6e3ae 100644 --- a/sdk/spring/CHANGELOG.md +++ b/sdk/spring/CHANGELOG.md @@ -7,7 +7,7 @@ This section includes changes in `spring-cloud-azure-autoconfigure` module. #### Breaking Changes -- AAD resource server now requires `spring.cloud.azure.active-directory.profile.tenant-id` to be set to a specific (non-reserved) tenant ID. Empty string, `common`, `organizations`, and `consumers` are no longer accepted and will cause application startup to fail with an `IllegalArgumentException`. ([#49033](https://github.com/Azure/azure-sdk-for-java/pull/49033)) +- AAD resource server now requires `spring.cloud.azure.active-directory.profile.tenant-id` to be set to a specific (non-reserved) tenant ID. Empty string, `common`, `organizations`, and `consumers` are no longer accepted and will cause application startup to fail with an `IllegalArgumentException`. In addition, AAD authentication now enables explicit audience validation by default, so applications that previously relied on issuer-only validation may fail authentication unless the expected audience is configured correctly. ([#49033](https://github.com/Azure/azure-sdk-for-java/pull/49033)) #### Bugs Fixed From 5070bfe0eb826dca106962e2d5e2ec4d792fcf4e Mon Sep 17 00:00:00 2001 From: Rujun Chen Date: Wed, 6 May 2026 17:20:27 +0800 Subject: [PATCH 16/27] Update CHANGELOG to document AadAuthenticationFilter audience validation Expand breaking changes section to provide detailed information about the AadAuthenticationFilter's new explicit audience validation behavior: - Documents that JWT aud claim must match client-id or app-id-uri - Explains rejection behavior with BadJWTException for mismatched tokens - Clarifies security benefit: prevents cross-application token reuse - Notes alignment with OAuth2/OIDC security best practices This provides users with complete context for understanding and preparing for this security-focused breaking change. --- sdk/spring/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sdk/spring/CHANGELOG.md b/sdk/spring/CHANGELOG.md index 12f907b6e3ae..43115e10ec39 100644 --- a/sdk/spring/CHANGELOG.md +++ b/sdk/spring/CHANGELOG.md @@ -7,7 +7,8 @@ This section includes changes in `spring-cloud-azure-autoconfigure` module. #### Breaking Changes -- AAD resource server now requires `spring.cloud.azure.active-directory.profile.tenant-id` to be set to a specific (non-reserved) tenant ID. Empty string, `common`, `organizations`, and `consumers` are no longer accepted and will cause application startup to fail with an `IllegalArgumentException`. In addition, AAD authentication now enables explicit audience validation by default, so applications that previously relied on issuer-only validation may fail authentication unless the expected audience is configured correctly. ([#49033](https://github.com/Azure/azure-sdk-for-java/pull/49033)) +- AAD resource server now requires `spring.cloud.azure.active-directory.profile.tenant-id` to be set to a specific (non-reserved) tenant ID. Empty string, `common`, `organizations`, and `consumers` are no longer accepted and will cause application startup to fail with an `IllegalArgumentException`. ([#49033](https://github.com/Azure/azure-sdk-for-java/pull/49033)) +- `AadAuthenticationFilter` now enables explicit audience validation by default. The filter will verify that the JWT's `aud` (audience) claim matches either `spring.cloud.azure.active-directory.credential.client-id` or `spring.cloud.azure.active-directory.app-id-uri`. Tokens issued for other applications will be rejected with `BadJWTException`. This prevents cross-application token reuse and aligns with OAuth2/OIDC security best practices. ([#49033](https://github.com/Azure/azure-sdk-for-java/pull/49033)) #### Bugs Fixed From 8e15962e9d7c3398273ad65ba3cd2ccad86bdc0a Mon Sep 17 00:00:00 2001 From: Rujun Chen Date: Wed, 6 May 2026 17:25:54 +0800 Subject: [PATCH 17/27] Update comment --- .../AadResourceServerConfiguration.java | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java index 6c608badd991..579428d3f92d 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java @@ -64,19 +64,17 @@ JwtDecoder jwtDecoder(AadAuthenticationProperties aadAuthenticationProperties) { List> createDefaultValidator(AadAuthenticationProperties aadAuthenticationProperties) { // Validate JWT claims according to Microsoft's security best practices: - // https://learn.microsoft.com/en-us/entra/identity-platform/claims-validation - // + // https://learn.microsoft.com/en-us/entra/identity-platform/claims-validation // This method creates validators for the following claims in order: - // 1. aud (Audience) - Ensures the token is intended for this application - // 2. tid (Tenant ID) - Ensures the token is from the configured tenant (critical for tenant isolation) - // 3. iss (Issuer) - Ensures the token is issued by the expected Azure AD tenant endpoint - // 4. exp/nbf (Timestamp) - Ensures the token is not expired or used before its valid time - // - // Note: Subject (sub, oid, roles, groups, wids) and Actor (scp) validation are NOT performed here. - // These are application-specific authorization concerns that vary by endpoint and should be handled - // in business logic using Spring Security's @PreAuthorize, @Secured, or custom authorization logic. - // See: https://learn.microsoft.com/en-us/entra/identity-platform/claims-validation#validate-the-subject - + // 1. aud (Audience) - Ensures the token is intended for this application + // 2. tid (Tenant ID) - Ensures the token is from the configured tenant (critical for tenant isolation) + // 3. iss (Issuer) - Ensures the token is issued by the expected Azure AD tenant endpoint + // 4. exp/nbf (Timestamp) - Ensures the token is not expired or used before its valid time + // Note: + // Subject (sub, oid, roles, groups, wids) and Actor (scp) validation are NOT performed here. + // These are application-specific authorization concerns that vary by endpoint and should be handled + // in business logic using Spring Security's @PreAuthorize, @Secured, or custom authorization logic. + // See: https://learn.microsoft.com/en-us/entra/identity-platform/claims-validation#validate-the-subject List> validators = new ArrayList<>(); List validAudiences = new ArrayList<>(); String tenantId = aadAuthenticationProperties.getProfile().getTenantId(); From 81eb2f4563fa21fdd89dd77ffb65a7eae47f2840 Mon Sep 17 00:00:00 2001 From: Rujun Chen Date: Wed, 6 May 2026 18:14:23 +0800 Subject: [PATCH 18/27] Delete comment --- .../AadResourceServerConfiguration.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java index 579428d3f92d..029a83c6cb43 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java @@ -63,18 +63,6 @@ JwtDecoder jwtDecoder(AadAuthenticationProperties aadAuthenticationProperties) { } List> createDefaultValidator(AadAuthenticationProperties aadAuthenticationProperties) { - // Validate JWT claims according to Microsoft's security best practices: - // https://learn.microsoft.com/en-us/entra/identity-platform/claims-validation - // This method creates validators for the following claims in order: - // 1. aud (Audience) - Ensures the token is intended for this application - // 2. tid (Tenant ID) - Ensures the token is from the configured tenant (critical for tenant isolation) - // 3. iss (Issuer) - Ensures the token is issued by the expected Azure AD tenant endpoint - // 4. exp/nbf (Timestamp) - Ensures the token is not expired or used before its valid time - // Note: - // Subject (sub, oid, roles, groups, wids) and Actor (scp) validation are NOT performed here. - // These are application-specific authorization concerns that vary by endpoint and should be handled - // in business logic using Spring Security's @PreAuthorize, @Secured, or custom authorization logic. - // See: https://learn.microsoft.com/en-us/entra/identity-platform/claims-validation#validate-the-subject List> validators = new ArrayList<>(); List validAudiences = new ArrayList<>(); String tenantId = aadAuthenticationProperties.getProfile().getTenantId(); From 0d4a4fa8e1a75c974abc8d34ff0ca6e1d7bb7d05 Mon Sep 17 00:00:00 2001 From: Rujun Chen Date: Wed, 6 May 2026 18:15:31 +0800 Subject: [PATCH 19/27] Delete useless empty lines --- .../aad/configuration/AadResourceServerConfiguration.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java index 029a83c6cb43..a3c66eff400b 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java @@ -67,7 +67,6 @@ List> createDefaultValidator(AadAuthenticationProperti List validAudiences = new ArrayList<>(); String tenantId = aadAuthenticationProperties.getProfile().getTenantId(); validateTenantId(tenantId); - if (StringUtils.hasText(aadAuthenticationProperties.getAppIdUri())) { validAudiences.add(aadAuthenticationProperties.getAppIdUri()); } @@ -77,11 +76,9 @@ List> createDefaultValidator(AadAuthenticationProperti if (!validAudiences.isEmpty()) { validators.add(new JwtClaimValidator>(AadJwtClaimNames.AUD, validAudiences::containsAll)); } - validators.add(new JwtClaimValidator(AadJwtClaimNames.TID, tenantId::equals)); validators.add(new AadJwtIssuerValidator(new AadTrustedIssuerRepository(tenantId))); validators.add(new JwtTimestampValidator()); - return validators; } From 93f7bbbc37d9fe43bf75fb440baabc8733a01be8 Mon Sep 17 00:00:00 2001 From: Rujun Chen Date: Wed, 6 May 2026 18:35:53 +0800 Subject: [PATCH 20/27] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../aad/configuration/AadResourceServerConfiguration.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java index a3c66eff400b..09cca20339d4 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java @@ -74,7 +74,10 @@ List> createDefaultValidator(AadAuthenticationProperti validAudiences.add(aadAuthenticationProperties.getCredential().getClientId()); } if (!validAudiences.isEmpty()) { - validators.add(new JwtClaimValidator>(AadJwtClaimNames.AUD, validAudiences::containsAll)); + validators.add(new JwtClaimValidator>(AadJwtClaimNames.AUD, + audiences -> audiences != null + && !audiences.isEmpty() + && audiences.stream().anyMatch(validAudiences::contains))); } validators.add(new JwtClaimValidator(AadJwtClaimNames.TID, tenantId::equals)); validators.add(new AadJwtIssuerValidator(new AadTrustedIssuerRepository(tenantId))); From cbaa20bd5766e381022be7fe4c1bce3950703f9d Mon Sep 17 00:00:00 2001 From: Rujun Chen Date: Wed, 6 May 2026 18:36:31 +0800 Subject: [PATCH 21/27] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../aad/configuration/AadResourceServerConfiguration.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java index 09cca20339d4..30a2c2701cf5 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java @@ -92,8 +92,8 @@ private static void validateTenantId(String tenantId) { || "consumers".equalsIgnoreCase(tenantId)) { throw new IllegalArgumentException( "For resource server, 'spring.cloud.azure.active-directory.profile.tenant-id' cannot be null, empty, or set to 'common', 'organizations', or 'consumers'. " - + "This configuration would accept tokens from any Azure AD tenant, creating a security vulnerability. " - + "Please configure a specific tenant ID to restrict token validation to your organization's tenant only."); + + "These values are not supported for resource server token validation because a specific tenant ID is required to validate the token 'tid' claim and issuer against a single Azure AD tenant. " + + "Please configure an explicit tenant ID for your organization's tenant."); } } From 583fe2c7f98e4b0b339d49405a80bf2556d4a3c9 Mon Sep 17 00:00:00 2001 From: Rujun Chen Date: Wed, 6 May 2026 18:43:22 +0800 Subject: [PATCH 22/27] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../aad/configuration/AadResourceServerConfiguration.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java index 30a2c2701cf5..fc72f571f54f 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java @@ -87,8 +87,8 @@ List> createDefaultValidator(AadAuthenticationProperti private static void validateTenantId(String tenantId) { if (!StringUtils.hasText(tenantId) - || "common".equalsIgnoreCase(tenantId) - || "organizations".equalsIgnoreCase(tenantId) + || "common".equalsIgnoreCase(tenantId) + || "organizations".equalsIgnoreCase(tenantId) || "consumers".equalsIgnoreCase(tenantId)) { throw new IllegalArgumentException( "For resource server, 'spring.cloud.azure.active-directory.profile.tenant-id' cannot be null, empty, or set to 'common', 'organizations', or 'consumers'. " From 72e7f88c042fe6e500e483d69b43eca8cab7a7b5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 10:49:34 +0000 Subject: [PATCH 23/27] Fix test assertions to match actual validateTenantId error message Agent-Logs-Url: https://github.com/Azure/azure-sdk-for-java/sessions/5f933fe4-f982-43ca-bf21-aa15b7b6e35d Co-authored-by: rujche <171773178+rujche@users.noreply.github.com> --- .../AadResourceServerConfigurationTests.java | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java index fdb40d6ab1f7..ddbe72107631 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java @@ -126,8 +126,7 @@ void testValidateTenantIdRejectsCommon() { assertThat(context).hasFailed(); assertThat(context.getStartupFailure()) .hasRootCauseInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("cannot be null, empty, or set to") - .hasMessageContaining("security vulnerability"); + .hasMessageContaining("cannot be null, empty, or set to"); }); } @@ -141,8 +140,7 @@ void testValidateTenantIdRejectsOrganizations() { assertThat(context).hasFailed(); assertThat(context.getStartupFailure()) .hasRootCauseInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("cannot be null, empty, or set to") - .hasMessageContaining("security vulnerability"); + .hasMessageContaining("cannot be null, empty, or set to"); }); } @@ -156,8 +154,7 @@ void testValidateTenantIdRejectsConsumers() { assertThat(context).hasFailed(); assertThat(context.getStartupFailure()) .hasRootCauseInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("cannot be null, empty, or set to") - .hasMessageContaining("security vulnerability"); + .hasMessageContaining("cannot be null, empty, or set to"); }); } @@ -172,8 +169,7 @@ void testValidateTenantIdRejectsNull() { assertThat(context).hasFailed(); assertThat(context.getStartupFailure()) .hasRootCauseInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("cannot be null, empty, or set to") - .hasMessageContaining("security vulnerability"); + .hasMessageContaining("cannot be null, empty, or set to"); }); } @@ -189,8 +185,7 @@ void testValidateTenantIdRejectsEmptyString() { assertThat(context).hasFailed(); assertThat(context.getStartupFailure()) .hasRootCauseInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("cannot be null, empty, or set to") - .hasMessageContaining("security vulnerability"); + .hasMessageContaining("cannot be null, empty, or set to"); }); } From ec7587ce4b0496e054300b21d5722ee027e4c9f2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 11:14:07 +0000 Subject: [PATCH 24/27] Trim tenant-id before validation and use in validators to prevent whitespace bypass Agent-Logs-Url: https://github.com/Azure/azure-sdk-for-java/sessions/2a42cd2f-33b4-4aec-b260-e3faedadfb31 Co-authored-by: rujche <171773178+rujche@users.noreply.github.com> --- .../aad/configuration/AadResourceServerConfiguration.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java index fc72f571f54f..9e5bed3c817a 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java @@ -66,6 +66,9 @@ List> createDefaultValidator(AadAuthenticationProperti List> validators = new ArrayList<>(); List validAudiences = new ArrayList<>(); String tenantId = aadAuthenticationProperties.getProfile().getTenantId(); + if (tenantId != null) { + tenantId = tenantId.trim(); + } validateTenantId(tenantId); if (StringUtils.hasText(aadAuthenticationProperties.getAppIdUri())) { validAudiences.add(aadAuthenticationProperties.getAppIdUri()); From 1174babd5309cf5f34a5fa7d72e1e8920fd27fdf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 11:14:58 +0000 Subject: [PATCH 25/27] Add test for whitespace-padded reserved tenant-id rejection after trimming Agent-Logs-Url: https://github.com/Azure/azure-sdk-for-java/sessions/2a42cd2f-33b4-4aec-b260-e3faedadfb31 Co-authored-by: rujche <171773178+rujche@users.noreply.github.com> --- .../AadResourceServerConfigurationTests.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java index ddbe72107631..f02ab9edc315 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java @@ -189,6 +189,20 @@ void testValidateTenantIdRejectsEmptyString() { }); } + @Test + void testValidateTenantIdRejectsWhitespacePaddedReservedValue() { + resourceServerRunner() + .withPropertyValues("spring.cloud.azure.active-directory.enabled=true", + "spring.cloud.azure.active-directory.profile.tenant-id= common ", + "spring.cloud.azure.active-directory.app-id-uri=fake-app-id-uri") + .run(context -> { + assertThat(context).hasFailed(); + assertThat(context.getStartupFailure()) + .hasRootCauseInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("cannot be null, empty, or set to"); + }); + } + @Test void testValidateTenantIdAcceptsValidGuid() { resourceServerRunner() From 93f18a64a1d2921d9ce01fa27040b759b1401b6d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 13:51:42 +0000 Subject: [PATCH 26/27] Trim tenant-id in jwtDecoder() before building AadAuthorizationServerEndpoints Agent-Logs-Url: https://github.com/Azure/azure-sdk-for-java/sessions/43c934ea-32ac-4a47-b018-0b58d11fc30a Co-authored-by: rujche <171773178+rujche@users.noreply.github.com> --- .../aad/configuration/AadResourceServerConfiguration.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java index 9e5bed3c817a..6a8219e70e25 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java @@ -51,8 +51,12 @@ class AadResourceServerConfiguration { @Bean @ConditionalOnMissingBean(JwtDecoder.class) JwtDecoder jwtDecoder(AadAuthenticationProperties aadAuthenticationProperties) { + String tenantId = aadAuthenticationProperties.getProfile().getTenantId(); + if (tenantId != null) { + tenantId = tenantId.trim(); + } AadAuthorizationServerEndpoints identityEndpoints = new AadAuthorizationServerEndpoints( - aadAuthenticationProperties.getProfile().getEnvironment().getActiveDirectoryEndpoint(), aadAuthenticationProperties.getProfile().getTenantId()); + aadAuthenticationProperties.getProfile().getEnvironment().getActiveDirectoryEndpoint(), tenantId); NimbusJwtDecoder nimbusJwtDecoder = NimbusJwtDecoder .withJwkSetUri(identityEndpoints.getJwkSetEndpoint()) .restOperations(createRestTemplate(restTemplateBuilder)) From 3c3b72fd682d78071a1caf506c339a5f70a5d04d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 13:52:50 +0000 Subject: [PATCH 27/27] Extract getTrimmedTenantId helper to eliminate duplicate trim logic Agent-Logs-Url: https://github.com/Azure/azure-sdk-for-java/sessions/43c934ea-32ac-4a47-b018-0b58d11fc30a Co-authored-by: rujche <171773178+rujche@users.noreply.github.com> --- .../AadResourceServerConfiguration.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java index 6a8219e70e25..d03d7215f997 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfiguration.java @@ -51,10 +51,7 @@ class AadResourceServerConfiguration { @Bean @ConditionalOnMissingBean(JwtDecoder.class) JwtDecoder jwtDecoder(AadAuthenticationProperties aadAuthenticationProperties) { - String tenantId = aadAuthenticationProperties.getProfile().getTenantId(); - if (tenantId != null) { - tenantId = tenantId.trim(); - } + String tenantId = getTrimmedTenantId(aadAuthenticationProperties); AadAuthorizationServerEndpoints identityEndpoints = new AadAuthorizationServerEndpoints( aadAuthenticationProperties.getProfile().getEnvironment().getActiveDirectoryEndpoint(), tenantId); NimbusJwtDecoder nimbusJwtDecoder = NimbusJwtDecoder @@ -69,10 +66,7 @@ JwtDecoder jwtDecoder(AadAuthenticationProperties aadAuthenticationProperties) { List> createDefaultValidator(AadAuthenticationProperties aadAuthenticationProperties) { List> validators = new ArrayList<>(); List validAudiences = new ArrayList<>(); - String tenantId = aadAuthenticationProperties.getProfile().getTenantId(); - if (tenantId != null) { - tenantId = tenantId.trim(); - } + String tenantId = getTrimmedTenantId(aadAuthenticationProperties); validateTenantId(tenantId); if (StringUtils.hasText(aadAuthenticationProperties.getAppIdUri())) { validAudiences.add(aadAuthenticationProperties.getAppIdUri()); @@ -92,6 +86,11 @@ List> createDefaultValidator(AadAuthenticationProperti return validators; } + private static String getTrimmedTenantId(AadAuthenticationProperties aadAuthenticationProperties) { + String tenantId = aadAuthenticationProperties.getProfile().getTenantId(); + return tenantId != null ? tenantId.trim() : null; + } + private static void validateTenantId(String tenantId) { if (!StringUtils.hasText(tenantId) || "common".equalsIgnoreCase(tenantId)