diff --git a/src/client/Microsoft.Identity.Client/AppConfig/AuthorityInfo.cs b/src/client/Microsoft.Identity.Client/AppConfig/AuthorityInfo.cs index 66b6d69352..3777ef89ff 100644 --- a/src/client/Microsoft.Identity.Client/AppConfig/AuthorityInfo.cs +++ b/src/client/Microsoft.Identity.Client/AppConfig/AuthorityInfo.cs @@ -485,7 +485,7 @@ public static IAuthorityValidator CreateAuthorityValidator(AuthorityInfo authori /// /// Figures out the authority based on the authority from the config and the authority from the request, /// and optionally the homeAccountTenantId, which has an impact on AcquireTokenSilent - /// If the request authority is consumers, organizations, or common, it should just be set an the app level. + /// If the request authority is "common" or "organizations", it should be set at the app level. /// The algorithm is: /// /// 1. If there is no request authority (i.e. no authority override), use the config authority. diff --git a/src/client/Microsoft.Identity.Client/Instance/CiamAuthority.cs b/src/client/Microsoft.Identity.Client/Instance/CiamAuthority.cs index 30f902a6ba..70e08fe65e 100644 --- a/src/client/Microsoft.Identity.Client/Instance/CiamAuthority.cs +++ b/src/client/Microsoft.Identity.Client/Instance/CiamAuthority.cs @@ -2,11 +2,6 @@ // Licensed under the MIT License. using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.Identity.Client.Internal; namespace Microsoft.Identity.Client.Instance @@ -17,23 +12,6 @@ internal CiamAuthority(AuthorityInfo authorityInfo) : base(authorityInfo) { } - internal override string GetTenantedAuthority(string tenantId, bool forceSpecifiedTenant = false) - { - if (!string.IsNullOrEmpty(tenantId) && - (forceSpecifiedTenant || IsCommonOrOrganizationsTenant())) - { - var authorityUri = AuthorityInfo.CanonicalAuthority; - - return string.Format( - CultureInfo.InvariantCulture, - AADCanonicalAuthorityTemplate, - authorityUri.Authority, - tenantId); - } - - return AuthorityInfo.CanonicalAuthority.AbsoluteUri; - } - /// /// Translates CIAM authorities into a usable form. This is needed only until ESTS is updated to support the north star format /// North star format: https://idgciamdemo.ciamlogin.com diff --git a/src/client/Microsoft.Identity.Client/Instance/DstsAuthority.cs b/src/client/Microsoft.Identity.Client/Instance/DstsAuthority.cs index e12b4c5c11..4c0db47ff9 100644 --- a/src/client/Microsoft.Identity.Client/Instance/DstsAuthority.cs +++ b/src/client/Microsoft.Identity.Client/Instance/DstsAuthority.cs @@ -22,6 +22,12 @@ public DstsAuthority(AuthorityInfo authorityInfo) TenantId = AuthorityInfo.GetSecondPathSegment(AuthorityInfo.CanonicalAuthority); } + // DSTS authorities use their own URL template (dstsv2/{tenantId}/), not the AAD template. + // Rewrite the authority when either: + // - the caller forces it via .WithTenantId() (forceSpecifiedTenant=true), or + // - the configured authority is tenantless (e.g. ".../dstsv2/common/" or ".../dstsv2/organizations/"), + // which is the silent-flow path used by Authority.CreateAuthorityWithTenant when deriving a + // tenanted authority from a home account / id token. internal override string GetTenantedAuthority(string tenantId, bool forceSpecifiedTenant = false) { if (!string.IsNullOrEmpty(tenantId) && diff --git a/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/DstsAuthorityTests.cs b/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/DstsAuthorityTests.cs index 265af7c244..31858953ef 100644 --- a/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/DstsAuthorityTests.cs +++ b/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/DstsAuthorityTests.cs @@ -214,5 +214,37 @@ public void TenantlessAuthorityChanges() Assert.AreEqual("common", authority.TenantId); } + + [TestMethod] + public void CreateAuthorityFromTenantlessDsts_NonForced_RewritesToSpecifiedTenant() + { + // Regression test for the case where a DSTS authority is configured with a tenantless + // path segment ("common" or "organizations") and Authority.CreateAuthorityWithTenant + // is invoked with forceSpecifiedTenant:false (the silent-flow / legacy-cache path). + // The authority must be rewritten to use the supplied tenant id, otherwise downstream + // cache keys and token requests would use the literal "common" tenant. + Authority commonAuthority = Authority.CreateAuthority(TestConstants.DstsAuthorityCommon); + Assert.AreEqual("common", commonAuthority.TenantId); + + string rewritten = commonAuthority.GetTenantedAuthority(TestConstants.TenantId, forceSpecifiedTenant: false); + + Assert.AreEqual( + $"https://some.url.dsts.core.azure-test.net/dstsv2/{TestConstants.TenantId}/", + rewritten, + "Tenantless DSTS authorities must be rewritten when GetTenantedAuthority is called with a real tenant id, even when forceSpecifiedTenant=false."); + } + + [TestMethod] + public void CreateAuthorityFromOrganizationsDsts_NonForced_RewritesToSpecifiedTenant() + { + Authority orgsAuthority = Authority.CreateAuthority(TestConstants.DstsAuthorityOrganizations); + Assert.AreEqual("organizations", orgsAuthority.TenantId); + + string rewritten = orgsAuthority.GetTenantedAuthority(TestConstants.TenantId, forceSpecifiedTenant: false); + + Assert.AreEqual( + $"https://some.url.dsts.core.azure-test.net/dstsv2/{TestConstants.TenantId}/", + rewritten); + } } }