From ad213046fc8d455e8300a0c913c44efa3471ad63 Mon Sep 17 00:00:00 2001 From: trwalke Date: Tue, 18 Feb 2025 23:13:22 -0800 Subject: [PATCH 1/5] Adding FMI Integration tests --- .../HeadlessTests/FmiIntegrationTests.cs | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/FmiIntegrationTests.cs diff --git a/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/FmiIntegrationTests.cs b/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/FmiIntegrationTests.cs new file mode 100644 index 0000000000..8e38bd6503 --- /dev/null +++ b/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/FmiIntegrationTests.cs @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Identity.Client; +using Microsoft.Identity.Client.NativeInterop; +using Microsoft.Identity.Test.Integration.Infrastructure; +using Microsoft.Identity.Test.LabInfrastructure; +using Microsoft.Identity.Test.Unit; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.Identity.Test.Common.Core.Helpers; +using Microsoft.IdentityModel.JsonWebTokens; +using Microsoft.IdentityModel.Tokens; +using Microsoft.Identity.Client.Utils; + +namespace Microsoft.Identity.Test.Integration.NetCore.HeadlessTests +{ + [TestClass] + public class FmiIntegrationTests + { + private const string _fmiAppUrn = "urn:microsoft:identity:fmi"; + private const string _fmiClientId = "4df2cbbb-8612-49c1-87c8-f334d6d065ad"; + private const string _fmiTenantId = "f645ad92-e38d-4d1a-b510-d1b09a74a8ca"; + private const string _fmiAuthority = "https://login.microsoftonline.com/" + _fmiTenantId; + private const string _fmiScope1 = "api://AzureFMITokenExchange/.default"; + private const string _fmiScope2 = "022907d3-0f1b-48f7-badc-1ba6abab6d66/.default"; + private const string _fmiScope3 = "api://AzureAADTokenExchange/.default"; + private const string _fmiPath = "SomeFmiPath/fmi"; + + [TestMethod] + [DataRow(_fmiClientId, _fmiAuthority, _fmiScope1, false)] + [DataRow(_fmiClientId, _fmiAuthority, _fmiScope2, false)] + [DataRow(_fmiClientId, _fmiAuthority, _fmiScope1, true)] + //[DataRow(_fmiAppUrn, _fmiAuthority, _fmiScope3, true)] + public async Task FmiIntegrationTestAsync(string clientId, string authority, string scope, bool useAssertion) + { + await RunHappyPath(clientId, authority, scope, useAssertion).ConfigureAwait(false); + } + + private async Task RunHappyPath(string clientId, string authority, string scope, bool useAssertion) + { + X509Certificate2 cert = CertificateHelper.FindCertificateByName(TestConstants.AutomationTestCertName); + + ConfidentialClientApplication confidentialApp = null; + var builder = ConfidentialClientApplicationBuilder + .Create(clientId) + .WithAuthority(authority, true) + .WithExtraQueryParameters("dc=ESTS-PUB-SCUS-LZ1-FD000-TEST1") + .WithExperimentalFeatures(true); + + if (useAssertion) + { + builder.WithClientAssertion(options => GetSignedClientAssertion(cert, options.TokenEndpoint, options.ClientID)); + } + else + { + builder.WithCertificate(cert, sendX5C: true); + } + + confidentialApp = builder.BuildConcrete(); + + var appCacheRecorder = confidentialApp.AppTokenCache.RecordAccess(); + + var authResult = await confidentialApp.AcquireTokenForClient(new[] { scope }) + .WithFmiPath(_fmiPath) + .ExecuteAsync() + .ConfigureAwait(false); + + MsalAssert.AssertAuthResult(authResult); + appCacheRecorder.AssertAccessCounts(1, 1); + Assert.AreEqual(TokenSource.IdentityProvider, authResult.AuthenticationResultMetadata.TokenSource); + Assert.IsTrue(appCacheRecorder.LastAfterAccessNotificationArgs.IsApplicationCache); + Assert.IsTrue(appCacheRecorder.LastAfterAccessNotificationArgs.HasTokens); + CollectionAssert.AreEquivalent(new[] { scope }, appCacheRecorder.LastBeforeAccessNotificationArgs.RequestScopes.ToArray()); + CollectionAssert.AreEquivalent(new[] { scope }, appCacheRecorder.LastAfterAccessNotificationArgs.RequestScopes.ToArray()); + Assert.AreEqual(_fmiTenantId, appCacheRecorder.LastBeforeAccessNotificationArgs.RequestTenantId ?? ""); + Assert.AreEqual(_fmiTenantId, appCacheRecorder.LastAfterAccessNotificationArgs.RequestTenantId ?? ""); + Assert.IsTrue(authResult.AuthenticationResultMetadata.DurationTotalInMs > 0); + Assert.IsTrue(authResult.AuthenticationResultMetadata.DurationInHttpInMs > 0); + } + + private Task GetSignedClientAssertion(X509Certificate2 certificate, string tokenEndpoint, string clientId) + { + // no need to add exp, nbf as JsonWebTokenHandler will add them by default. + var claims = new Dictionary() + { + { "aud", tokenEndpoint }, + { "iss", clientId }, + { "jti", Guid.NewGuid().ToString() }, + { "sub", clientId } + }; + + var securityTokenDescriptor = new SecurityTokenDescriptor + { + Claims = claims, + SigningCredentials = new X509SigningCredentials(certificate) + }; + + var handler = new JsonWebTokenHandler(); + return Task.FromResult(handler.CreateToken(securityTokenDescriptor)); + } + } +} From ffd9e093d4d0ad8a44bcd5f3176e45ef0799c674 Mon Sep 17 00:00:00 2001 From: trwalke Date: Wed, 19 Feb 2025 14:50:36 -0800 Subject: [PATCH 2/5] Clean up --- .../HeadlessTests/FmiIntegrationTests.cs | 88 +++++++++++-------- 1 file changed, 50 insertions(+), 38 deletions(-) diff --git a/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/FmiIntegrationTests.cs b/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/FmiIntegrationTests.cs index 8e38bd6503..69be80c6d0 100644 --- a/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/FmiIntegrationTests.cs +++ b/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/FmiIntegrationTests.cs @@ -29,40 +29,37 @@ public class FmiIntegrationTests private const string _fmiAuthority = "https://login.microsoftonline.com/" + _fmiTenantId; private const string _fmiScope1 = "api://AzureFMITokenExchange/.default"; private const string _fmiScope2 = "022907d3-0f1b-48f7-badc-1ba6abab6d66/.default"; - private const string _fmiScope3 = "api://AzureAADTokenExchange/.default"; + private const string _fmiScope3 = "api://AzureADTokenExchange/.default"; private const string _fmiPath = "SomeFmiPath/fmi"; [TestMethod] - [DataRow(_fmiClientId, _fmiAuthority, _fmiScope1, false)] - [DataRow(_fmiClientId, _fmiAuthority, _fmiScope2, false)] - [DataRow(_fmiClientId, _fmiAuthority, _fmiScope1, true)] - //[DataRow(_fmiAppUrn, _fmiAuthority, _fmiScope3, true)] - public async Task FmiIntegrationTestAsync(string clientId, string authority, string scope, bool useAssertion) + public async Task RmaFmiCredFlow1Async() { - await RunHappyPath(clientId, authority, scope, useAssertion).ConfigureAwait(false); + await RunRmaFlow(_fmiClientId, _fmiAuthority, _fmiScope1).ConfigureAwait(false); } - private async Task RunHappyPath(string clientId, string authority, string scope, bool useAssertion) + public async Task RmaFmiCredFlow2Async() + { + await RunRmaFlow(_fmiClientId, _fmiAuthority, _fmiScope2).ConfigureAwait(false); + } + + public async Task FmiCredFlow2Async() + { + var fmiCredential = await RunRmaFlow(_fmiClientId, _fmiAuthority, _fmiScope2).ConfigureAwait(false); + await RunFicFlow(fmiCredential).ConfigureAwait(false); + } + + private async Task RunRmaFlow(string clientId, string authority, string scope) { X509Certificate2 cert = CertificateHelper.FindCertificateByName(TestConstants.AutomationTestCertName); - ConfidentialClientApplication confidentialApp = null; - var builder = ConfidentialClientApplicationBuilder + var confidentialApp = ConfidentialClientApplicationBuilder .Create(clientId) .WithAuthority(authority, true) .WithExtraQueryParameters("dc=ESTS-PUB-SCUS-LZ1-FD000-TEST1") - .WithExperimentalFeatures(true); - - if (useAssertion) - { - builder.WithClientAssertion(options => GetSignedClientAssertion(cert, options.TokenEndpoint, options.ClientID)); - } - else - { - builder.WithCertificate(cert, sendX5C: true); - } - - confidentialApp = builder.BuildConcrete(); + .WithExperimentalFeatures(true) + .WithCertificate(cert, sendX5C: true) + .BuildConcrete(); var appCacheRecorder = confidentialApp.AppTokenCache.RecordAccess(); @@ -82,27 +79,42 @@ private async Task RunHappyPath(string clientId, string authority, string scope, Assert.AreEqual(_fmiTenantId, appCacheRecorder.LastAfterAccessNotificationArgs.RequestTenantId ?? ""); Assert.IsTrue(authResult.AuthenticationResultMetadata.DurationTotalInMs > 0); Assert.IsTrue(authResult.AuthenticationResultMetadata.DurationInHttpInMs > 0); + + return authResult.AccessToken; } - private Task GetSignedClientAssertion(X509Certificate2 certificate, string tokenEndpoint, string clientId) + private async Task RunFicFlow(string fmiCredential) { - // no need to add exp, nbf as JsonWebTokenHandler will add them by default. - var claims = new Dictionary() - { - { "aud", tokenEndpoint }, - { "iss", clientId }, - { "jti", Guid.NewGuid().ToString() }, - { "sub", clientId } - }; + var confidentialApp = ConfidentialClientApplicationBuilder + .Create(_fmiAppUrn) + .WithAuthority(_fmiAuthority, true) + .WithExtraQueryParameters("dc=ESTS-PUB-SCUS-LZ1-FD000-TEST1") + .WithExperimentalFeatures(true) + .WithClientAssertion((options) => + { + Assert.AreEqual(_fmiAppUrn, options.ClientID); + return Task.FromResult(fmiCredential); + }) + .BuildConcrete(); + + var appCacheRecorder = confidentialApp.AppTokenCache.RecordAccess(); - var securityTokenDescriptor = new SecurityTokenDescriptor - { - Claims = claims, - SigningCredentials = new X509SigningCredentials(certificate) - }; + var authResult = await confidentialApp.AcquireTokenForClient(new[] { _fmiScope3 }) + .WithFmiPath(_fmiPath) + .ExecuteAsync() + .ConfigureAwait(false); - var handler = new JsonWebTokenHandler(); - return Task.FromResult(handler.CreateToken(securityTokenDescriptor)); + MsalAssert.AssertAuthResult(authResult); + appCacheRecorder.AssertAccessCounts(1, 1); + Assert.AreEqual(TokenSource.IdentityProvider, authResult.AuthenticationResultMetadata.TokenSource); + Assert.IsTrue(appCacheRecorder.LastAfterAccessNotificationArgs.IsApplicationCache); + Assert.IsTrue(appCacheRecorder.LastAfterAccessNotificationArgs.HasTokens); + CollectionAssert.AreEquivalent(new[] { _fmiScope3 }, appCacheRecorder.LastBeforeAccessNotificationArgs.RequestScopes.ToArray()); + CollectionAssert.AreEquivalent(new[] { _fmiScope3 }, appCacheRecorder.LastAfterAccessNotificationArgs.RequestScopes.ToArray()); + Assert.AreEqual(_fmiTenantId, appCacheRecorder.LastBeforeAccessNotificationArgs.RequestTenantId ?? ""); + Assert.AreEqual(_fmiTenantId, appCacheRecorder.LastAfterAccessNotificationArgs.RequestTenantId ?? ""); + Assert.IsTrue(authResult.AuthenticationResultMetadata.DurationTotalInMs > 0); + Assert.IsTrue(authResult.AuthenticationResultMetadata.DurationInHttpInMs > 0); } } } From e8240f2834f1cb8609844f87c448837854e87222 Mon Sep 17 00:00:00 2001 From: trwalke Date: Wed, 19 Feb 2025 22:31:54 -0800 Subject: [PATCH 3/5] Adding more FMI-FIC tests --- .../HeadlessTests/FmiIntegrationTests.cs | 71 +++++++++++++------ 1 file changed, 48 insertions(+), 23 deletions(-) diff --git a/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/FmiIntegrationTests.cs b/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/FmiIntegrationTests.cs index 69be80c6d0..26247fa398 100644 --- a/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/FmiIntegrationTests.cs +++ b/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/FmiIntegrationTests.cs @@ -1,22 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System; -using System.Collections.Generic; using System.Linq; using System.Security.Cryptography.X509Certificates; -using System.Text; using System.Threading.Tasks; using Microsoft.Identity.Client; -using Microsoft.Identity.Client.NativeInterop; using Microsoft.Identity.Test.Integration.Infrastructure; using Microsoft.Identity.Test.LabInfrastructure; using Microsoft.Identity.Test.Unit; using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.Identity.Test.Common.Core.Helpers; -using Microsoft.IdentityModel.JsonWebTokens; -using Microsoft.IdentityModel.Tokens; -using Microsoft.Identity.Client.Utils; namespace Microsoft.Identity.Test.Integration.NetCore.HeadlessTests { @@ -24,29 +17,61 @@ namespace Microsoft.Identity.Test.Integration.NetCore.HeadlessTests public class FmiIntegrationTests { private const string _fmiAppUrn = "urn:microsoft:identity:fmi"; - private const string _fmiClientId = "4df2cbbb-8612-49c1-87c8-f334d6d065ad"; + private const string _fmiRmaClientId = "4df2cbbb-8612-49c1-87c8-f334d6d065ad"; + private const string _fmiNonRmaClientId = ""; + private const string _fmiNonRmaClientIdInpersonation = ""; private const string _fmiTenantId = "f645ad92-e38d-4d1a-b510-d1b09a74a8ca"; private const string _fmiAuthority = "https://login.microsoftonline.com/" + _fmiTenantId; - private const string _fmiScope1 = "api://AzureFMITokenExchange/.default"; - private const string _fmiScope2 = "022907d3-0f1b-48f7-badc-1ba6abab6d66/.default"; - private const string _fmiScope3 = "api://AzureADTokenExchange/.default"; + private const string _fmiExchangeScope = "api://AzureFMITokenExchange/.default"; + private const string _fmiExchangeScopeAsGuid = "022907d3-0f1b-48f7-badc-1ba6abab6d66/.default"; + private const string _fmiAadExchangeScope = "api://AzureADTokenExchange/.default"; + private const string _fmiAadExchangeScopeAsGuid = "api://d796a5d2-0fb6-499a-b311-8bf5b3d058f7"; private const string _fmiPath = "SomeFmiPath/fmi"; [TestMethod] - public async Task RmaFmiCredFlow1Async() + public async Task RmaFmiCredFlowAsync() { - await RunRmaFlow(_fmiClientId, _fmiAuthority, _fmiScope1).ConfigureAwait(false); + await RunRmaFlow(_fmiRmaClientId, _fmiAuthority, _fmiExchangeScope).ConfigureAwait(false); } - public async Task RmaFmiCredFlow2Async() + [TestMethod] + public async Task RmaFmiCredFlowGuidAsync() { - await RunRmaFlow(_fmiClientId, _fmiAuthority, _fmiScope2).ConfigureAwait(false); + await RunRmaFlow(_fmiRmaClientId, _fmiAuthority, _fmiExchangeScopeAsGuid).ConfigureAwait(false); } + [TestMethod] public async Task FmiCredFlow2Async() { - var fmiCredential = await RunRmaFlow(_fmiClientId, _fmiAuthority, _fmiScope2).ConfigureAwait(false); - await RunFicFlow(fmiCredential).ConfigureAwait(false); + var fmiCredential = await RunRmaFlow(_fmiRmaClientId, _fmiAuthority, _fmiExchangeScope).ConfigureAwait(false); + await RunFicFlow(fmiCredential, _fmiExchangeScope, _fmiAppUrn).ConfigureAwait(false); + } + + [TestMethod] + public async Task FmiCredFlowAadExchangeAsync() + { + var fmiCredential = await RunRmaFlow(_fmiRmaClientId, _fmiAuthority, _fmiExchangeScope).ConfigureAwait(false); + await RunFicFlow(fmiCredential, _fmiAadExchangeScope, _fmiAppUrn).ConfigureAwait(false); + } + + [TestMethod] + public async Task FmiCredFlowAadExchangeGuidAsync() + { + var fmiCredential = await RunRmaFlow(_fmiRmaClientId, _fmiAuthority, _fmiAadExchangeScopeAsGuid).ConfigureAwait(false); + await RunFicFlow(fmiCredential, _fmiAadExchangeScope, _fmiAppUrn).ConfigureAwait(false); + } + + [TestMethod] + public async Task NonRmaFmiCredFlowAsync() + { + await RunRmaFlow(_fmiNonRmaClientId, _fmiAuthority, _fmiAadExchangeScope).ConfigureAwait(false); + } + + [TestMethod] + public async Task NonRmaFmiCredFlowAadExchangeAsync() + { + var fmiCredential = await RunRmaFlow(_fmiRmaClientId, _fmiAuthority, _fmiExchangeScope).ConfigureAwait(false); + await RunFicFlow(fmiCredential, _fmiExchangeScope, _fmiNonRmaClientIdInpersonation).ConfigureAwait(false); } private async Task RunRmaFlow(string clientId, string authority, string scope) @@ -83,23 +108,23 @@ private async Task RunRmaFlow(string clientId, string authority, string return authResult.AccessToken; } - private async Task RunFicFlow(string fmiCredential) + private async Task RunFicFlow(string fmiCredential, string scope, string clientId) { var confidentialApp = ConfidentialClientApplicationBuilder - .Create(_fmiAppUrn) + .Create(clientId) .WithAuthority(_fmiAuthority, true) .WithExtraQueryParameters("dc=ESTS-PUB-SCUS-LZ1-FD000-TEST1") .WithExperimentalFeatures(true) .WithClientAssertion((options) => { - Assert.AreEqual(_fmiAppUrn, options.ClientID); + Assert.AreEqual(clientId, options.ClientID); return Task.FromResult(fmiCredential); }) .BuildConcrete(); var appCacheRecorder = confidentialApp.AppTokenCache.RecordAccess(); - var authResult = await confidentialApp.AcquireTokenForClient(new[] { _fmiScope3 }) + var authResult = await confidentialApp.AcquireTokenForClient(new[] { scope }) .WithFmiPath(_fmiPath) .ExecuteAsync() .ConfigureAwait(false); @@ -109,8 +134,8 @@ private async Task RunFicFlow(string fmiCredential) Assert.AreEqual(TokenSource.IdentityProvider, authResult.AuthenticationResultMetadata.TokenSource); Assert.IsTrue(appCacheRecorder.LastAfterAccessNotificationArgs.IsApplicationCache); Assert.IsTrue(appCacheRecorder.LastAfterAccessNotificationArgs.HasTokens); - CollectionAssert.AreEquivalent(new[] { _fmiScope3 }, appCacheRecorder.LastBeforeAccessNotificationArgs.RequestScopes.ToArray()); - CollectionAssert.AreEquivalent(new[] { _fmiScope3 }, appCacheRecorder.LastAfterAccessNotificationArgs.RequestScopes.ToArray()); + CollectionAssert.AreEquivalent(new[] { scope }, appCacheRecorder.LastBeforeAccessNotificationArgs.RequestScopes.ToArray()); + CollectionAssert.AreEquivalent(new[] { scope }, appCacheRecorder.LastAfterAccessNotificationArgs.RequestScopes.ToArray()); Assert.AreEqual(_fmiTenantId, appCacheRecorder.LastBeforeAccessNotificationArgs.RequestTenantId ?? ""); Assert.AreEqual(_fmiTenantId, appCacheRecorder.LastAfterAccessNotificationArgs.RequestTenantId ?? ""); Assert.IsTrue(authResult.AuthenticationResultMetadata.DurationTotalInMs > 0); From 5df44818191153604556528df5538c65b26f3cf5 Mon Sep 17 00:00:00 2001 From: trwalke Date: Thu, 20 Feb 2025 10:51:54 -0800 Subject: [PATCH 4/5] Update --- .../HeadlessTests/FmiIntegrationTests.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/FmiIntegrationTests.cs b/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/FmiIntegrationTests.cs index 26247fa398..7190f9f354 100644 --- a/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/FmiIntegrationTests.cs +++ b/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/FmiIntegrationTests.cs @@ -41,6 +41,7 @@ public async Task RmaFmiCredFlowGuidAsync() } [TestMethod] + [Ignore("Waiting for ESTS Implementation")] public async Task FmiCredFlow2Async() { var fmiCredential = await RunRmaFlow(_fmiRmaClientId, _fmiAuthority, _fmiExchangeScope).ConfigureAwait(false); @@ -48,6 +49,7 @@ public async Task FmiCredFlow2Async() } [TestMethod] + [Ignore("Waiting for ESTS Implementation")] public async Task FmiCredFlowAadExchangeAsync() { var fmiCredential = await RunRmaFlow(_fmiRmaClientId, _fmiAuthority, _fmiExchangeScope).ConfigureAwait(false); @@ -55,6 +57,7 @@ public async Task FmiCredFlowAadExchangeAsync() } [TestMethod] + [Ignore("Waiting for ESTS Implementation")] public async Task FmiCredFlowAadExchangeGuidAsync() { var fmiCredential = await RunRmaFlow(_fmiRmaClientId, _fmiAuthority, _fmiAadExchangeScopeAsGuid).ConfigureAwait(false); @@ -62,12 +65,14 @@ public async Task FmiCredFlowAadExchangeGuidAsync() } [TestMethod] + [Ignore("Waiting for ESTS Implementation")] public async Task NonRmaFmiCredFlowAsync() { await RunRmaFlow(_fmiNonRmaClientId, _fmiAuthority, _fmiAadExchangeScope).ConfigureAwait(false); } [TestMethod] + [Ignore("Waiting for ESTS Implementation")] public async Task NonRmaFmiCredFlowAadExchangeAsync() { var fmiCredential = await RunRmaFlow(_fmiRmaClientId, _fmiAuthority, _fmiExchangeScope).ConfigureAwait(false); From bfe71d5e33ae5e8fefb8c22f588a18e9b8ef309f Mon Sep 17 00:00:00 2001 From: trwalke Date: Tue, 25 Feb 2025 23:53:24 -0800 Subject: [PATCH 5/5] Updating FMI tests --- .../HeadlessTests/FmiIntegrationTests.cs | 443 +++++++++++++++--- 1 file changed, 367 insertions(+), 76 deletions(-) diff --git a/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/FmiIntegrationTests.cs b/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/FmiIntegrationTests.cs index 7190f9f354..918000dae8 100644 --- a/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/FmiIntegrationTests.cs +++ b/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/FmiIntegrationTests.cs @@ -5,146 +5,437 @@ using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using Microsoft.Identity.Client; -using Microsoft.Identity.Test.Integration.Infrastructure; using Microsoft.Identity.Test.LabInfrastructure; using Microsoft.Identity.Test.Unit; using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.Identity.Test.Common.Core.Helpers; +using System; +using System.IdentityModel.Tokens.Jwt; namespace Microsoft.Identity.Test.Integration.NetCore.HeadlessTests { + /// + /// The tests in this file are demonstrations of the various authentication flows outlined in the "FMI protocol spec v1.0" Section 3.2 + /// [TestClass] public class FmiIntegrationTests { - private const string _fmiAppUrn = "urn:microsoft:identity:fmi"; - private const string _fmiRmaClientId = "4df2cbbb-8612-49c1-87c8-f334d6d065ad"; - private const string _fmiNonRmaClientId = ""; - private const string _fmiNonRmaClientIdInpersonation = ""; - private const string _fmiTenantId = "f645ad92-e38d-4d1a-b510-d1b09a74a8ca"; - private const string _fmiAuthority = "https://login.microsoftonline.com/" + _fmiTenantId; - private const string _fmiExchangeScope = "api://AzureFMITokenExchange/.default"; - private const string _fmiExchangeScopeAsGuid = "022907d3-0f1b-48f7-badc-1ba6abab6d66/.default"; - private const string _fmiAadExchangeScope = "api://AzureADTokenExchange/.default"; - private const string _fmiAadExchangeScopeAsGuid = "api://d796a5d2-0fb6-499a-b311-8bf5b3d058f7"; - private const string _fmiPath = "SomeFmiPath/fmi"; + private byte[] _serializedCache; [TestMethod] - public async Task RmaFmiCredFlowAsync() + //RMA getting FMI cred for a leaf entity or sub-RMA + public async Task Flow1_RmaCredential_From_CertTestAsync() { - await RunRmaFlow(_fmiRmaClientId, _fmiAuthority, _fmiExchangeScope).ConfigureAwait(false); + //Arrange + X509Certificate2 cert = CertificateHelper.FindCertificateByName(TestConstants.AutomationTestCertName); + string expectedExternalCacheKey = null; + + Action extCacheKeyEvaluator = (args) => + { + if (expectedExternalCacheKey != null) + { + Assert.IsTrue(args.SuggestedCacheKey.Contains(expectedExternalCacheKey)); + } + }; + + //Fmi app/scenario parameters + var clientId = "4df2cbbb-8612-49c1-87c8-f334d6d065ad"; + var scope = "api://AzureFMITokenExchange/.default"; + + //Act + //Create application + var confidentialApp = ConfidentialClientApplicationBuilder + .Create(clientId) + .WithAuthority("https://login.microsoftonline.com/f645ad92-e38d-4d1a-b510-d1b09a74a8ca", true) + .WithExtraQueryParameters("dc=ESTS-PUB-SCUS-LZ1-FD000-TEST1") //Enables MSAL to target ESTS Test slice + .WithExperimentalFeatures(true) //WithFmiPath is experimental so experimental features needs to be enabled on the app + .WithCertificate(cert, sendX5C: true) //sendX5c enables SN+I auth which is required for FMI flows + .BuildConcrete(); + + //Configure token cache serialization + confidentialApp.AppTokenCache.SetBeforeAccess(BeforeCacheAccess); + confidentialApp.AppTokenCache.SetAfterAccess(AfterCacheAccess); + + //Recording test data for Asserts + var appCacheAccess = confidentialApp.AppTokenCache.RecordAccess(extCacheKeyEvaluator); + expectedExternalCacheKey = "4df2cbbb-8612-49c1-87c8-f334d6d065ad_f645ad92-e38d-4d1a-b510-d1b09a74a8ca_7CX57Q63os7benQ6ER0sxgJPtNQSv7TGb5zexcidFoI_AppTokenCache"; + + //Acquire Fmi Cred + var authResult = await confidentialApp.AcquireTokenForClient(new[] { scope }) + .WithFmiPath("SomeFmiPath/Path") //Sets fmi path in client credential request. + .ExecuteAsync() + .ConfigureAwait(false); + + //Assert + AssertResults(authResult, + confidentialApp, + "-login.windows.net-atext-4df2cbbb-8612-49c1-87c8-f334d6d065ad-f645ad92-e38d-4d1a-b510-d1b09a74a8ca-api://azurefmitokenexchange/.default-7cx57q63os7benq6er0sxgjptnqsv7tgb5zexcidfoi", + "a9dd8a2a-df54-4ae0-84f9-38c8d57e5265"); } [TestMethod] - public async Task RmaFmiCredFlowGuidAsync() + //RMA getting FMI token for a leaf entity + public async Task Flow2_RmaToken_From_CertTestAsync() { - await RunRmaFlow(_fmiRmaClientId, _fmiAuthority, _fmiExchangeScopeAsGuid).ConfigureAwait(false); + //Arrange + X509Certificate2 cert = CertificateHelper.FindCertificateByName(TestConstants.AutomationTestCertName); + string expectedExternalCacheKey = null; + + Action extCacheKeyEvaluator = (args) => + { + if (expectedExternalCacheKey != null) + { + Assert.IsTrue(args.SuggestedCacheKey.Contains(expectedExternalCacheKey)); + } + }; + + //Fmi app/scenario parameters + var clientId = "4df2cbbb-8612-49c1-87c8-f334d6d065ad"; + var scope = "022907d3-0f1b-48f7-badc-1ba6abab6d66/.default"; + + //Act + //Create application + var confidentialApp = ConfidentialClientApplicationBuilder + .Create(clientId) + .WithAuthority("https://login.microsoftonline.com/f645ad92-e38d-4d1a-b510-d1b09a74a8ca", true) + .WithExtraQueryParameters("dc=ESTS-PUB-SCUS-LZ1-FD000-TEST1") //Enables MSAL to target ESTS Test slice + .WithExperimentalFeatures(true) //WithFmiPath is experimental so experimental features needs to be enabled on the app + .WithCertificate(cert, sendX5C: true) //sendX5c enables SN+I auth which is required for FMI flows + .BuildConcrete(); + + //Configure token cache serialization + confidentialApp.AppTokenCache.SetBeforeAccess(BeforeCacheAccess); + confidentialApp.AppTokenCache.SetAfterAccess(AfterCacheAccess); + + //Recording test data for Asserts + var appCacheAccess = confidentialApp.AppTokenCache.RecordAccess(extCacheKeyEvaluator); + expectedExternalCacheKey = "4df2cbbb-8612-49c1-87c8-f334d6d065ad_f645ad92-e38d-4d1a-b510-d1b09a74a8ca_7CX57Q63os7benQ6ER0sxgJPtNQSv7TGb5zexcidFoI_AppTokenCache"; + + //Acquire Token + var authResult = await confidentialApp.AcquireTokenForClient(new[] { scope }) + .WithFmiPath("SomeFmiPath/Path") //Sets fmi path in client credential request. + .ExecuteAsync() + .ConfigureAwait(false); + + //Assert + AssertResults(authResult, + confidentialApp, + "-login.windows.net-atext-4df2cbbb-8612-49c1-87c8-f334d6d065ad-f645ad92-e38d-4d1a-b510-d1b09a74a8ca-022907d3-0f1b-48f7-badc-1ba6abab6d66/.default-7cx57q63os7benq6er0sxgjptnqsv7tgb5zexcidfoi", + "022907d3-0f1b-48f7-badc-1ba6abab6d66"); } [TestMethod] [Ignore("Waiting for ESTS Implementation")] - public async Task FmiCredFlow2Async() + //Sub-RMA getting FMI cred for a child sub-RMA + public async Task Flow3_FmiCredential_From_RmaCredential() { - var fmiCredential = await RunRmaFlow(_fmiRmaClientId, _fmiAuthority, _fmiExchangeScope).ConfigureAwait(false); - await RunFicFlow(fmiCredential, _fmiExchangeScope, _fmiAppUrn).ConfigureAwait(false); + //Arrange + X509Certificate2 cert = CertificateHelper.FindCertificateByName(TestConstants.AutomationTestCertName); + string expectedExternalCacheKey = null; + + Action extCacheKeyEvaluator = (args) => + { + if (expectedExternalCacheKey != null) + { + Assert.IsTrue(args.SuggestedCacheKey.Contains(expectedExternalCacheKey)); + } + }; + + //Fmi app/scenario parameters + var clientId = "urn:microsoft:identity:fmi"; + var scope = "api://AzureFMITokenExchange/.default\""; + + //Act + //Create application + var confidentialApp = ConfidentialClientApplicationBuilder + .Create(clientId) + .WithAuthority("https://login.microsoftonline.com/f645ad92-e38d-4d1a-b510-d1b09a74a8ca", true) + .WithExtraQueryParameters("dc=ESTS-PUB-SCUS-LZ1-FD000-TEST1") //Enables MSAL to target ESTS Test slice + .WithExperimentalFeatures(true) //WithFmiPath is experimental so experimental features needs to be enabled on the app + .WithClientAssertion((options) => GetParentCredential(options)) + .BuildConcrete(); + + //Configure token cache serialization + confidentialApp.AppTokenCache.SetBeforeAccess(BeforeCacheAccess); + confidentialApp.AppTokenCache.SetAfterAccess(AfterCacheAccess); + + //Recording test data for Asserts + var appCacheAccess = confidentialApp.AppTokenCache.RecordAccess(extCacheKeyEvaluator); + expectedExternalCacheKey = "4df2cbbb-8612-49c1-87c8-f334d6d065ad_f645ad92-e38d-4d1a-b510-d1b09a74a8ca_7CX57Q63os7benQ6ER0sxgJPtNQSv7TGb5zexcidFoI_AppTokenCache"; + + //Acquire Fmi Cred + var authResult = await confidentialApp.AcquireTokenForClient(new[] { scope }) + .WithFmiPath("SomeFmiPath/Path") //Sets fmi path in client credential request. + .ExecuteAsync() + .ConfigureAwait(false); + + //Assert + AssertResults(authResult, + confidentialApp, + "expectedInternalCacheKey", + "expectedAudience"); } [TestMethod] [Ignore("Waiting for ESTS Implementation")] - public async Task FmiCredFlowAadExchangeAsync() + //Sub-RMA getting FIC for leaf entity. + public async Task Flow4_SubRma_FmiCredential_For_leaf() { - var fmiCredential = await RunRmaFlow(_fmiRmaClientId, _fmiAuthority, _fmiExchangeScope).ConfigureAwait(false); - await RunFicFlow(fmiCredential, _fmiAadExchangeScope, _fmiAppUrn).ConfigureAwait(false); + //Arrange + X509Certificate2 cert = CertificateHelper.FindCertificateByName(TestConstants.AutomationTestCertName); + string expectedExternalCacheKey = null; + + Action extCacheKeyEvaluator = (args) => + { + if (expectedExternalCacheKey != null) + { + Assert.IsTrue(args.SuggestedCacheKey.Contains(expectedExternalCacheKey)); + } + }; + + //Fmi app/scenario parameters + var clientId = "urn:microsoft:identity:fmi"; + var scope = "api://d796a5d2-0fb6-499a-b311-8bf5b3d058f7/.default"; + + //Act + //Create application + var confidentialApp = ConfidentialClientApplicationBuilder + .Create(clientId) + .WithAuthority("https://login.microsoftonline.com/f645ad92-e38d-4d1a-b510-d1b09a74a8ca", true) + .WithExtraQueryParameters("dc=ESTS-PUB-SCUS-LZ1-FD000-TEST1") //Enables MSAL to target ESTS Test slice + .WithExperimentalFeatures(true) //WithFmiPath is experimental so experimental features needs to be enabled on the app + .WithClientAssertion((options) => GetParentCredential(options)) + .BuildConcrete(); + + //Configure token cache serialization + confidentialApp.AppTokenCache.SetBeforeAccess(BeforeCacheAccess); + confidentialApp.AppTokenCache.SetAfterAccess(AfterCacheAccess); + + //Recording test data for Asserts + var appCacheAccess = confidentialApp.AppTokenCache.RecordAccess(extCacheKeyEvaluator); + expectedExternalCacheKey = ""; + + //Acquire Fmi Cred + var authResult = await confidentialApp.AcquireTokenForClient(new[] { scope }) + .WithFmiPath("SomeFmiPath/Path") //Sets fmi path in client credential request. + .ExecuteAsync() + .ConfigureAwait(false); + + //Assert + AssertResults(authResult, + confidentialApp, + "expectedInternalCacheKey", + "expectedAudience"); } [TestMethod] [Ignore("Waiting for ESTS Implementation")] - public async Task FmiCredFlowAadExchangeGuidAsync() + //Sub-RMA getting FMI token for leaf entity + public async Task Flow5_SubRma_FmiToken_From_FmiCred_For_leafTestAsync() { - var fmiCredential = await RunRmaFlow(_fmiRmaClientId, _fmiAuthority, _fmiAadExchangeScopeAsGuid).ConfigureAwait(false); - await RunFicFlow(fmiCredential, _fmiAadExchangeScope, _fmiAppUrn).ConfigureAwait(false); + //Arrange + X509Certificate2 cert = CertificateHelper.FindCertificateByName(TestConstants.AutomationTestCertName); + string expectedExternalCacheKey = null; + + Action extCacheKeyEvaluator = (args) => + { + if (expectedExternalCacheKey != null) + { + Assert.IsTrue(args.SuggestedCacheKey.Contains(expectedExternalCacheKey)); + } + }; + + //Fmi app/scenario parameters + var clientId = "urn:microsoft:identity:fmi"; + var scope = "api://d796a5d2-0fb6-499a-b311-8bf5b3d058f7/.default"; + + //Act + //Create application + var confidentialApp = ConfidentialClientApplicationBuilder + .Create(clientId) + .WithAuthority("https://login.microsoftonline.com/f645ad92-e38d-4d1a-b510-d1b09a74a8ca", true) + .WithExtraQueryParameters("dc=ESTS-PUB-SCUS-LZ1-FD000-TEST1") //Enables MSAL to target ESTS Test slice + .WithExperimentalFeatures(true) //WithFmiPath is experimental so experimental features needs to be enabled on the app + .WithClientAssertion((options) => GetParentCredential(options)) + .BuildConcrete(); + + //Configure token cache serialization + confidentialApp.AppTokenCache.SetBeforeAccess(BeforeCacheAccess); + confidentialApp.AppTokenCache.SetAfterAccess(AfterCacheAccess); + + //Recording test data for Asserts + var appCacheAccess = confidentialApp.AppTokenCache.RecordAccess(extCacheKeyEvaluator); + expectedExternalCacheKey = ""; + + //Acquire Fmi Cred + var authResult = await confidentialApp.AcquireTokenForClient(new[] { scope }) + .WithFmiPath("SomeFmiPath/Path") //Sets fmi path in client credential request. + .ExecuteAsync() + .ConfigureAwait(false); + + //Assert + AssertResults(authResult, + confidentialApp, + "expectedInternalCacheKey", + "expectedAudience"); } [TestMethod] [Ignore("Waiting for ESTS Implementation")] - public async Task NonRmaFmiCredFlowAsync() + //Any app cred (including RMAs) -> FMI-FIC (1st leg of FMI connector flow). + public async Task Flow6_FmiFic_From_AnyAppTestAsync() { - await RunRmaFlow(_fmiNonRmaClientId, _fmiAuthority, _fmiAadExchangeScope).ConfigureAwait(false); + //Arrange + X509Certificate2 cert = CertificateHelper.FindCertificateByName(TestConstants.AutomationTestCertName); + string expectedExternalCacheKey = null; + + Action extCacheKeyEvaluator = (args) => + { + if (expectedExternalCacheKey != null) + { + Assert.IsTrue(args.SuggestedCacheKey.Contains(expectedExternalCacheKey)); + } + }; + + //Fmi app/scenario parameters + var clientId = "4df2cbbb-8612-49c1-87c8-f334d6d065ad"; + var scope = "api://AzureADTokenExchange/.default"; + + //Act + //Create application + var confidentialApp = ConfidentialClientApplicationBuilder + .Create(clientId) + .WithAuthority("https://login.microsoftonline.com/f645ad92-e38d-4d1a-b510-d1b09a74a8ca", true) + .WithExtraQueryParameters("dc=ESTS-PUB-SCUS-LZ1-FD000-TEST1") //Enables MSAL to target ESTS Test slice + .WithExperimentalFeatures(true) //WithFmiPath is experimental so experimental features needs to be enabled on the app + .WithCertificate(cert, sendX5C: true) //sendX5c enables SN+I auth which is required for FMI flows + .BuildConcrete(); + + //Configure token cache serialization + confidentialApp.AppTokenCache.SetBeforeAccess(BeforeCacheAccess); + confidentialApp.AppTokenCache.SetAfterAccess(AfterCacheAccess); + + //Recording test data for Asserts + var appCacheAccess = confidentialApp.AppTokenCache.RecordAccess(extCacheKeyEvaluator); + expectedExternalCacheKey = ""; + + //Acquire Fmi Cred + var authResult = await confidentialApp.AcquireTokenForClient(new[] { scope }) + .WithFmiPath("SomeFmiPath/Path") //Sets fmi path in client credential request. + .ExecuteAsync() + .ConfigureAwait(false); + + //Assert + AssertResults(authResult, + confidentialApp, + "expectedInternalCacheKey", + "expectedAudience"); } [TestMethod] [Ignore("Waiting for ESTS Implementation")] - public async Task NonRmaFmiCredFlowAadExchangeAsync() - { - var fmiCredential = await RunRmaFlow(_fmiRmaClientId, _fmiAuthority, _fmiExchangeScope).ConfigureAwait(false); - await RunFicFlow(fmiCredential, _fmiExchangeScope, _fmiNonRmaClientIdInpersonation).ConfigureAwait(false); - } - - private async Task RunRmaFlow(string clientId, string authority, string scope) + //FMI-FIC -> Access token (2nd leg of FMI connector flow) + public async Task Flow7_FmiToken_From_FmiFicTestAsync() { + //Arrange X509Certificate2 cert = CertificateHelper.FindCertificateByName(TestConstants.AutomationTestCertName); + string expectedExternalCacheKey = null; + + Action extCacheKeyEvaluator = (args) => + { + if (expectedExternalCacheKey != null) + { + Assert.IsTrue(args.SuggestedCacheKey.Contains(expectedExternalCacheKey)); + } + }; + //Fmi app/scenario parameters + var clientId = "AppId to impersonate"; + var scope = "api://d796a5d2-0fb6-499a-b311-8bf5b3d058f7/.default"; + + //Act + //Create application var confidentialApp = ConfidentialClientApplicationBuilder .Create(clientId) - .WithAuthority(authority, true) - .WithExtraQueryParameters("dc=ESTS-PUB-SCUS-LZ1-FD000-TEST1") - .WithExperimentalFeatures(true) - .WithCertificate(cert, sendX5C: true) + .WithAuthority("https://login.microsoftonline.com/f645ad92-e38d-4d1a-b510-d1b09a74a8ca", true) + .WithExtraQueryParameters("dc=ESTS-PUB-SCUS-LZ1-FD000-TEST1") //Enables MSAL to target ESTS Test slice + .WithExperimentalFeatures(true) //WithFmiPath is experimental so experimental features needs to be enabled on the app + .WithClientAssertion((options) => GetParentCredential(options)) .BuildConcrete(); - var appCacheRecorder = confidentialApp.AppTokenCache.RecordAccess(); + //Configure token cache serialization + confidentialApp.AppTokenCache.SetBeforeAccess(BeforeCacheAccess); + confidentialApp.AppTokenCache.SetAfterAccess(AfterCacheAccess); + + //Recording test data for Asserts + var appCacheAccess = confidentialApp.AppTokenCache.RecordAccess(extCacheKeyEvaluator); + expectedExternalCacheKey = ""; + //Acquire Fmi Cred var authResult = await confidentialApp.AcquireTokenForClient(new[] { scope }) - .WithFmiPath(_fmiPath) + .WithFmiPath("") //Sets fmi path in client credential request. .ExecuteAsync() .ConfigureAwait(false); - MsalAssert.AssertAuthResult(authResult); - appCacheRecorder.AssertAccessCounts(1, 1); - Assert.AreEqual(TokenSource.IdentityProvider, authResult.AuthenticationResultMetadata.TokenSource); - Assert.IsTrue(appCacheRecorder.LastAfterAccessNotificationArgs.IsApplicationCache); - Assert.IsTrue(appCacheRecorder.LastAfterAccessNotificationArgs.HasTokens); - CollectionAssert.AreEquivalent(new[] { scope }, appCacheRecorder.LastBeforeAccessNotificationArgs.RequestScopes.ToArray()); - CollectionAssert.AreEquivalent(new[] { scope }, appCacheRecorder.LastAfterAccessNotificationArgs.RequestScopes.ToArray()); - Assert.AreEqual(_fmiTenantId, appCacheRecorder.LastBeforeAccessNotificationArgs.RequestTenantId ?? ""); - Assert.AreEqual(_fmiTenantId, appCacheRecorder.LastAfterAccessNotificationArgs.RequestTenantId ?? ""); - Assert.IsTrue(authResult.AuthenticationResultMetadata.DurationTotalInMs > 0); - Assert.IsTrue(authResult.AuthenticationResultMetadata.DurationInHttpInMs > 0); - - return authResult.AccessToken; + //Assert + AssertResults(authResult, + confidentialApp, + "expectedInternalCacheKey", + "expectedAudience"); } - private async Task RunFicFlow(string fmiCredential, string scope, string clientId) + private static async Task GetParentCredential(AssertionRequestOptions options) { + //Fmi app/scenario parameters + var clientId = "4df2cbbb-8612-49c1-87c8-f334d6d065ad"; + var scope = "022907d3-0f1b-48f7-badc-1ba6abab6d66/.default"; + + X509Certificate2 cert = CertificateHelper.FindCertificateByName(TestConstants.AutomationTestCertName); + + //Create application var confidentialApp = ConfidentialClientApplicationBuilder .Create(clientId) - .WithAuthority(_fmiAuthority, true) - .WithExtraQueryParameters("dc=ESTS-PUB-SCUS-LZ1-FD000-TEST1") - .WithExperimentalFeatures(true) - .WithClientAssertion((options) => - { - Assert.AreEqual(clientId, options.ClientID); - return Task.FromResult(fmiCredential); - }) + .WithAuthority("https://login.microsoftonline.com/f645ad92-e38d-4d1a-b510-d1b09a74a8ca", true) + .WithExtraQueryParameters("dc=ESTS-PUB-SCUS-LZ1-FD000-TEST1") //Enables MSAL to target ESTS Test slice + .WithExperimentalFeatures(true) //WithFmiPath is experimental so experimental features needs to be enabled on the app + .WithCertificate(cert, sendX5C: true) //sendX5c enables SN+I auth which is required for FMI flows .BuildConcrete(); - var appCacheRecorder = confidentialApp.AppTokenCache.RecordAccess(); - + //Acquire Token var authResult = await confidentialApp.AcquireTokenForClient(new[] { scope }) - .WithFmiPath(_fmiPath) + .WithFmiPath("SomeFmiPath/Path") //Sets fmi path in client credential request. .ExecuteAsync() .ConfigureAwait(false); - MsalAssert.AssertAuthResult(authResult); - appCacheRecorder.AssertAccessCounts(1, 1); - Assert.AreEqual(TokenSource.IdentityProvider, authResult.AuthenticationResultMetadata.TokenSource); - Assert.IsTrue(appCacheRecorder.LastAfterAccessNotificationArgs.IsApplicationCache); - Assert.IsTrue(appCacheRecorder.LastAfterAccessNotificationArgs.HasTokens); - CollectionAssert.AreEquivalent(new[] { scope }, appCacheRecorder.LastBeforeAccessNotificationArgs.RequestScopes.ToArray()); - CollectionAssert.AreEquivalent(new[] { scope }, appCacheRecorder.LastAfterAccessNotificationArgs.RequestScopes.ToArray()); - Assert.AreEqual(_fmiTenantId, appCacheRecorder.LastBeforeAccessNotificationArgs.RequestTenantId ?? ""); - Assert.AreEqual(_fmiTenantId, appCacheRecorder.LastAfterAccessNotificationArgs.RequestTenantId ?? ""); - Assert.IsTrue(authResult.AuthenticationResultMetadata.DurationTotalInMs > 0); - Assert.IsTrue(authResult.AuthenticationResultMetadata.DurationInHttpInMs > 0); + return authResult.AccessToken; + } + + private void BeforeCacheAccess(TokenCacheNotificationArgs args) + { + args.TokenCache.DeserializeMsalV3(_serializedCache); + } + + private void AfterCacheAccess(TokenCacheNotificationArgs args) + { + _serializedCache = args.TokenCache.SerializeMsalV3(); + } + + private void AssertResults( + AuthenticationResult authResult, + ConfidentialClientApplication confidentialApp, + string expectedInternalCacheKey, + string expectedAudience) + { + var handler = new JwtSecurityTokenHandler(); + var jsonToken = handler.ReadToken(authResult.AccessToken) as JwtSecurityToken; + var subject = jsonToken.Payload["sub"].ToString(); + var audience = jsonToken.Payload["aud"].ToString(); + var token = confidentialApp.AppTokenCacheInternal.Accessor.GetAllAccessTokens().First(); + + Assert.IsNotNull(authResult); + Assert.AreEqual(expectedAudience, audience); + Assert.IsTrue(subject.Contains("SomeFmiPath/Path")); + Assert.AreEqual(expectedInternalCacheKey, token.CacheKey); } } }