From 7aa8f04404d68ae9e4ae057be009785bff232e8c Mon Sep 17 00:00:00 2001 From: Robbie Ginsburg Date: Tue, 28 Apr 2026 12:55:21 -0400 Subject: [PATCH 1/6] Fix: Honor MSA tenant GUID when specified via WithTenantId at request level When a ConfidentialClientApplication is configured with a specific tenant at the app level, calling .WithTenantId('9188040d-...') (the MSA/consumer tenant GUID) at the request level was silently ignored. Root cause: IsCommonOrganizationsOrConsumersTenant() equates both the 'consumers' string alias and the MSA GUID as tenantless, causing the request-level override to be discarded. Fix: Add IsConsumersGuid() helper to AadAuthority that returns true only for the MSA GUID (not the 'consumers' alias). Use it in the request authority resolution condition in CreateAuthorityForRequestAsync so the GUID is honored as a real tenant ID at request level. The 'consumers' string alias continues to be ignored at request level per the existing known limitation (#2929). Fixes #5951 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../MtlsPopParametersInitializer.cs | 2 +- .../AppConfig/AuthorityInfo.cs | 5 +- .../Instance/AadAuthority.cs | 16 ++++++ .../ApiConfigTests/AuthorityTests.cs | 55 +++++++++++++++++++ .../PublicApiTests/MtlsPopTests.cs | 42 ++++++++++++++ 5 files changed, 118 insertions(+), 2 deletions(-) diff --git a/src/client/Microsoft.Identity.Client/ApiConfig/Parameters/MtlsPopParametersInitializer.cs b/src/client/Microsoft.Identity.Client/ApiConfig/Parameters/MtlsPopParametersInitializer.cs index 2c9a962f6b..5a4747a53b 100644 --- a/src/client/Microsoft.Identity.Client/ApiConfig/Parameters/MtlsPopParametersInitializer.cs +++ b/src/client/Microsoft.Identity.Client/ApiConfig/Parameters/MtlsPopParametersInitializer.cs @@ -140,7 +140,7 @@ private static void InitMtlsPopParameters( if (serviceBundle.Config.Authority.AuthorityInfo.AuthorityType == AuthorityType.Aad) { string tenant = AuthorityInfo.GetFirstPathSegment(serviceBundle.Config.Authority.AuthorityInfo.CanonicalAuthority); - if (AadAuthority.IsCommonOrganizationsOrConsumersTenant(tenant)) + if (AadAuthority.IsCommonOrganizationsOrConsumersTenant(tenant) && !AadAuthority.IsConsumersGuid(tenant)) { throw new MsalClientException( MsalError.MissingTenantedAuthority, diff --git a/src/client/Microsoft.Identity.Client/AppConfig/AuthorityInfo.cs b/src/client/Microsoft.Identity.Client/AppConfig/AuthorityInfo.cs index 57e65cdabf..c977867b1c 100644 --- a/src/client/Microsoft.Identity.Client/AppConfig/AuthorityInfo.cs +++ b/src/client/Microsoft.Identity.Client/AppConfig/AuthorityInfo.cs @@ -499,8 +499,10 @@ public static IAuthorityValidator CreateAuthorityValidator(AuthorityInfo authori /// - if the authority is not defined at the application level and the request level is not AAD, use the request authority /// - if the authority is defined at app level, and the request level authority is of different type, throw an exception /// - /// - if the intended authority is consumers, please define it at the app level and not at the request level. + /// - if the intended authority is the "consumers" alias, please define it at the app level and not at the request level. /// known issue: https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/2929 + /// - if the intended authority is the MSA tenant GUID (9188040d-6c67-4c5b-b112-36a304b66dad), it IS honored + /// at the request level because it is a specific tenant ID rather than a tenantless alias. /// public static async Task CreateAuthorityForRequestAsync(RequestContext requestContext, AuthorityInfo requestAuthorityInfo, @@ -566,6 +568,7 @@ public static async Task CreateAuthorityForRequestAsync(RequestContex new AadAuthority(CreateAuthorityWithEnvironment(requestAuthorityInfo, account?.Environment).AuthorityInfo) : new AadAuthority(requestAuthorityInfo); if (!requestAuthority.IsCommonOrganizationsOrConsumersTenant() || + requestAuthority.IsConsumersGuid() || requestAuthority.IsOrganizationsTenantWithMsaPassthroughEnabled(requestContext.ServiceBundle.Config.IsBrokerEnabled && requestContext.ServiceBundle.Config.BrokerOptions != null && requestContext.ServiceBundle.Config.BrokerOptions.MsaPassthrough, account?.HomeAccountId?.TenantId)) { return requestAuthority; diff --git a/src/client/Microsoft.Identity.Client/Instance/AadAuthority.cs b/src/client/Microsoft.Identity.Client/Instance/AadAuthority.cs index 5d2c37e4b0..ffa8b09a5f 100644 --- a/src/client/Microsoft.Identity.Client/Instance/AadAuthority.cs +++ b/src/client/Microsoft.Identity.Client/Instance/AadAuthority.cs @@ -62,6 +62,22 @@ internal static bool IsCommonOrganizationsOrConsumersTenant(string tenantId) (IsCommonOrOrganizationsTenant(tenantId) || IsConsumers(tenantId)); } + /// + /// Returns true only when the tenant is the MSA GUID (9188040d-6c67-4c5b-b112-36a304b66dad), + /// as distinct from the "consumers" string alias. The GUID is a real tenant ID and should be + /// honored when specified explicitly (e.g. via WithTenantId at request level). + /// + internal bool IsConsumersGuid() + { + return IsConsumersGuid(TenantId); + } + + internal static bool IsConsumersGuid(string tenantId) + { + return !string.IsNullOrEmpty(tenantId) && + tenantId.Equals(Constants.MsaTenantId, StringComparison.OrdinalIgnoreCase); + } + internal bool IsOrganizationsTenantWithMsaPassthroughEnabled(bool isMsaPassthrough, string accountTenantId) { return accountTenantId!= null && isMsaPassthrough && TenantId.Equals(Constants.Organizations, StringComparison.OrdinalIgnoreCase) && diff --git a/tests/Microsoft.Identity.Test.Unit/ApiConfigTests/AuthorityTests.cs b/tests/Microsoft.Identity.Test.Unit/ApiConfigTests/AuthorityTests.cs index 217e409ab1..b257b7de0f 100644 --- a/tests/Microsoft.Identity.Test.Unit/ApiConfigTests/AuthorityTests.cs +++ b/tests/Microsoft.Identity.Test.Unit/ApiConfigTests/AuthorityTests.cs @@ -27,6 +27,11 @@ public class AuthorityTests : TestBase private static readonly Authority s_b2cAuthority = Authority.CreateAuthority(TestConstants.B2CAuthority, true); private static readonly Authority s_commonNetAuthority = Authority.CreateAuthority(TestConstants.PrefCacheAuthorityCommonTenant, true); + private static readonly Authority s_consumersTenantAuthority = + Authority.CreateAuthority(TestConstants.AuthorityConsumersTenant, true); + private static readonly Authority s_consumerTidAuthority = + Authority.CreateAuthority(TestConstants.AuthorityConsumerTidTenant, true); + private MockHttpAndServiceBundle _harness; private RequestContext _testRequestContext; @@ -453,6 +458,56 @@ public void VerifyConfigAuthorityType(string authorityHost, Type authorityTypeIn Assert.AreEqual(app.AuthorityInfo.AuthorityType.ToString(), authorityType); } + /// + /// Regression test for https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/5951 + /// When WithTenantId is called with the MSA GUID at request level, it should be honored regardless + /// of the app-level authority, because the GUID is a real tenant ID (not a tenantless alias). + /// The "consumers" string alias is a different case and is still ignored at request level. + /// + [TestMethod] + public void WithTenantId_ConsumerGuid_IsHonoredAtRequestLevel() + { + // Case 1 (bug regression): app=specific tenant, request=MSA GUID → MSA GUID wins + VerifyAuthority( + configAuthority: s_utidAuthority, + requestAuthority: s_consumerTidAuthority, + account: null, + expectedTenantId: TestConstants.MsaTenantId, + _testRequestContext); + + // Case 2: app=common, request=MSA GUID → MSA GUID wins + VerifyAuthority( + configAuthority: s_commonAuthority, + requestAuthority: s_consumerTidAuthority, + account: null, + expectedTenantId: TestConstants.MsaTenantId, + _testRequestContext); + + // Case 3: app=specific tenant, request="consumers" string alias → app tenant wins (existing behavior preserved) + VerifyAuthority( + configAuthority: s_utidAuthority, + requestAuthority: s_consumersTenantAuthority, + account: null, + expectedTenantId: TestConstants.Utid, + _testRequestContext); + + // Case 4: app=common, request="consumers" string alias → "common" used (existing behavior preserved) + VerifyAuthority( + configAuthority: s_commonAuthority, + requestAuthority: s_consumersTenantAuthority, + account: null, + expectedTenantId: "common", + _testRequestContext); + + // Case 5: app=MSA GUID, no request override → MSA GUID used (no regression) + VerifyAuthority( + configAuthority: s_consumerTidAuthority, + requestAuthority: null, + account: null, + expectedTenantId: TestConstants.MsaTenantId, + _testRequestContext); + } + private static void VerifyAuthority( Authority configAuthority, Authority requestAuthority, diff --git a/tests/Microsoft.Identity.Test.Unit/PublicApiTests/MtlsPopTests.cs b/tests/Microsoft.Identity.Test.Unit/PublicApiTests/MtlsPopTests.cs index 6e473eebdf..2698ec4cb5 100644 --- a/tests/Microsoft.Identity.Test.Unit/PublicApiTests/MtlsPopTests.cs +++ b/tests/Microsoft.Identity.Test.Unit/PublicApiTests/MtlsPopTests.cs @@ -591,6 +591,48 @@ await app.AcquireTokenForClient(TestConstants.s_scope) } } + [TestMethod] + public async Task MtlsPop_WithMsaTenantGuidAuthority_DoesNotThrowMissingTenantedAuthorityAsync() + { + // Regression test: the MSA tenant GUID (9188040d-...) is a real tenant ID, not a tenantless alias. + // Configuring an app with it at the app level and using mTLS PoP should NOT throw MissingTenantedAuthority. + // See https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/5951 + const string region = "eastus"; + + using (var envContext = new EnvVariableContext()) + { + Environment.SetEnvironmentVariable("REGION_NAME", region); + + using (var httpManager = new MockHttpManager()) + { + var app = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId) + .WithCertificate(s_testCertificate) + .WithAuthority(TestConstants.AuthorityConsumerTidTenant) + .WithAzureRegion(ConfidentialClientApplication.AttemptRegionDiscovery) + .WithHttpManager(httpManager) + .BuildConcrete(); + + // Should NOT throw MissingTenantedAuthority — the MSA GUID is a tenanted authority. + // Any other exception (e.g. MsalServiceException from the network call) is acceptable. + try + { + await app.AcquireTokenForClient(TestConstants.s_scope) + .WithMtlsProofOfPossession() + .ExecuteAsync() + .ConfigureAwait(false); + } + catch (MsalClientException ex) when (ex.ErrorCode == MsalError.MissingTenantedAuthority) + { + Assert.Fail("MSA tenant GUID should not be rejected as a non-tenanted authority"); + } + catch (Exception) + { + // Any other exception means we passed the MissingTenantedAuthority guard — test passes. + } + } + } + } + [TestMethod] public async Task MtlsPop_ValidateExpectedUrlAsync() { From d7bd7e151ca255161223ae9a8f1710656132e260 Mon Sep 17 00:00:00 2001 From: Robbie Ginsburg Date: Tue, 28 Apr 2026 15:35:19 -0400 Subject: [PATCH 2/6] Fix: extend MSA GUID handling to GetTenantedAuthority and OBO cache filtering GetTenantedAuthority (AadAuthority): the MSA tenant GUID is a real tenant ID and should not be replaced by a caller-supplied tenant when forceSpecifiedTenant=false. Previously IsCommonOrganizationsOrConsumersTenant() returned true for the GUID, causing it to be replaced like 'common' or 'consumers'. Added && !IsConsumersGuid() guard to preserve the GUID unless replacement is explicitly forced. FilterTokensByHomeAccountTenantOrAssertion (OBO cache): the same root cause meant OBO cache lookups with the MSA GUID as authority set filterByTenantId=false, logging a spurious 'Please use tenanted authority' warning and skipping tenant filtering. Fixed by treating the MSA GUID as a tenanted authority in the filter expression. Test: GetTenantedAuthority_MsaGuid_IsNotReplaced in AadAuthorityTests covers both forceSpecifiedTenant=false (preserved) and =true (replaced) cases. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Instance/AadAuthority.cs | 2 +- .../TokenCache.ITokenCacheInternal.cs | 4 +++- .../InstanceTests/AadAuthorityTests.cs | 19 +++++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/client/Microsoft.Identity.Client/Instance/AadAuthority.cs b/src/client/Microsoft.Identity.Client/Instance/AadAuthority.cs index ffa8b09a5f..7a84397c51 100644 --- a/src/client/Microsoft.Identity.Client/Instance/AadAuthority.cs +++ b/src/client/Microsoft.Identity.Client/Instance/AadAuthority.cs @@ -99,7 +99,7 @@ internal static bool IsCommonOrOrganizationsTenant(string tenantId) internal override string GetTenantedAuthority(string tenantId, bool forceSpecifiedTenant = false) { if (!string.IsNullOrEmpty(tenantId) && - (forceSpecifiedTenant || IsCommonOrganizationsOrConsumersTenant())) + (forceSpecifiedTenant || (IsCommonOrganizationsOrConsumersTenant() && !IsConsumersGuid()))) { var authorityUri = AuthorityInfo.CanonicalAuthority; diff --git a/src/client/Microsoft.Identity.Client/TokenCache.ITokenCacheInternal.cs b/src/client/Microsoft.Identity.Client/TokenCache.ITokenCacheInternal.cs index f0fefb153f..a08ee26913 100644 --- a/src/client/Microsoft.Identity.Client/TokenCache.ITokenCacheInternal.cs +++ b/src/client/Microsoft.Identity.Client/TokenCache.ITokenCacheInternal.cs @@ -578,9 +578,11 @@ private static void FilterTokensByHomeAccountTenantOrAssertion( // OBO calls FindAccessTokenAsync directly, but we are not able to resolve the authority // unless the developer has configured a tenanted authority. If they have configured /common // then we cannot filter by tenant and will use whatever is in the cache. + // The MSA GUID (9188040d-...) is a real tenant ID and should be filtered normally. filterByTenantId = !string.IsNullOrEmpty(requestTenantId) && - !AadAuthority.IsCommonOrganizationsOrConsumersTenant(requestTenantId); + (!AadAuthority.IsCommonOrganizationsOrConsumersTenant(requestTenantId) || + AadAuthority.IsConsumersGuid(requestTenantId)); } if (filterByTenantId) diff --git a/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/AadAuthorityTests.cs b/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/AadAuthorityTests.cs index bba573706d..8b7186044d 100644 --- a/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/AadAuthorityTests.cs +++ b/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/AadAuthorityTests.cs @@ -246,6 +246,25 @@ public void CreateAuthorityFromTenantedWithTenantTest() Assert.AreEqual("https://login.microsoft.com/other_tenant_id/", updatedAuthority2, "Changed with forced flag"); } + [TestMethod] + public void GetTenantedAuthority_MsaGuid_IsNotReplaced() + { + // Regression test for https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/5951 + // The MSA GUID is a real tenant ID — GetTenantedAuthority should NOT replace it + // unless forceSpecifiedTenant=true. + Authority authority = Authority.CreateAuthority(TestConstants.AuthorityConsumerTidTenant); + + // Without force: MSA GUID is preserved (it's a real tenant) + string updatedAuthority = authority.GetTenantedAuthority("other_tenant_id", false); + Assert.AreEqual(TestConstants.AuthorityConsumerTidTenant, updatedAuthority, + "MSA GUID authority should not be replaced when forceSpecifiedTenant=false"); + + // With force: replacement is honored + string updatedAuthority2 = authority.GetTenantedAuthority("other_tenant_id", true); + StringAssert.Contains(updatedAuthority2, "other_tenant_id", + "MSA GUID authority should be replaced when forceSpecifiedTenant=true"); + } + [TestMethod] public void CreateAuthorityFromCommonWithTenantTest() { From 7a49064e29e4554cf8e122b2854dea57e3bd3432 Mon Sep 17 00:00:00 2001 From: Robbie Ginsburg Date: Fri, 1 May 2026 18:52:57 -0400 Subject: [PATCH 3/6] Fix IsCommonOrganizationsOrConsumersTenant to exclude MSA GUID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MSA tenant GUID (9188040d-6c67-4c5b-b112-36a304b66dad) is a real, concrete tenant ID — not a tenantless alias like 'consumers', 'common', or 'organizations'. The previous fix added IsConsumersGuid() guards in four places; this commit fixes the predicate itself instead. Changes: - AadAuthority.IsCommonOrganizationsOrConsumersTenant: replace IsConsumers(tenantId) with a plain 'consumers' string equality check, so only the alias string is tenantless — not the GUID - Remove IsConsumersGuid() instance and static helpers (no longer needed) - Remove the four IsConsumersGuid() guards that compensated for the incorrect predicate behavior - Add predicate unit test asserting MSA GUID returns false - Add Bearer token mocked HTTP test (issue #5951) asserting the token request targets the MSA tenant endpoint, not 'common' Fixes https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/5951 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../MtlsPopParametersInitializer.cs | 2 +- .../AppConfig/AuthorityInfo.cs | 1 - .../Instance/AadAuthority.cs | 22 ++-------- .../TokenCache.ITokenCacheInternal.cs | 4 +- .../InstanceTests/AadAuthorityTests.cs | 14 +++++++ .../ConfidentialClientApplicationTests.cs | 42 +++++++++++++++++++ 6 files changed, 62 insertions(+), 23 deletions(-) diff --git a/src/client/Microsoft.Identity.Client/ApiConfig/Parameters/MtlsPopParametersInitializer.cs b/src/client/Microsoft.Identity.Client/ApiConfig/Parameters/MtlsPopParametersInitializer.cs index 318e9c5334..372b338e02 100644 --- a/src/client/Microsoft.Identity.Client/ApiConfig/Parameters/MtlsPopParametersInitializer.cs +++ b/src/client/Microsoft.Identity.Client/ApiConfig/Parameters/MtlsPopParametersInitializer.cs @@ -155,7 +155,7 @@ private static void InitMtlsPopParameters( if (serviceBundle.Config.Authority.AuthorityInfo.AuthorityType == AuthorityType.Aad) { string tenant = AuthorityInfo.GetFirstPathSegment(serviceBundle.Config.Authority.AuthorityInfo.CanonicalAuthority); - if (AadAuthority.IsCommonOrganizationsOrConsumersTenant(tenant) && !AadAuthority.IsConsumersGuid(tenant)) + if (AadAuthority.IsCommonOrganizationsOrConsumersTenant(tenant)) { throw new MsalClientException( MsalError.MissingTenantedAuthority, diff --git a/src/client/Microsoft.Identity.Client/AppConfig/AuthorityInfo.cs b/src/client/Microsoft.Identity.Client/AppConfig/AuthorityInfo.cs index c977867b1c..12ec621db9 100644 --- a/src/client/Microsoft.Identity.Client/AppConfig/AuthorityInfo.cs +++ b/src/client/Microsoft.Identity.Client/AppConfig/AuthorityInfo.cs @@ -568,7 +568,6 @@ public static async Task CreateAuthorityForRequestAsync(RequestContex new AadAuthority(CreateAuthorityWithEnvironment(requestAuthorityInfo, account?.Environment).AuthorityInfo) : new AadAuthority(requestAuthorityInfo); if (!requestAuthority.IsCommonOrganizationsOrConsumersTenant() || - requestAuthority.IsConsumersGuid() || requestAuthority.IsOrganizationsTenantWithMsaPassthroughEnabled(requestContext.ServiceBundle.Config.IsBrokerEnabled && requestContext.ServiceBundle.Config.BrokerOptions != null && requestContext.ServiceBundle.Config.BrokerOptions.MsaPassthrough, account?.HomeAccountId?.TenantId)) { return requestAuthority; diff --git a/src/client/Microsoft.Identity.Client/Instance/AadAuthority.cs b/src/client/Microsoft.Identity.Client/Instance/AadAuthority.cs index 7a84397c51..502cd5a561 100644 --- a/src/client/Microsoft.Identity.Client/Instance/AadAuthority.cs +++ b/src/client/Microsoft.Identity.Client/Instance/AadAuthority.cs @@ -58,24 +58,10 @@ internal bool IsCommonOrganizationsOrConsumersTenant() internal static bool IsCommonOrganizationsOrConsumersTenant(string tenantId) { + // The MSA GUID (9188040d-...) is a real tenant — only the "consumers" string alias is tenantless. return !string.IsNullOrEmpty(tenantId) && - (IsCommonOrOrganizationsTenant(tenantId) || IsConsumers(tenantId)); - } - - /// - /// Returns true only when the tenant is the MSA GUID (9188040d-6c67-4c5b-b112-36a304b66dad), - /// as distinct from the "consumers" string alias. The GUID is a real tenant ID and should be - /// honored when specified explicitly (e.g. via WithTenantId at request level). - /// - internal bool IsConsumersGuid() - { - return IsConsumersGuid(TenantId); - } - - internal static bool IsConsumersGuid(string tenantId) - { - return !string.IsNullOrEmpty(tenantId) && - tenantId.Equals(Constants.MsaTenantId, StringComparison.OrdinalIgnoreCase); + (IsCommonOrOrganizationsTenant(tenantId) || + tenantId.Equals(Constants.Consumers, StringComparison.OrdinalIgnoreCase)); } internal bool IsOrganizationsTenantWithMsaPassthroughEnabled(bool isMsaPassthrough, string accountTenantId) @@ -99,7 +85,7 @@ internal static bool IsCommonOrOrganizationsTenant(string tenantId) internal override string GetTenantedAuthority(string tenantId, bool forceSpecifiedTenant = false) { if (!string.IsNullOrEmpty(tenantId) && - (forceSpecifiedTenant || (IsCommonOrganizationsOrConsumersTenant() && !IsConsumersGuid()))) + (forceSpecifiedTenant || IsCommonOrganizationsOrConsumersTenant())) { var authorityUri = AuthorityInfo.CanonicalAuthority; diff --git a/src/client/Microsoft.Identity.Client/TokenCache.ITokenCacheInternal.cs b/src/client/Microsoft.Identity.Client/TokenCache.ITokenCacheInternal.cs index 0681addc4b..c3ae093365 100644 --- a/src/client/Microsoft.Identity.Client/TokenCache.ITokenCacheInternal.cs +++ b/src/client/Microsoft.Identity.Client/TokenCache.ITokenCacheInternal.cs @@ -595,11 +595,9 @@ private static void FilterTokensByHomeAccountTenantOrAssertion( // OBO calls FindAccessTokenAsync directly, but we are not able to resolve the authority // unless the developer has configured a tenanted authority. If they have configured /common // then we cannot filter by tenant and will use whatever is in the cache. - // The MSA GUID (9188040d-...) is a real tenant ID and should be filtered normally. filterByTenantId = !string.IsNullOrEmpty(requestTenantId) && - (!AadAuthority.IsCommonOrganizationsOrConsumersTenant(requestTenantId) || - AadAuthority.IsConsumersGuid(requestTenantId)); + !AadAuthority.IsCommonOrganizationsOrConsumersTenant(requestTenantId); } if (filterByTenantId) diff --git a/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/AadAuthorityTests.cs b/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/AadAuthorityTests.cs index 8b7186044d..86269ecc14 100644 --- a/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/AadAuthorityTests.cs +++ b/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/AadAuthorityTests.cs @@ -380,6 +380,20 @@ public void IsCommonOrOrganizationsTenantTest() Assert.IsFalse(aadAuthorityInstance.IsCommonOrOrganizationsTenant()); } + [TestMethod] + public void IsCommonOrganizationsOrConsumersTenant_MsaGuid_ReturnsFalse() + { + // Regression test for https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/5951 + // The MSA GUID is a real tenant, not a tenantless alias — it must NOT match the tenantless predicate. + Assert.IsTrue(AadAuthority.IsCommonOrganizationsOrConsumersTenant("common")); + Assert.IsTrue(AadAuthority.IsCommonOrganizationsOrConsumersTenant("organizations")); + Assert.IsTrue(AadAuthority.IsCommonOrganizationsOrConsumersTenant("consumers")); + Assert.IsFalse(AadAuthority.IsCommonOrganizationsOrConsumersTenant(TestConstants.MsaTenantId), + "MSA GUID should not be treated as a tenantless authority"); + Assert.IsFalse(AadAuthority.IsCommonOrganizationsOrConsumersTenant("some-real-tenant-guid"), + "Any real tenant GUID should not be treated as a tenantless authority"); + } + [TestMethod] public async Task CreateAuthorityForRequestAsync_MSAPassthroughAsync() { diff --git a/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ConfidentialClientApplicationTests.cs b/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ConfidentialClientApplicationTests.cs index f48f1dcf63..0c56de1f78 100644 --- a/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ConfidentialClientApplicationTests.cs +++ b/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ConfidentialClientApplicationTests.cs @@ -195,6 +195,48 @@ public async Task ClientCreds_MustFilterByTenantId_Async() } } + [TestMethod] + [TestCategory(TestCategories.Regression)] + [WorkItem(5951)] // https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/5951 + public async Task ClientCreds_WithTenantId_MsaGuid_UsesCorrectTokenEndpoint_Async() + { + // Regression test: the MSA tenant GUID (9188040d-...) is a real tenant. + // When .WithTenantId(msaGuid) is called on a common-authority app, the token + // request must target the MSA tenant endpoint — not silently fall back to common. + string expectedTokenEndpoint = + $"https://login.microsoftonline.com/{TestConstants.MsaTenantId}/oauth2/v2.0/token"; + + using (var httpManager = new MockHttpManager()) + { + httpManager.AddInstanceDiscoveryMockHandler(); + + var tokenHandler = new MockHttpMessageHandler + { + ExpectedUrl = expectedTokenEndpoint, + ExpectedMethod = HttpMethod.Post, + ResponseMessage = MockHelpers.CreateSuccessfulClientCredentialTokenResponseMessage() + }; + httpManager.AddMockHandler(tokenHandler); + + ConfidentialClientApplication app = + ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId) + .WithClientSecret(TestConstants.ClientSecret) + .WithAuthority(TestConstants.AuthorityCommonTenant) + .WithHttpManager(httpManager) + .BuildConcrete(); + + var result = await app.AcquireTokenForClient(TestConstants.s_scope.ToArray()) + .WithTenantId(TestConstants.MsaTenantId) + .ExecuteAsync(CancellationToken.None).ConfigureAwait(false); + + Assert.IsNotNull(result.AccessToken); + // The cache entry should be keyed on the MSA tenant, not "common" + Assert.AreEqual(TestConstants.MsaTenantId, + app.AppTokenCacheInternal.Accessor.GetAllAccessTokens().Single().TenantId, + StringComparer.OrdinalIgnoreCase); + } + } + [TestMethod] public async Task ClientCreds_UsesDefaultPartitionedCacheCorrectly_Async() { From 3d75bf10f1c87c334d66621d3b5209ad68cce17b Mon Sep 17 00:00:00 2001 From: Robbie Ginsburg Date: Thu, 7 May 2026 12:58:43 -0400 Subject: [PATCH 4/6] test: address Neha's review feedback on PR 5958 - ConfidentialClientApplicationTests: change ClientCreds_WithTenantId_MsaGuid test to configure app with MSA GUID authority and override with a different tenant at request level, validating that the override fires (not the app-level MSA GUID) - AuthorityTests: refactor WithTenantId_ConsumerGuid_IsHonoredAtRequestLevel from inline cases to [DataRow] with descriptive DisplayNames for easier reading in the test runner Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../ApiConfigTests/AuthorityTests.cs | 57 +++++++------------ .../ConfidentialClientApplicationTests.cs | 15 ++--- 2 files changed, 29 insertions(+), 43 deletions(-) diff --git a/tests/Microsoft.Identity.Test.Unit/ApiConfigTests/AuthorityTests.cs b/tests/Microsoft.Identity.Test.Unit/ApiConfigTests/AuthorityTests.cs index b257b7de0f..879c11942a 100644 --- a/tests/Microsoft.Identity.Test.Unit/ApiConfigTests/AuthorityTests.cs +++ b/tests/Microsoft.Identity.Test.Unit/ApiConfigTests/AuthorityTests.cs @@ -465,46 +465,31 @@ public void VerifyConfigAuthorityType(string authorityHost, Type authorityTypeIn /// The "consumers" string alias is a different case and is still ignored at request level. /// [TestMethod] - public void WithTenantId_ConsumerGuid_IsHonoredAtRequestLevel() + [DataRow(TestConstants.AuthorityUtidTenant, TestConstants.AuthorityConsumerTidTenant, TestConstants.MsaTenantId, + DisplayName = "AppSpecificTenant_RequestMsaGuid_MsaGuidWins")] + [DataRow(TestConstants.AuthorityCommonTenant, TestConstants.AuthorityConsumerTidTenant, TestConstants.MsaTenantId, + DisplayName = "AppCommon_RequestMsaGuid_MsaGuidWins")] + [DataRow(TestConstants.AuthorityUtidTenant, TestConstants.AuthorityConsumersTenant, TestConstants.Utid, + DisplayName = "AppSpecificTenant_RequestConsumersAlias_AppTenantWins")] + [DataRow(TestConstants.AuthorityCommonTenant, TestConstants.AuthorityConsumersTenant, "common", + DisplayName = "AppCommon_RequestConsumersAlias_CommonUsed")] + [DataRow(TestConstants.AuthorityConsumerTidTenant, null, TestConstants.MsaTenantId, + DisplayName = "AppMsaGuid_NoRequestOverride_MsaGuidUsed")] + public void WithTenantId_ConsumerGuid_IsHonoredAtRequestLevel( + string configAuthorityUrl, + string requestAuthorityUrl, + string expectedTenantId) { - // Case 1 (bug regression): app=specific tenant, request=MSA GUID → MSA GUID wins - VerifyAuthority( - configAuthority: s_utidAuthority, - requestAuthority: s_consumerTidAuthority, - account: null, - expectedTenantId: TestConstants.MsaTenantId, - _testRequestContext); - - // Case 2: app=common, request=MSA GUID → MSA GUID wins - VerifyAuthority( - configAuthority: s_commonAuthority, - requestAuthority: s_consumerTidAuthority, - account: null, - expectedTenantId: TestConstants.MsaTenantId, - _testRequestContext); + var configAuthority = Authority.CreateAuthority(configAuthorityUrl, true); + Authority requestAuthority = requestAuthorityUrl == null + ? null + : Authority.CreateAuthority(requestAuthorityUrl, true); - // Case 3: app=specific tenant, request="consumers" string alias → app tenant wins (existing behavior preserved) VerifyAuthority( - configAuthority: s_utidAuthority, - requestAuthority: s_consumersTenantAuthority, - account: null, - expectedTenantId: TestConstants.Utid, - _testRequestContext); - - // Case 4: app=common, request="consumers" string alias → "common" used (existing behavior preserved) - VerifyAuthority( - configAuthority: s_commonAuthority, - requestAuthority: s_consumersTenantAuthority, - account: null, - expectedTenantId: "common", - _testRequestContext); - - // Case 5: app=MSA GUID, no request override → MSA GUID used (no regression) - VerifyAuthority( - configAuthority: s_consumerTidAuthority, - requestAuthority: null, + configAuthority: configAuthority, + requestAuthority: requestAuthority, account: null, - expectedTenantId: TestConstants.MsaTenantId, + expectedTenantId: expectedTenantId, _testRequestContext); } diff --git a/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ConfidentialClientApplicationTests.cs b/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ConfidentialClientApplicationTests.cs index 0c56de1f78..310636aa73 100644 --- a/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ConfidentialClientApplicationTests.cs +++ b/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ConfidentialClientApplicationTests.cs @@ -201,10 +201,11 @@ public async Task ClientCreds_MustFilterByTenantId_Async() public async Task ClientCreds_WithTenantId_MsaGuid_UsesCorrectTokenEndpoint_Async() { // Regression test: the MSA tenant GUID (9188040d-...) is a real tenant. - // When .WithTenantId(msaGuid) is called on a common-authority app, the token - // request must target the MSA tenant endpoint — not silently fall back to common. + // App is configured with the MSA GUID authority at app level. A request-level + // .WithTenantId() override with a different tenant must be honored — the token + // request must target the override tenant endpoint, not silently use the MSA GUID. string expectedTokenEndpoint = - $"https://login.microsoftonline.com/{TestConstants.MsaTenantId}/oauth2/v2.0/token"; + $"https://login.microsoftonline.com/{TestConstants.Utid}/oauth2/v2.0/token"; using (var httpManager = new MockHttpManager()) { @@ -221,17 +222,17 @@ public async Task ClientCreds_WithTenantId_MsaGuid_UsesCorrectTokenEndpoint_Asyn ConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId) .WithClientSecret(TestConstants.ClientSecret) - .WithAuthority(TestConstants.AuthorityCommonTenant) + .WithAuthority(TestConstants.AuthorityConsumerTidTenant) .WithHttpManager(httpManager) .BuildConcrete(); var result = await app.AcquireTokenForClient(TestConstants.s_scope.ToArray()) - .WithTenantId(TestConstants.MsaTenantId) + .WithTenantId(TestConstants.Utid) .ExecuteAsync(CancellationToken.None).ConfigureAwait(false); Assert.IsNotNull(result.AccessToken); - // The cache entry should be keyed on the MSA tenant, not "common" - Assert.AreEqual(TestConstants.MsaTenantId, + // The cache entry should be keyed on the override tenant, not the MSA GUID + Assert.AreEqual(TestConstants.Utid, app.AppTokenCacheInternal.Accessor.GetAllAccessTokens().Single().TenantId, StringComparer.OrdinalIgnoreCase); } From 1db0f59dcd7cf34c27057645013c56e35ef1fcf0 Mon Sep 17 00:00:00 2001 From: Robbie Ginsburg Date: Mon, 11 May 2026 13:57:35 -0400 Subject: [PATCH 5/6] refactor: remove IsCommonOrganizationsOrConsumersTenant, apply full request-level tenant relaxation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Delete the misleading IsCommonOrganizationsOrConsumersTenant helper (both instance and static overloads) — it conflated truly tenantless aliases (common/organizations) with real tenant identifiers (consumers/MSA GUID). - Remove 'consumers' from s_tenantlessTenantNames to match the semantic model: only 'common' and 'organizations' are tenantless. - Replace all 6 call sites with IsCommonOrOrganizationsTenant: AadAuthority.GetTenantedAuthority, AuthorityInfo.CreateAuthorityForRequestAsync, CiamAuthority.GetTenantedAuthority, DstsAuthority.GetTenantedAuthority, MtlsPopParametersInitializer, TokenCache.FilterTokensByHomeAccountTenantOrAssertion. - Full relaxation: any request-level tenant that is not 'common' or 'organizations' (including 'consumers' string and the MSA GUID) is now honored at request level. - Update AuthorityTests DataRows: consumers at request level now wins instead of being ignored (AppSpecificTenant_RequestConsumersAlias_ConsumersWins, AppCommon_RequestConsumersAlias_ConsumersWins). - Delete IsCommonOrganizationsOrConsumersTenant_MsaGuid_ReturnsFalse test (method no longer exists). - Delete MtlsPop_WithMsaTenantGuidAuthority_DoesNotThrowMissingTenantedAuthority test per Bogdan's review — covered by the bearer token regression test. Fixes #5951 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../MtlsPopParametersInitializer.cs | 2 +- .../AppConfig/AuthorityInfo.cs | 14 +++---- .../Instance/AadAuthority.cs | 16 +------ .../Instance/CiamAuthority.cs | 2 +- .../Instance/DstsAuthority.cs | 2 +- .../TokenCache.ITokenCacheInternal.cs | 2 +- .../ApiConfigTests/AuthorityTests.cs | 14 +++---- .../InstanceTests/AadAuthorityTests.cs | 14 ------- .../PublicApiTests/MtlsPopTests.cs | 42 ------------------- 9 files changed, 18 insertions(+), 90 deletions(-) diff --git a/src/client/Microsoft.Identity.Client/ApiConfig/Parameters/MtlsPopParametersInitializer.cs b/src/client/Microsoft.Identity.Client/ApiConfig/Parameters/MtlsPopParametersInitializer.cs index 372b338e02..b283833523 100644 --- a/src/client/Microsoft.Identity.Client/ApiConfig/Parameters/MtlsPopParametersInitializer.cs +++ b/src/client/Microsoft.Identity.Client/ApiConfig/Parameters/MtlsPopParametersInitializer.cs @@ -155,7 +155,7 @@ private static void InitMtlsPopParameters( if (serviceBundle.Config.Authority.AuthorityInfo.AuthorityType == AuthorityType.Aad) { string tenant = AuthorityInfo.GetFirstPathSegment(serviceBundle.Config.Authority.AuthorityInfo.CanonicalAuthority); - if (AadAuthority.IsCommonOrganizationsOrConsumersTenant(tenant)) + if (AadAuthority.IsCommonOrOrganizationsTenant(tenant)) { throw new MsalClientException( MsalError.MissingTenantedAuthority, diff --git a/src/client/Microsoft.Identity.Client/AppConfig/AuthorityInfo.cs b/src/client/Microsoft.Identity.Client/AppConfig/AuthorityInfo.cs index 12ec621db9..66b6d69352 100644 --- a/src/client/Microsoft.Identity.Client/AppConfig/AuthorityInfo.cs +++ b/src/client/Microsoft.Identity.Client/AppConfig/AuthorityInfo.cs @@ -491,18 +491,16 @@ public static IAuthorityValidator CreateAuthorityValidator(AuthorityInfo authori /// 1. If there is no request authority (i.e. no authority override), use the config authority. /// 1.1. For AAD, if the config authority is "common" etc, try to use the tenanted version with the home account tenant ID /// 2. If there is a request authority, try to use it. - /// 2.1. If the request authority is not "common", then use it - /// 2.2 If the request authority is "common", ignore it, and use 1.1 + /// 2.1. If the request authority is not "common" or "organizations", use it (full relaxation) + /// 2.2 If the request authority is "common" or "organizations", ignore it, and use 1.1 /// /// Special cases: /// /// - if the authority is not defined at the application level and the request level is not AAD, use the request authority /// - if the authority is defined at app level, and the request level authority is of different type, throw an exception - /// - /// - if the intended authority is the "consumers" alias, please define it at the app level and not at the request level. - /// known issue: https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/2929 - /// - if the intended authority is the MSA tenant GUID (9188040d-6c67-4c5b-b112-36a304b66dad), it IS honored - /// at the request level because it is a specific tenant ID rather than a tenantless alias. + /// + /// Note: "consumers" and the MSA tenant GUID (9188040d-6c67-4c5b-b112-36a304b66dad) are both real tenant + /// identifiers — they are honored when specified at the request level, unlike "common" and "organizations". /// public static async Task CreateAuthorityForRequestAsync(RequestContext requestContext, AuthorityInfo requestAuthorityInfo, @@ -567,7 +565,7 @@ public static async Task CreateAuthorityForRequestAsync(RequestContex var requestAuthority = updateEnvironment ? new AadAuthority(CreateAuthorityWithEnvironment(requestAuthorityInfo, account?.Environment).AuthorityInfo) : new AadAuthority(requestAuthorityInfo); - if (!requestAuthority.IsCommonOrganizationsOrConsumersTenant() || + if (!requestAuthority.IsCommonOrOrganizationsTenant() || requestAuthority.IsOrganizationsTenantWithMsaPassthroughEnabled(requestContext.ServiceBundle.Config.IsBrokerEnabled && requestContext.ServiceBundle.Config.BrokerOptions != null && requestContext.ServiceBundle.Config.BrokerOptions.MsaPassthrough, account?.HomeAccountId?.TenantId)) { return requestAuthority; diff --git a/src/client/Microsoft.Identity.Client/Instance/AadAuthority.cs b/src/client/Microsoft.Identity.Client/Instance/AadAuthority.cs index 502cd5a561..046400b577 100644 --- a/src/client/Microsoft.Identity.Client/Instance/AadAuthority.cs +++ b/src/client/Microsoft.Identity.Client/Instance/AadAuthority.cs @@ -23,7 +23,6 @@ internal class AadAuthority : Authority { Constants.Common, Constants.Organizations, - Constants.Consumers, }, StringComparer.OrdinalIgnoreCase); @@ -51,19 +50,6 @@ internal static bool IsConsumers(string tenantId) tenantId.Equals(Constants.MsaTenantId, StringComparison.OrdinalIgnoreCase); } - internal bool IsCommonOrganizationsOrConsumersTenant() - { - return IsCommonOrganizationsOrConsumersTenant(TenantId); - } - - internal static bool IsCommonOrganizationsOrConsumersTenant(string tenantId) - { - // The MSA GUID (9188040d-...) is a real tenant — only the "consumers" string alias is tenantless. - return !string.IsNullOrEmpty(tenantId) && - (IsCommonOrOrganizationsTenant(tenantId) || - tenantId.Equals(Constants.Consumers, StringComparison.OrdinalIgnoreCase)); - } - internal bool IsOrganizationsTenantWithMsaPassthroughEnabled(bool isMsaPassthrough, string accountTenantId) { return accountTenantId!= null && isMsaPassthrough && TenantId.Equals(Constants.Organizations, StringComparison.OrdinalIgnoreCase) && @@ -85,7 +71,7 @@ internal static bool IsCommonOrOrganizationsTenant(string tenantId) internal override string GetTenantedAuthority(string tenantId, bool forceSpecifiedTenant = false) { if (!string.IsNullOrEmpty(tenantId) && - (forceSpecifiedTenant || IsCommonOrganizationsOrConsumersTenant())) + (forceSpecifiedTenant || IsCommonOrOrganizationsTenant())) { var authorityUri = AuthorityInfo.CanonicalAuthority; diff --git a/src/client/Microsoft.Identity.Client/Instance/CiamAuthority.cs b/src/client/Microsoft.Identity.Client/Instance/CiamAuthority.cs index 95537e3e02..30f902a6ba 100644 --- a/src/client/Microsoft.Identity.Client/Instance/CiamAuthority.cs +++ b/src/client/Microsoft.Identity.Client/Instance/CiamAuthority.cs @@ -20,7 +20,7 @@ internal CiamAuthority(AuthorityInfo authorityInfo) : internal override string GetTenantedAuthority(string tenantId, bool forceSpecifiedTenant = false) { if (!string.IsNullOrEmpty(tenantId) && - (forceSpecifiedTenant || IsCommonOrganizationsOrConsumersTenant())) + (forceSpecifiedTenant || IsCommonOrOrganizationsTenant())) { var authorityUri = AuthorityInfo.CanonicalAuthority; diff --git a/src/client/Microsoft.Identity.Client/Instance/DstsAuthority.cs b/src/client/Microsoft.Identity.Client/Instance/DstsAuthority.cs index 67f12a4cfd..e12b4c5c11 100644 --- a/src/client/Microsoft.Identity.Client/Instance/DstsAuthority.cs +++ b/src/client/Microsoft.Identity.Client/Instance/DstsAuthority.cs @@ -25,7 +25,7 @@ public DstsAuthority(AuthorityInfo authorityInfo) internal override string GetTenantedAuthority(string tenantId, bool forceSpecifiedTenant = false) { if (!string.IsNullOrEmpty(tenantId) && - (forceSpecifiedTenant || AadAuthority.IsCommonOrganizationsOrConsumersTenant(TenantId))) + (forceSpecifiedTenant || AadAuthority.IsCommonOrOrganizationsTenant(TenantId))) { var authorityUri = AuthorityInfo.CanonicalAuthority; diff --git a/src/client/Microsoft.Identity.Client/TokenCache.ITokenCacheInternal.cs b/src/client/Microsoft.Identity.Client/TokenCache.ITokenCacheInternal.cs index c3ae093365..7e9a6faee7 100644 --- a/src/client/Microsoft.Identity.Client/TokenCache.ITokenCacheInternal.cs +++ b/src/client/Microsoft.Identity.Client/TokenCache.ITokenCacheInternal.cs @@ -597,7 +597,7 @@ private static void FilterTokensByHomeAccountTenantOrAssertion( // then we cannot filter by tenant and will use whatever is in the cache. filterByTenantId = !string.IsNullOrEmpty(requestTenantId) && - !AadAuthority.IsCommonOrganizationsOrConsumersTenant(requestTenantId); + !AadAuthority.IsCommonOrOrganizationsTenant(requestTenantId); } if (filterByTenantId) diff --git a/tests/Microsoft.Identity.Test.Unit/ApiConfigTests/AuthorityTests.cs b/tests/Microsoft.Identity.Test.Unit/ApiConfigTests/AuthorityTests.cs index eef288ac29..e4b7074485 100644 --- a/tests/Microsoft.Identity.Test.Unit/ApiConfigTests/AuthorityTests.cs +++ b/tests/Microsoft.Identity.Test.Unit/ApiConfigTests/AuthorityTests.cs @@ -460,19 +460,19 @@ public void VerifyConfigAuthorityType(string authorityHost, Type authorityTypeIn /// /// Regression test for https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/5951 - /// When WithTenantId is called with the MSA GUID at request level, it should be honored regardless - /// of the app-level authority, because the GUID is a real tenant ID (not a tenantless alias). - /// The "consumers" string alias is a different case and is still ignored at request level. + /// When WithTenantId is called with a real tenant (MSA GUID or "consumers" alias) at request level, + /// it should be honored regardless of the app-level authority. Only "common" and "organizations" + /// are truly tenantless and are ignored at request level. /// [TestMethod] [DataRow(TestConstants.AuthorityUtidTenant, TestConstants.AuthorityConsumerTidTenant, TestConstants.MsaTenantId, DisplayName = "AppSpecificTenant_RequestMsaGuid_MsaGuidWins")] [DataRow(TestConstants.AuthorityCommonTenant, TestConstants.AuthorityConsumerTidTenant, TestConstants.MsaTenantId, DisplayName = "AppCommon_RequestMsaGuid_MsaGuidWins")] - [DataRow(TestConstants.AuthorityUtidTenant, TestConstants.AuthorityConsumersTenant, TestConstants.Utid, - DisplayName = "AppSpecificTenant_RequestConsumersAlias_AppTenantWins")] - [DataRow(TestConstants.AuthorityCommonTenant, TestConstants.AuthorityConsumersTenant, "common", - DisplayName = "AppCommon_RequestConsumersAlias_CommonUsed")] + [DataRow(TestConstants.AuthorityUtidTenant, TestConstants.AuthorityConsumersTenant, TestConstants.Consumers, + DisplayName = "AppSpecificTenant_RequestConsumersAlias_ConsumersWins")] + [DataRow(TestConstants.AuthorityCommonTenant, TestConstants.AuthorityConsumersTenant, TestConstants.Consumers, + DisplayName = "AppCommon_RequestConsumersAlias_ConsumersWins")] [DataRow(TestConstants.AuthorityConsumerTidTenant, null, TestConstants.MsaTenantId, DisplayName = "AppMsaGuid_NoRequestOverride_MsaGuidUsed")] public void WithTenantId_ConsumerGuid_IsHonoredAtRequestLevel( diff --git a/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/AadAuthorityTests.cs b/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/AadAuthorityTests.cs index 86269ecc14..8b7186044d 100644 --- a/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/AadAuthorityTests.cs +++ b/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/AadAuthorityTests.cs @@ -380,20 +380,6 @@ public void IsCommonOrOrganizationsTenantTest() Assert.IsFalse(aadAuthorityInstance.IsCommonOrOrganizationsTenant()); } - [TestMethod] - public void IsCommonOrganizationsOrConsumersTenant_MsaGuid_ReturnsFalse() - { - // Regression test for https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/5951 - // The MSA GUID is a real tenant, not a tenantless alias — it must NOT match the tenantless predicate. - Assert.IsTrue(AadAuthority.IsCommonOrganizationsOrConsumersTenant("common")); - Assert.IsTrue(AadAuthority.IsCommonOrganizationsOrConsumersTenant("organizations")); - Assert.IsTrue(AadAuthority.IsCommonOrganizationsOrConsumersTenant("consumers")); - Assert.IsFalse(AadAuthority.IsCommonOrganizationsOrConsumersTenant(TestConstants.MsaTenantId), - "MSA GUID should not be treated as a tenantless authority"); - Assert.IsFalse(AadAuthority.IsCommonOrganizationsOrConsumersTenant("some-real-tenant-guid"), - "Any real tenant GUID should not be treated as a tenantless authority"); - } - [TestMethod] public async Task CreateAuthorityForRequestAsync_MSAPassthroughAsync() { diff --git a/tests/Microsoft.Identity.Test.Unit/PublicApiTests/MtlsPopTests.cs b/tests/Microsoft.Identity.Test.Unit/PublicApiTests/MtlsPopTests.cs index 0aa131ea70..94a5984265 100644 --- a/tests/Microsoft.Identity.Test.Unit/PublicApiTests/MtlsPopTests.cs +++ b/tests/Microsoft.Identity.Test.Unit/PublicApiTests/MtlsPopTests.cs @@ -601,48 +601,6 @@ await app.AcquireTokenForClient(TestConstants.s_scope) } } - [TestMethod] - public async Task MtlsPop_WithMsaTenantGuidAuthority_DoesNotThrowMissingTenantedAuthorityAsync() - { - // Regression test: the MSA tenant GUID (9188040d-...) is a real tenant ID, not a tenantless alias. - // Configuring an app with it at the app level and using mTLS PoP should NOT throw MissingTenantedAuthority. - // See https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/5951 - const string region = "eastus"; - - using (var envContext = new EnvVariableContext()) - { - Environment.SetEnvironmentVariable("REGION_NAME", region); - - using (var httpManager = new MockHttpManager()) - { - var app = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId) - .WithCertificate(s_testCertificate) - .WithAuthority(TestConstants.AuthorityConsumerTidTenant) - .WithAzureRegion(ConfidentialClientApplication.AttemptRegionDiscovery) - .WithHttpManager(httpManager) - .BuildConcrete(); - - // Should NOT throw MissingTenantedAuthority — the MSA GUID is a tenanted authority. - // Any other exception (e.g. MsalServiceException from the network call) is acceptable. - try - { - await app.AcquireTokenForClient(TestConstants.s_scope) - .WithMtlsProofOfPossession() - .ExecuteAsync() - .ConfigureAwait(false); - } - catch (MsalClientException ex) when (ex.ErrorCode == MsalError.MissingTenantedAuthority) - { - Assert.Fail("MSA tenant GUID should not be rejected as a non-tenanted authority"); - } - catch (Exception) - { - // Any other exception means we passed the MissingTenantedAuthority guard — test passes. - } - } - } - } - [TestMethod] public async Task MtlsPop_ValidateExpectedUrlAsync() { From 022e7f7126c4abb5cb8f174ed57379029e028c1a Mon Sep 17 00:00:00 2001 From: Robbie Ginsburg Date: Mon, 11 May 2026 15:16:50 -0400 Subject: [PATCH 6/6] Fix test assertion: expect consumers/ not common in URL GetAuthorizationRequestUrl_WithConsumerInCreate_ReturnsConsumers was asserting StartsWith(Constants.Common) but the test name says 'ReturnsConsumers'. The PR fix correctly routes consumers authority, so update the assertion to match the intended behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../PublicApiTests/ConfidentialClientApplicationTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ConfidentialClientApplicationTests.cs b/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ConfidentialClientApplicationTests.cs index 310636aa73..3e297bbab8 100644 --- a/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ConfidentialClientApplicationTests.cs +++ b/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ConfidentialClientApplicationTests.cs @@ -1092,7 +1092,7 @@ public void GetAuthorizationRequestUrl_WithConsumerInCreate_ReturnsConsumers() .GetResult(); #pragma warning restore CS0618 // Type or member is obsolete - Assert.StartsWith(Constants.Common, authorizationRequestUrl.Segments[1]); + Assert.StartsWith(Constants.Consumers, authorizationRequestUrl.Segments[1]); } }