From f4ffe6d1923ea202e463deb422110a55b040fb88 Mon Sep 17 00:00:00 2001 From: trwalke Date: Thu, 6 Mar 2025 22:29:12 -0800 Subject: [PATCH 1/3] Adding a flag to TokenCacheNotificationArgs to signal that the client id is an RMA node. --- .../Internal/Constants.cs | 1 + .../PublicApi/net462/PublicAPI.Unshipped.txt | 1 + .../PublicApi/net472/PublicAPI.Unshipped.txt | 1 + .../net8.0-android/PublicAPI.Unshipped.txt | 1 + .../net8.0-ios/PublicAPI.Unshipped.txt | 1 + .../PublicApi/net8.0/PublicAPI.Unshipped.txt | 1 + .../netstandard2.0/PublicAPI.Unshipped.txt | 1 + .../TokenCacheNotificationArgs.cs | 9 ++++ .../TestConstants.cs | 1 + .../CacheTests/TokenCacheNotificationTests.cs | 42 +++++++++++++++++++ 10 files changed, 59 insertions(+) diff --git a/src/client/Microsoft.Identity.Client/Internal/Constants.cs b/src/client/Microsoft.Identity.Client/Internal/Constants.cs index dc7f6ac706..64ebc66e50 100644 --- a/src/client/Microsoft.Identity.Client/Internal/Constants.cs +++ b/src/client/Microsoft.Identity.Client/Internal/Constants.cs @@ -49,6 +49,7 @@ internal static class Constants public const string ManagedIdentityDefaultTenant = "managed_identity"; public const string CiamAuthorityHostSuffix = ".ciamlogin.com"; public const string CertSerialNumber = "cert_sn"; + public const string FmiNodeClientId = "urn:microsoft:identity:fmi"; public const int CallerSdkIdMaxLength = 10; public const int CallerSdkVersionMaxLength = 20; diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt index e69de29bb2..87330fe97b 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +Microsoft.Identity.Client.TokenCacheNotificationArgs.IsFmiClientNode.get -> bool \ No newline at end of file diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt index e69de29bb2..d107378822 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +Microsoft.Identity.Client.TokenCacheNotificationArgs.IsFmiClientNode.get -> bool diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt index e69de29bb2..d107378822 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +Microsoft.Identity.Client.TokenCacheNotificationArgs.IsFmiClientNode.get -> bool diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt index e69de29bb2..d107378822 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +Microsoft.Identity.Client.TokenCacheNotificationArgs.IsFmiClientNode.get -> bool diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt index e69de29bb2..d107378822 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +Microsoft.Identity.Client.TokenCacheNotificationArgs.IsFmiClientNode.get -> bool diff --git a/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt index e69de29bb2..d107378822 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +Microsoft.Identity.Client.TokenCacheNotificationArgs.IsFmiClientNode.get -> bool diff --git a/src/client/Microsoft.Identity.Client/TokenCacheNotificationArgs.cs b/src/client/Microsoft.Identity.Client/TokenCacheNotificationArgs.cs index dc784558b5..bf364a5554 100644 --- a/src/client/Microsoft.Identity.Client/TokenCacheNotificationArgs.cs +++ b/src/client/Microsoft.Identity.Client/TokenCacheNotificationArgs.cs @@ -5,6 +5,7 @@ using System.Collections; using System.Collections.Generic; using System.Threading; +using Microsoft.Identity.Client.Internal; using Microsoft.Identity.Client.TelemetryCore.TelemetryClient; using Microsoft.IdentityModel.Abstractions; @@ -255,5 +256,13 @@ public TokenCacheNotificationArgs( // only use this constructor in product co /// Cache Details contains the details of L1/ L2 cache for telemetry logging. /// public TelemetryData TelemetryData { get; } + + /// + /// Determines if the client application authentication is an FMI node under an RMA. + /// + public bool IsFmiClientNode + { + get { return ClientId.Equals(Constants.FmiNodeClientId); } + } } } diff --git a/tests/Microsoft.Identity.Test.Common/TestConstants.cs b/tests/Microsoft.Identity.Test.Common/TestConstants.cs index 44e3004acd..23d2f41bbc 100644 --- a/tests/Microsoft.Identity.Test.Common/TestConstants.cs +++ b/tests/Microsoft.Identity.Test.Common/TestConstants.cs @@ -216,6 +216,7 @@ public static HashSet s_scope public const string Bearer = "Bearer"; public const string Pop = "PoP"; + public const string FmiNodeClientId = "urn:microsoft:identity:fmi"; public static IDictionary ExtraQueryParameters { diff --git a/tests/Microsoft.Identity.Test.Unit/CacheTests/TokenCacheNotificationTests.cs b/tests/Microsoft.Identity.Test.Unit/CacheTests/TokenCacheNotificationTests.cs index 2e853b361a..1db1572b06 100644 --- a/tests/Microsoft.Identity.Test.Unit/CacheTests/TokenCacheNotificationTests.cs +++ b/tests/Microsoft.Identity.Test.Unit/CacheTests/TokenCacheNotificationTests.cs @@ -533,5 +533,47 @@ public async Task TokenCacheSerializationArgs_UserCache_TenantIdScopes_Async() } } + + [TestMethod] + [DataRow(TestConstants.ClientId)] + [DataRow(TestConstants.FmiNodeClientId)] + public async Task TokenCacheSerializationArgs_AppCache_IsFmiClientNode_Async(string clientId) + { + using (var harness = CreateTestHarness()) + { + //Confirm that IsFmiClientNode is correct + // Arrange + var cca = ConfidentialClientApplicationBuilder + .Create(clientId) + .WithClientSecret(TestConstants.ClientSecret) + .WithHttpManager(harness.HttpManager) + .BuildConcrete(); + + var appTokenCacheRecoder = cca.AppTokenCache.RecordAccess((args) => + { + Assert.AreEqual(clientId, args.ClientId); + if (clientId.Equals(TestConstants.FmiNodeClientId)) + { + Assert.IsTrue(args.IsFmiClientNode); + } + else + { + Assert.IsFalse(args.IsFmiClientNode); + } + + CollectionAssert.AreEquivalent(TestConstants.s_scope.ToArray(), args.RequestScopes.ToArray()); + }); + + harness.HttpManager.AddAllMocks(TokenResponseType.Valid_ClientCredentials); + + // Act - Client Credentials with authority override + await cca.AcquireTokenForClient(TestConstants.s_scope) + .WithTenantId(TestConstants.TenantId2) + .ExecuteAsync() + .ConfigureAwait(false); + + appTokenCacheRecoder.AssertAccessCounts(1, 1); + } + } } } From 6860e2a2236a61092f517eb72947e59c4bd6f72d Mon Sep 17 00:00:00 2001 From: Travis Walker Date: Wed, 12 Mar 2025 14:59:20 -0700 Subject: [PATCH 2/3] Update src/client/Microsoft.Identity.Client/TokenCacheNotificationArgs.cs Co-authored-by: Gladwin Johnson <90415114+gladjohn@users.noreply.github.com> --- .../Microsoft.Identity.Client/TokenCacheNotificationArgs.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/Microsoft.Identity.Client/TokenCacheNotificationArgs.cs b/src/client/Microsoft.Identity.Client/TokenCacheNotificationArgs.cs index bf364a5554..7bd88e304f 100644 --- a/src/client/Microsoft.Identity.Client/TokenCacheNotificationArgs.cs +++ b/src/client/Microsoft.Identity.Client/TokenCacheNotificationArgs.cs @@ -258,7 +258,7 @@ public TokenCacheNotificationArgs( // only use this constructor in product co public TelemetryData TelemetryData { get; } /// - /// Determines if the client application authentication is an FMI node under an RMA. + /// Determines whether the client application authentication instance is classified as an FMI (Federated Managed Identity) node under a specified RMA (Resource Managed Authority). /// public bool IsFmiClientNode { From 3e3a5becd9bc773cf5aa7a49250e95f49d18d661 Mon Sep 17 00:00:00 2001 From: trwalke Date: Wed, 12 Mar 2025 16:08:34 -0700 Subject: [PATCH 3/3] Refactoring to remove bool and add string --- .../PublicApi/net462/PublicAPI.Unshipped.txt | 2 +- .../PublicApi/net472/PublicAPI.Unshipped.txt | 2 +- .../net8.0-android/PublicAPI.Unshipped.txt | 2 +- .../net8.0-ios/PublicAPI.Unshipped.txt | 2 +- .../PublicApi/net8.0/PublicAPI.Unshipped.txt | 2 +- .../netstandard2.0/PublicAPI.Unshipped.txt | 2 +- .../TokenCacheNotificationArgs.cs | 30 ++++++++++++------- .../CacheTests/TokenCacheNotificationTests.cs | 8 +++-- 8 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt index 87330fe97b..541e617e62 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt @@ -1 +1 @@ -Microsoft.Identity.Client.TokenCacheNotificationArgs.IsFmiClientNode.get -> bool \ No newline at end of file +Microsoft.Identity.Client.TokenCacheNotificationArgs.NoDistributedCacheUseReason.get -> string diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt index d107378822..541e617e62 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt @@ -1 +1 @@ -Microsoft.Identity.Client.TokenCacheNotificationArgs.IsFmiClientNode.get -> bool +Microsoft.Identity.Client.TokenCacheNotificationArgs.NoDistributedCacheUseReason.get -> string diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt index d107378822..541e617e62 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt @@ -1 +1 @@ -Microsoft.Identity.Client.TokenCacheNotificationArgs.IsFmiClientNode.get -> bool +Microsoft.Identity.Client.TokenCacheNotificationArgs.NoDistributedCacheUseReason.get -> string diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt index d107378822..541e617e62 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt @@ -1 +1 @@ -Microsoft.Identity.Client.TokenCacheNotificationArgs.IsFmiClientNode.get -> bool +Microsoft.Identity.Client.TokenCacheNotificationArgs.NoDistributedCacheUseReason.get -> string diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt index d107378822..541e617e62 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt @@ -1 +1 @@ -Microsoft.Identity.Client.TokenCacheNotificationArgs.IsFmiClientNode.get -> bool +Microsoft.Identity.Client.TokenCacheNotificationArgs.NoDistributedCacheUseReason.get -> string diff --git a/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt index d107378822..541e617e62 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt @@ -1 +1 @@ -Microsoft.Identity.Client.TokenCacheNotificationArgs.IsFmiClientNode.get -> bool +Microsoft.Identity.Client.TokenCacheNotificationArgs.NoDistributedCacheUseReason.get -> string diff --git a/src/client/Microsoft.Identity.Client/TokenCacheNotificationArgs.cs b/src/client/Microsoft.Identity.Client/TokenCacheNotificationArgs.cs index 7bd88e304f..dc3cf8e611 100644 --- a/src/client/Microsoft.Identity.Client/TokenCacheNotificationArgs.cs +++ b/src/client/Microsoft.Identity.Client/TokenCacheNotificationArgs.cs @@ -41,13 +41,13 @@ public TokenCacheNotificationArgs( hasTokens, suggestedCacheExpiry, cancellationToken, - default, - default, + default, + default, default, null, default) - { - } + { + } /// /// This constructor is for test purposes only. It allows apps to unit test their MSAL token cache implementation code. @@ -62,7 +62,7 @@ public TokenCacheNotificationArgs( bool hasTokens, DateTimeOffset? suggestedCacheExpiry, CancellationToken cancellationToken, - Guid correlationId) + Guid correlationId) : this(tokenCache, clientId, account, @@ -77,7 +77,7 @@ public TokenCacheNotificationArgs( default, null, default) - { + { } /// @@ -93,10 +93,10 @@ public TokenCacheNotificationArgs( // only use this constructor in product co bool hasTokens, DateTimeOffset? suggestedCacheExpiry, CancellationToken cancellationToken, - Guid correlationId, + Guid correlationId, IEnumerable requestScopes, string requestTenantId) - + { TokenCache = tokenCache; ClientId = clientId; @@ -146,7 +146,7 @@ public TokenCacheNotificationArgs( // only use this constructor in product co SuggestedCacheExpiry = suggestedCacheExpiry; IdentityLogger = identityLogger; PiiLoggingEnabled = piiLoggingEnabled; - TelemetryData = telemetryData?? new TelemetryData(); + TelemetryData = telemetryData ?? new TelemetryData(); } /// @@ -260,9 +260,17 @@ public TokenCacheNotificationArgs( // only use this constructor in product co /// /// Determines whether the client application authentication instance is classified as an FMI (Federated Managed Identity) node under a specified RMA (Resource Managed Authority). /// - public bool IsFmiClientNode + public string NoDistributedCacheUseReason { - get { return ClientId.Equals(Constants.FmiNodeClientId); } + get + { + if (ClientId.Equals(Constants.FmiNodeClientId)) + { + return "The currently provided client id indicates that this is a RMA (Resource Managed Authority) node client. RMA node clients should not use a distributed cache, please use an in memory cache instead."; + } + + return string.Empty; + } } } } diff --git a/tests/Microsoft.Identity.Test.Unit/CacheTests/TokenCacheNotificationTests.cs b/tests/Microsoft.Identity.Test.Unit/CacheTests/TokenCacheNotificationTests.cs index 1db1572b06..d727becc63 100644 --- a/tests/Microsoft.Identity.Test.Unit/CacheTests/TokenCacheNotificationTests.cs +++ b/tests/Microsoft.Identity.Test.Unit/CacheTests/TokenCacheNotificationTests.cs @@ -541,7 +541,7 @@ public async Task TokenCacheSerializationArgs_AppCache_IsFmiClientNode_Async(str { using (var harness = CreateTestHarness()) { - //Confirm that IsFmiClientNode is correct + // Confirm that NoDistributedCacheUseReason is correct // Arrange var cca = ConfidentialClientApplicationBuilder .Create(clientId) @@ -554,11 +554,13 @@ public async Task TokenCacheSerializationArgs_AppCache_IsFmiClientNode_Async(str Assert.AreEqual(clientId, args.ClientId); if (clientId.Equals(TestConstants.FmiNodeClientId)) { - Assert.IsTrue(args.IsFmiClientNode); + // string should not be null or empty + Assert.IsTrue(!string.IsNullOrEmpty(args.NoDistributedCacheUseReason)); } else { - Assert.IsFalse(args.IsFmiClientNode); + // string should be null or empty + Assert.IsTrue(string.IsNullOrEmpty(args.NoDistributedCacheUseReason)); } CollectionAssert.AreEquivalent(TestConstants.s_scope.ToArray(), args.RequestScopes.ToArray());