private IConfidentialClientApplication BuildCCA( IConfidentialAppSettings settings, HttpSnifferClientFactory factory, bool useClaims = false, string region = ConfidentialClientApplication.AttemptRegionDiscovery) { var builder = ConfidentialClientApplicationBuilder.Create(settings.ClientId); if (useClaims) { builder.WithClientAssertion(GetSignedClientAssertionUsingMsalInternal(settings.ClientId, GetClaims(settings))); } else { builder.WithCertificate(settings.GetCertificate()); } builder.WithAuthority($@"https://{settings.Environment}/{settings.TenantId}") .WithTestLogging() .WithExperimentalFeatures(true) .WithHttpClientFactory(factory); if (region != null) { builder.WithAzureRegion(region); } return(builder.Build()); }
public async Task AcquireTokenToGlobalEndpointThenRegionalEndpoint_UseTokenFromCacheAsync() { // Arrange var factory = new HttpSnifferClientFactory(); _confidentialClientApplication = ConfidentialClientApplicationBuilder.Create(PublicCloudConfidentialClientID) .WithClientAssertion(GetSignedClientAssertionUsingMsalInternal(PublicCloudConfidentialClientID, GetClaims())) //.WithClientSecret(_keyVault.GetSecret(TestConstants.MsalCCAKeyVaultUri).Value) .WithAuthority(PublicCloudTestAuthority) .WithTestLogging() .WithExperimentalFeatures(true) .WithHttpClientFactory(factory) .Build(); Environment.SetEnvironmentVariable(TestConstants.RegionName, TestConstants.Region); AuthenticationResult result = await GetAuthenticationResultAsync(autoDetectRegion : false).ConfigureAwait(false); // global endpoint AssertValidHost(false, factory); AssertTokenSource_IsIdP(result); result = await GetAuthenticationResultAsync().ConfigureAwait(false); // regional endpoint, use cached token AssertTokenSource_IsCache(result); result = await GetAuthenticationResultAsync(autoDetectRegion : false).ConfigureAwait(false); // global endpoint, use cached token AssertTokenSource_IsCache(result); result = await GetAuthenticationResultAsync(withForceRefresh : true).ConfigureAwait(false); // regional endpoint, new token AssertValidHost(true, factory, 1); AssertTokenSource_IsIdP(result); }
public async Task PKeyAuthNonInteractiveTestAsync() { //Arrange var labResponse = await LabUserHelper.GetSpecificUserAsync(_deviceAuthuser).ConfigureAwait(false); var factory = new HttpSnifferClientFactory(); var msalPublicClient = PublicClientApplicationBuilder .Create(labResponse.App.AppId) .WithAuthority("https://login.microsoftonline.com/organizations/") .WithHttpClientFactory(factory) .Build(); //Act var authResult = msalPublicClient.AcquireTokenByUsernamePassword( new[] { "user.read" }, labResponse.User.Upn, new NetworkCredential("", labResponse.User.GetOrFetchPassword()).SecurePassword) .WithClaims(JObject.Parse(_claims).ToString()) .ExecuteAsync(CancellationToken.None).Result; //Assert Assert.IsNotNull(authResult); Assert.IsNotNull(authResult.AccessToken); Assert.IsNotNull(authResult.IdToken); Assert.IsTrue(string.Equals(_deviceAuthuser, authResult.Account.Username, StringComparison.InvariantCultureIgnoreCase)); //Assert that the PKeyAuth header is used and the token response is successful var(req, res) = factory.RequestsAndResponses .Where(x => x.Item1.Headers.Authorization != null && x.Item1.Headers.Authorization.Scheme.Contains(PKeyAuthConstants.PKeyAuthName) && x.Item2.StatusCode == HttpStatusCode.OK).FirstOrDefault(); Assert.IsNotNull(req); Assert.IsNotNull(res); }
public async Task PKeyAuthNonInteractiveTestAsync() { //Arrange var labResponse = await LabUserHelper.GetSpecificUserAsync(_deviceAuthuser).ConfigureAwait(false); var factory = new HttpSnifferClientFactory(); var msalPublicClient = PublicClientApplicationBuilder .Create(labResponse.App.AppId) .WithAuthority("https://login.microsoftonline.com/organizations/") .WithHttpClientFactory(factory) .Build(); //Act var authResult = msalPublicClient.AcquireTokenByUsernamePassword( new[] { "user.read" }, labResponse.User.Upn, new NetworkCredential("", labResponse.User.GetOrFetchPassword()).SecurePassword) .WithClaims(JObject.Parse(_claims).ToString()) .ExecuteAsync(CancellationToken.None).Result; //Assert Assert.IsNotNull(authResult); Assert.IsNotNull(authResult.AccessToken); Assert.IsNotNull(authResult.IdToken); Assert.IsTrue(string.Equals(_deviceAuthuser, authResult.Account.Username, StringComparison.InvariantCultureIgnoreCase)); var(req, res) = factory.RequestsAndResponses .Where(x => x.Item1.RequestUri.AbsoluteUri == labResponse.Lab.Authority + "organizations/oauth2/v2.0/token" && x.Item2.StatusCode == HttpStatusCode.OK).ElementAt(1); var AuthHeader = req.Headers.Single(h => h.Key == "Authorization").Value.FirstOrDefault(); Assert.IsTrue(!string.IsNullOrEmpty(AuthHeader)); Assert.IsTrue(AuthHeader.Contains("PKeyAuth")); }
[Ignore] //See https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/2781 public async Task InvalidRegion_GoesToInvalidAuthority_Async() { // Arrange var factory = new HttpSnifferClientFactory(); var settings = ConfidentialAppSettings.GetSettings(Cloud.Public); _confidentialClientApplication = BuildCCA(settings, factory, true, "invalid"); Environment.SetEnvironmentVariable(TestConstants.RegionName, TestConstants.Region); AuthenticationResult result = await GetAuthenticationResultAsync(settings.AppScopes).ConfigureAwait(false); // regional endpoint AssertTokenSourceIsIdp(result); Assert.AreEqual( "https://invalid.login.microsoft.com/72f988bf-86f1-41af-91ab-2d7cd011db47/oauth2/v2.0/token?allowestsrnonmsi=true", factory.RequestsAndResponses.Single().Item1.RequestUri.ToString()); AssertTelemetry(factory, $"{TelemetryConstants.HttpTelemetrySchemaVersion}|1004,{CacheInfoTelemetry.NoCachedAT:D},invalid,3,3|0,1"); _confidentialClientApplication = BuildCCA(settings, factory, true, TestConstants.Region); result = await GetAuthenticationResultAsync(settings.AppScopes, withForceRefresh : true).ConfigureAwait(false); // regional endpoint AssertTokenSourceIsIdp(result); AssertValidHost(true, factory, 1); AssertTelemetry(factory, $"{TelemetryConstants.HttpTelemetrySchemaVersion}|1004,{CacheInfoTelemetry.ForceRefresh:D},centralus,2,1|0,1", 1); }
private async Task CheckTelemetryHeadersAsync( LabResponse labResponse) { var factory = new HttpSnifferClientFactory(); var msalPublicClient = PublicClientApplicationBuilder .Create(labResponse.App.AppId) .WithAuthority(Authority) .WithHttpClientFactory(factory) .Build(); await RunAcquireTokenWithUsernameIncorrectPasswordAsync(msalPublicClient, labResponse.User.Upn).ConfigureAwait(false); AuthenticationResult authResult = await msalPublicClient .AcquireTokenByUsernamePassword(s_scopes, labResponse.User.Upn, new NetworkCredential("", labResponse.User.GetOrFetchPassword()).SecurePassword) .WithCorrelationId(CorrelationId) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.IsNotNull(authResult); Assert.AreEqual(TokenSource.IdentityProvider, authResult.AuthenticationResultMetadata.TokenSource); Assert.IsNotNull(authResult.AccessToken); Assert.IsNotNull(authResult.IdToken); Assert.IsTrue(string.Equals(labResponse.User.Upn, authResult.Account.Username, StringComparison.InvariantCultureIgnoreCase)); AssertTelemetryHeaders(factory, true, labResponse); }
public async Task AcquireTokenToRegionalEndpointAsync() { // Arrange var factory = new HttpSnifferClientFactory(); _confidentialClientApplication = ConfidentialClientApplicationBuilder.Create(PublicCloudConfidentialClientID) .WithClientAssertion(GetSignedClientAssertionUsingMsalInternal(PublicCloudConfidentialClientID, GetClaims())) //.WithClientSecret(_keyVault.GetSecret(TestConstants.MsalCCAKeyVaultUri).Value) //use the client secret in case of cert errors .WithAuthority(PublicCloudTestAuthority) .WithTestLogging() .WithExperimentalFeatures(true) .WithHttpClientFactory(factory) .Build(); try { Environment.SetEnvironmentVariable(TestConstants.RegionName, TestConstants.Region); AuthenticationResult result = await CreateAuthenticationResultAsync().ConfigureAwait(false); // regional endpoint AssertTokenSource_IsIdP(result); AssertValidHost(true, factory); } finally { Environment.SetEnvironmentVariable(TestConstants.RegionName, null); } }
public async Task AcquireTokenUserProvidedRegionDifferentFromRegionDetectedAsync() { // Arrange var factory = new HttpSnifferClientFactory(); _confidentialClientApplication = ConfidentialClientApplicationBuilder.Create(PublicCloudConfidentialClientID) .WithClientAssertion(GetSignedClientAssertionUsingMsalInternal(PublicCloudConfidentialClientID, GetClaims())) //.WithClientSecret(_keyVault.GetSecret(TestConstants.MsalCCAKeyVaultUri).Value) //use the client secret in case of cert errors .WithAuthority(PublicCloudTestAuthority) .WithTestLogging() .WithExperimentalFeatures(true) .WithHttpClientFactory(factory) .Build(); Environment.SetEnvironmentVariable(TestConstants.RegionName, TestConstants.Region); AuthenticationResult result = await GetAuthenticationResultAsync(userProvidedRegion : "invalid").ConfigureAwait(false); // regional endpoint AssertTokenSource_IsIdP(result); AssertValidHost(true, factory); AssertTelemetry(factory, "2|1004,0|centralus,1,0,invalid,0"); result = await GetAuthenticationResultAsync(userProvidedRegion : TestConstants.Region, withForceRefresh : true).ConfigureAwait(false); // regional endpoint AssertTokenSource_IsIdP(result); AssertValidHost(true, factory, 1); AssertTelemetry(factory, "2|1004,1|centralus,3,0,centralus,", 1); }
private void AssertCcsRoutingInformationIsNotSent(HttpSnifferClientFactory factory) { var(req, res) = factory.RequestsAndResponses.Single(x => x.Item1.RequestUri.AbsoluteUri.Contains("oauth2/v2.0/token") && x.Item2.StatusCode == HttpStatusCode.OK); Assert.IsTrue(!req.Headers.Any(h => h.Key == Constants.CcsRoutingHintHeader)); }
private async Task RunHappyPathTestAsync(LabResponse labResponse) { var factory = new HttpSnifferClientFactory(); var msalPublicClient = PublicClientApplicationBuilder .Create(labResponse.App.AppId) .WithTestLogging() .WithHttpClientFactory(factory) .WithAuthority(labResponse.Lab.Authority, "organizations") .Build(); AuthenticationResult authResult = await msalPublicClient .AcquireTokenByUsernamePassword(s_scopes, labResponse.User.Upn, new NetworkCredential("", labResponse.User.GetOrFetchPassword()).SecurePassword) .WithCorrelationId(CorrelationId) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.IsNotNull(authResult); Assert.AreEqual(TokenSource.IdentityProvider, authResult.AuthenticationResultMetadata.TokenSource); Assert.IsNotNull(authResult.AccessToken); Assert.IsNotNull(authResult.IdToken); Assert.IsTrue(string.Equals(labResponse.User.Upn, authResult.Account.Username, StringComparison.InvariantCultureIgnoreCase)); AssertTelemetryHeaders(factory, false, labResponse); // If test fails with "user needs to consent to the application, do an interactive request" error, // Do the following: // 1) Add in code to pull the user's password before creating the SecureString, and put a breakpoint there. // string password = ((LabUser)user).GetPassword(); // 2) Using the MSAL Desktop app, make sure the ClientId matches the one used in integration testing. // 3) Do the interactive sign-in with the MSAL Desktop app with the username and password from step 1. // 4) After successful log-in, remove the password line you added in with step 1, and run the integration test again. }
private async Task RunB2CHappyPathTestAsync(LabResponse labResponse, string federationMetadata = "") { var factory = new HttpSnifferClientFactory(); var user = labResponse.User; SecureString securePassword = new NetworkCredential("", user.GetOrFetchPassword()).SecurePassword; var msalPublicClient = PublicClientApplicationBuilder .Create(labResponse.App.AppId) .WithB2CAuthority(B2CROPCAuthority) .WithTestLogging() .WithHttpClientFactory(factory) .Build(); AuthenticationResult authResult = await msalPublicClient .AcquireTokenByUsernamePassword(s_b2cScopes, labResponse.User.Upn, new NetworkCredential("", labResponse.User.GetOrFetchPassword()).SecurePassword) .WithCorrelationId(CorrelationId) .WithFederationMetadata(federationMetadata) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.IsNotNull(authResult); Assert.AreEqual(TokenSource.IdentityProvider, authResult.AuthenticationResultMetadata.TokenSource); Assert.IsNotNull(authResult.AccessToken); Assert.IsNotNull(authResult.IdToken); AssertCcsRoutingInformationIsNotSent(factory); // If test fails with "user needs to consent to the application, do an interactive request" error, // Do the following: // 1) Add in code to pull the user's password before creating the SecureString, and put a breakpoint there. // string password = ((LabUser)user).GetPassword(); // 2) Using the MSAL Desktop app, make sure the ClientId matches the one used in integration testing. // 3) Do the interactive sign-in with the MSAL Desktop app with the username and password from step 1. // 4) After successful log-in, remove the password line you added in with step 1, and run the integration test again. }
public async Task ValidateCcsHeadersForInteractiveAuthCodeFlowAsync() { HttpSnifferClientFactory factory = null; LabResponse labResponse = await LabUserHelper.GetDefaultUserAsync().ConfigureAwait(false); var pca = PublicClientApplicationBuilder .Create(labResponse.App.AppId) .WithDefaultRedirectUri() .WithRedirectUri(SeleniumWebUI.FindFreeLocalhostRedirectUri()) .WithTestLogging(out factory) .Build(); AuthenticationResult authResult = await pca .AcquireTokenInteractive(s_scopes) .WithPrompt(Prompt.SelectAccount) .WithCustomWebUi(CreateSeleniumCustomWebUI(labResponse.User, Prompt.SelectAccount)) .ExecuteAsync(new CancellationTokenSource(_interactiveAuthTimeout).Token) .ConfigureAwait(false); var CcsHeader = TestCommon.GetCcsHeaderFromSnifferFactory(factory); var userObjectId = labResponse.User.ObjectId; var userTenantID = labResponse.User.TenantId; Assert.AreEqual($"X-AnchorMailbox:Oid:{userObjectId}@{userTenantID}", $"{CcsHeader.Key}:{CcsHeader.Value.FirstOrDefault()}"); Assert.IsNotNull(authResult); Assert.IsNotNull(authResult.AccessToken); }
private void AssertExtraHTTPHeadersAreSent(HttpSnifferClientFactory factory) { var(req, res) = factory.RequestsAndResponses.Single(x => x.Item1.RequestUri.AbsoluteUri.Contains("oauth2/v2.0/token") && x.Item2.StatusCode == HttpStatusCode.OK); var ExtraHttpHeader = req.Headers.Single(h => h.Key == TestConstants.ExtraHttpHeader.Keys.FirstOrDefault()); Assert.AreEqual(TestConstants.ExtraHttpHeader.Keys.FirstOrDefault(), ExtraHttpHeader.Key); Assert.AreEqual(TestConstants.ExtraHttpHeader.Values.FirstOrDefault(), ExtraHttpHeader.Value.FirstOrDefault()); }
private async Task KerberosRunHappyPathTestAsync(LabResponse labResponse) { // Test with Id token var factory = new HttpSnifferClientFactory(); var idTokenPublicClient = PublicClientApplicationBuilder .Create(labResponse.App.AppId) .WithTestLogging() .WithHttpClientFactory(factory) .WithAuthority(labResponse.Lab.Authority, "organizations") .WithClientId(TestConstants.KerberosTestApplicationId) .WithKerberosTicketClaim(TestConstants.KerberosServicePrincipalName, KerberosTicketContainer.IdToken) .Build(); AuthenticationResult authResult = await GetAuthenticationResultWithAssertAsync( labResponse, factory, idTokenPublicClient, "", Guid.NewGuid()).ConfigureAwait(false); KerberosSupplementalTicket ticket = TestCommon.GetValidatedKerberosTicketFromAuthenticationResult( authResult, KerberosTicketContainer.IdToken, labResponse.User.Upn); Assert.IsNotNull(ticket); TestCommon.ValidateKerberosWindowsTicketCacheOperation(ticket); // Test with Access Token factory = new HttpSnifferClientFactory(); var accessTokenPublicClient = PublicClientApplicationBuilder .Create(labResponse.App.AppId) .WithTestLogging() .WithHttpClientFactory(factory) .WithAuthority(labResponse.Lab.Authority, "organizations") .WithClientId(TestConstants.KerberosTestApplicationId) .WithKerberosTicketClaim(TestConstants.KerberosServicePrincipalName, KerberosTicketContainer.AccessToken) .Build(); authResult = await GetAuthenticationResultWithAssertAsync( labResponse, factory, accessTokenPublicClient, "", Guid.NewGuid()).ConfigureAwait(false); ticket = TestCommon.GetValidatedKerberosTicketFromAuthenticationResult( authResult, KerberosTicketContainer.AccessToken, labResponse.User.Upn); Assert.IsNotNull(ticket); TestCommon.ValidateKerberosWindowsTicketCacheOperation(ticket); }
public async Task AcquireTokenToRegionalEndpointAsync() { // Arrange var factory = new HttpSnifferClientFactory(); _confidentialClientApplication = BuildCCA(factory); Environment.SetEnvironmentVariable(TestConstants.RegionName, TestConstants.Region); AuthenticationResult result = await GetAuthenticationResultAsync().ConfigureAwait(false); // regional endpoint AssertTokenSourceIsIdp(result); AssertValidHost(true, factory); AssertTelemetry(factory, $"{TelemetryConstants.HttpTelemetrySchemaVersion}|1004,{CacheInfoTelemetry.NoCachedAT:D}|centralus,1,0,,,0,1"); }
private void AssertExtraHTTPHeadersAreSent(HttpSnifferClientFactory factory) { //Validate CCS Routing header if (!factory.RequestsAndResponses.Any()) { return; } var(req, res) = factory.RequestsAndResponses.Single(x => x.Item1.RequestUri.AbsoluteUri.Contains("oauth2/v2.0/token") && x.Item2.StatusCode == HttpStatusCode.OK); Assert.IsTrue(req.Headers.TryGetValues(Constants.CcsRoutingHintHeader, out var values)); Assert.AreEqual("oid:597f86cd-13f3-44c0-bece-a1e77ba43228@f645ad92-e38d-4d1a-b510-d1b09a74a8ca", values.First()); }
private void AssertCcsRoutingInformationIsSent(HttpSnifferClientFactory factory, LabResponse labResponse) { if (labResponse.User.FederationProvider != FederationProvider.None) { return; } var CcsHeader = TestCommon.GetCcsHeaderFromSnifferFactory(factory); if (!String.IsNullOrEmpty(CcsHeader.Value?.FirstOrDefault())) { ValidateCcsHeader(CcsHeader, labResponse); } }
public async Task InvalidRegion_GoesToInvalidAuthority_Async() { // Arrange var factory = new HttpSnifferClientFactory(); var settings = ConfidentialAppSettings.GetSettings(Cloud.Public); _confidentialClientApplication = BuildCCA(settings, factory, true, "invalid"); Environment.SetEnvironmentVariable(TestConstants.RegionName, TestConstants.Region); var ex = await Assert.ThrowsExceptionAsync <HttpRequestException>( async() => await GetAuthenticationResultAsync(settings.AppScopes).ConfigureAwait(false)).ConfigureAwait(false); Assert.IsTrue(ex is HttpRequestException); }
private IConfidentialClientApplication BuildCCA(HttpSnifferClientFactory factory, string region = ConfidentialClientApplication.AttemptRegionDiscovery) { var builder = ConfidentialClientApplicationBuilder.Create(PublicCloudConfidentialClientID) .WithClientAssertion(GetSignedClientAssertionUsingMsalInternal(PublicCloudConfidentialClientID, GetClaims())) .WithAuthority(PublicCloudTestAuthority, false) .WithTestLogging() .WithExperimentalFeatures(true) .WithHttpClientFactory(factory); if (region != null) { builder.WithAzureRegion(region); } return(builder.Build()); }
private void AssertValidHost( bool isRegionalHost, HttpSnifferClientFactory factory, int placement = 0) { if (isRegionalHost) { var(req, res) = factory.RequestsAndResponses.Skip(placement).Single(x => x.Item1.RequestUri.Host == RegionalHost && x.Item2.StatusCode == HttpStatusCode.OK); Assert.AreEqual(RegionalHost, req.RequestUri.Host); } else { var(req, res) = factory.RequestsAndResponses.Skip(placement).Single(x => x.Item1.RequestUri.Host == GlobalHost && x.Item2.StatusCode == HttpStatusCode.OK); Assert.AreEqual(GlobalHost, req.RequestUri.Host); } }
public async Task AcquireTokenToRegionalEndpointAsync() { // Arrange var factory = new HttpSnifferClientFactory(); var settings = ConfidentialAppSettings.GetSettings(Cloud.Public); _confidentialClientApplication = BuildCCA(settings, factory); Environment.SetEnvironmentVariable(TestConstants.RegionName, TestConstants.Region); AuthenticationResult result = await GetAuthenticationResultAsync(settings.AppScopes).ConfigureAwait(false); // regional endpoint AssertTokenSourceIsIdp(result); AssertValidHost(true, factory); AssertTelemetry(factory, $"{TelemetryConstants.HttpTelemetrySchemaVersion}|1004,{CacheRefreshReason.NoCachedAccessToken:D},centralus,3,4|0,1"); Assert.AreEqual( $"https://centralus.r.login.microsoftonline.com/{settings.TenantId}/oauth2/v2.0/token", result.AuthenticationResultMetadata.TokenEndpoint); }
private void AssertTelemetryHeaders(HttpSnifferClientFactory factory, bool IsFailure, LabResponse labResponse) { var(req, res) = factory.RequestsAndResponses.Single(x => x.Item1.RequestUri.AbsoluteUri == labResponse.Lab.Authority + "organizations/oauth2/v2.0/token" && x.Item2.StatusCode == HttpStatusCode.OK); var telemetryLastValue = req.Headers.Single(h => h.Key == TelemetryConstants.XClientLastTelemetry).Value; var telemetryCurrentValue = req.Headers.Single(h => h.Key == TelemetryConstants.XClientCurrentTelemetry).Value; HttpTelemetryRecorder httpTelemetryRecorder = new HttpTelemetryRecorder(); string csvCurrent = telemetryCurrentValue.FirstOrDefault(); string csvPrevious = telemetryLastValue.FirstOrDefault(); if (!IsFailure) { Assert.AreEqual(XClientCurrentTelemetryROPC, csvCurrent); Assert.AreEqual(XClientLastTelemetryROPC, csvPrevious); httpTelemetryRecorder.SplitCurrentCsv(csvCurrent); httpTelemetryRecorder.CheckSchemaVersion(csvCurrent); Assert.AreEqual(UPApiId, httpTelemetryRecorder.ApiId.FirstOrDefault(e => e.Contains(UPApiId))); Assert.AreEqual(TelemetryConstants.Zero, httpTelemetryRecorder.ForceRefresh); Assert.AreEqual(XClientLastTelemetryROPC, csvPrevious); } else { Assert.AreEqual(XClientCurrentTelemetryROPCFailure, csvCurrent); Assert.AreEqual(XClientLastTelemetryROPCFailure, csvPrevious); httpTelemetryRecorder.CheckSchemaVersion(csvCurrent); httpTelemetryRecorder.CheckSchemaVersion(csvPrevious); httpTelemetryRecorder.SplitCurrentCsv(csvCurrent); httpTelemetryRecorder.SplitPreviousCsv(csvPrevious); Assert.AreEqual(UPApiId, httpTelemetryRecorder.ApiId.FirstOrDefault(e => e.Contains(UPApiId))); Assert.AreEqual(1, httpTelemetryRecorder.ErrorCode.Count()); Assert.AreEqual(TelemetryConstants.Zero, httpTelemetryRecorder.SilentCallSuccessfulCount); Assert.AreEqual(TelemetryConstants.Zero, httpTelemetryRecorder.ForceRefresh); Assert.AreEqual(ApiIdAndCorrelationIdSection, httpTelemetryRecorder.ApiIdAndCorrelationIds.FirstOrDefault()); Assert.AreEqual(InvalidGrantError, httpTelemetryRecorder.ErrorCode.FirstOrDefault()); } }
private async Task RunHappyPathTestAsync(LabResponse labResponse, string federationMetadata = "") { var factory = new HttpSnifferClientFactory(); var msalPublicClient = PublicClientApplicationBuilder .Create(labResponse.App.AppId) .WithTestLogging() .WithHttpClientFactory(factory) .WithAuthority(labResponse.Lab.Authority, "organizations") .Build(); AuthenticationResult authResult = await GetAuthenticationResultWithAssertAsync( labResponse, factory, msalPublicClient, federationMetadata, CorrelationId).ConfigureAwait(false); if (AuthorityInfo.FromAuthorityUri(labResponse.Lab.Authority + "/" + labResponse.Lab.TenantId, false).AuthorityType == AuthorityType.Aad) { AssertTenantProfiles(authResult.Account.GetTenantProfiles(), authResult.TenantId); } else { Assert.IsNull(authResult.Account.GetTenantProfiles()); } TestCommon.ValidateNoKerberosTicketFromAuthenticationResult(authResult); // If test fails with "user needs to consent to the application, do an interactive request" error, // Do the following: // 1) Add in code to pull the user's password before creating the SecureString, and put a breakpoint there. // string password = ((LabUser)user).GetPassword(); // 2) Using the MSAL Desktop app, make sure the ClientId matches the one used in integration testing. // 3) Do the interactive sign-in with the MSAL Desktop app with the username and password from step 1. // 4) After successful log-in, remove the password line you added in with step 1, and run the integration test again. }
private async Task <AuthenticationResult> GetAuthenticationResultWithAssertAsync( LabResponse labResponse, HttpSnifferClientFactory factory, IPublicClientApplication msalPublicClient, string federationMetadata, Guid testCorrelationId) { AuthenticationResult authResult = await msalPublicClient .AcquireTokenByUsernamePassword(s_scopes, labResponse.User.Upn, new NetworkCredential("", labResponse.User.GetOrFetchPassword()).SecurePassword) .WithCorrelationId(testCorrelationId) .WithFederationMetadata(federationMetadata) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.IsNotNull(authResult); Assert.AreEqual(TokenSource.IdentityProvider, authResult.AuthenticationResultMetadata.TokenSource); Assert.IsNotNull(authResult.AccessToken); Assert.IsNotNull(authResult.IdToken); Assert.IsTrue(string.Equals(labResponse.User.Upn, authResult.Account.Username, StringComparison.InvariantCultureIgnoreCase)); AssertTelemetryHeaders(factory, false, labResponse); AssertCcsRoutingInformationIsSent(factory, labResponse); return(authResult); }
private void AssertCcsRoutingInformationIsSent(HttpSnifferClientFactory factory, LabResponse labResponse) { var CcsHeader = TestCommon.GetCcsHeaderFromSnifferFactory(factory); Assert.AreEqual($"X-AnchorMailbox:UPN:{labResponse.User.Upn}", $"{CcsHeader.Key}:{CcsHeader.Value.FirstOrDefault()}"); }
//Since this test performs a large number of operations it should not be rerun on other clouds. private async Task RunOnBehalfOfTestWithTokenCacheAsync(LabResponse labResponse) { LabUser user = labResponse.User; string oboHost; string secret; string authority; string publicClientID; string confidentialClientID; string[] oboScope; oboHost = PublicCloudHost; secret = _keyVault.GetSecret(TestConstants.MsalOBOKeyVaultUri).Value; authority = TestConstants.AuthorityOrganizationsTenant; publicClientID = PublicCloudPublicClientIDOBO; confidentialClientID = PublicCloudConfidentialClientIDOBO; oboScope = s_publicCloudOBOServiceScope; //TODO: acquire scenario specific client ids from the lab response SecureString securePassword = new NetworkCredential("", user.GetOrFetchPassword()).SecurePassword; var factory = new HttpSnifferClientFactory(); var msalPublicClient = PublicClientApplicationBuilder.Create(publicClientID) .WithAuthority(authority) .WithRedirectUri(TestConstants.RedirectUri) .WithTestLogging() .WithHttpClientFactory(factory) .Build(); var authResult = await msalPublicClient.AcquireTokenByUsernamePassword(oboScope, user.Upn, securePassword) .ExecuteAsync() .ConfigureAwait(false); var confidentialApp = ConfidentialClientApplicationBuilder .Create(confidentialClientID) .WithAuthority(new Uri(oboHost + authResult.TenantId), true) .WithClientSecret(secret) .WithTestLogging() .BuildConcrete(); var userCacheRecorder = confidentialApp.UserTokenCache.RecordAccess(); UserAssertion userAssertion = new UserAssertion(authResult.AccessToken); string atHash = userAssertion.AssertionHash; authResult = await confidentialApp.AcquireTokenOnBehalfOf(s_scopes, userAssertion) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); MsalAssert.AssertAuthResult(authResult, user); Assert.AreEqual(atHash, userCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheKey); Assert.AreEqual(TokenSource.IdentityProvider, authResult.AuthenticationResultMetadata.TokenSource); //Run OBO again. Should get token from cache authResult = await confidentialApp.AcquireTokenOnBehalfOf(s_scopes, userAssertion) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.IsNotNull(authResult); Assert.IsNotNull(authResult.AccessToken); Assert.IsNotNull(authResult.IdToken); Assert.IsTrue(!userCacheRecorder.LastAfterAccessNotificationArgs.IsApplicationCache); Assert.IsTrue(userCacheRecorder.LastAfterAccessNotificationArgs.HasTokens); Assert.AreEqual(atHash, userCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheKey); Assert.AreEqual(TokenSource.Cache, authResult.AuthenticationResultMetadata.TokenSource); //Expire access tokens TokenCacheHelper.ExpireAllAccessTokens(confidentialApp.UserTokenCacheInternal); //Run OBO again. Should do OBO flow since the AT is expired and RTs aren't cached for normal OBO flow authResult = await confidentialApp.AcquireTokenOnBehalfOf(s_scopes, userAssertion) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.IsNotNull(authResult); Assert.IsNotNull(authResult.AccessToken); Assert.IsNotNull(authResult.IdToken); Assert.IsTrue(!userCacheRecorder.LastAfterAccessNotificationArgs.IsApplicationCache); Assert.IsTrue(userCacheRecorder.LastAfterAccessNotificationArgs.HasTokens); Assert.AreEqual(atHash, userCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheKey); Assert.AreEqual(TokenSource.IdentityProvider, authResult.AuthenticationResultMetadata.TokenSource); AssertLastHttpContent("on_behalf_of"); //creating second app with no refresh tokens var atItems = confidentialApp.UserTokenCacheInternal.Accessor.GetAllAccessTokens(); var confidentialApp2 = ConfidentialClientApplicationBuilder .Create(confidentialClientID) .WithAuthority(new Uri(oboHost + authResult.TenantId), true) .WithClientSecret(secret) .WithTestLogging() .WithHttpClientFactory(factory) .BuildConcrete(); TokenCacheHelper.ExpireAccessToken(confidentialApp2.UserTokenCacheInternal, atItems.FirstOrDefault()); //Should perform OBO flow since the access token is expired and the refresh token does not exist authResult = await confidentialApp2.AcquireTokenOnBehalfOf(s_scopes, userAssertion) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.IsNotNull(authResult); Assert.IsNotNull(authResult.AccessToken); Assert.IsNotNull(authResult.IdToken); Assert.IsTrue(!userCacheRecorder.LastAfterAccessNotificationArgs.IsApplicationCache); Assert.IsTrue(userCacheRecorder.LastAfterAccessNotificationArgs.HasTokens); Assert.AreEqual(atHash, userCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheKey); Assert.AreEqual(TokenSource.IdentityProvider, authResult.AuthenticationResultMetadata.TokenSource); AssertLastHttpContent("on_behalf_of"); TokenCacheHelper.ExpireAllAccessTokens(confidentialApp2.UserTokenCacheInternal); TokenCacheHelper.UpdateUserAssertions(confidentialApp2); //Should perform OBO flow since the access token and the refresh token contains the wrong user assertion hash authResult = await confidentialApp2.AcquireTokenOnBehalfOf(s_scopes, userAssertion) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.IsNotNull(authResult); Assert.IsNotNull(authResult.AccessToken); Assert.IsNotNull(authResult.IdToken); Assert.IsTrue(!userCacheRecorder.LastAfterAccessNotificationArgs.IsApplicationCache); Assert.IsTrue(userCacheRecorder.LastAfterAccessNotificationArgs.HasTokens); Assert.AreEqual(atHash, userCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheKey); Assert.AreEqual(TokenSource.IdentityProvider, authResult.AuthenticationResultMetadata.TokenSource); AssertLastHttpContent("on_behalf_of"); }
private async Task <AuthenticationResult> RunTestForUserAsync(LabResponse labResponse, bool directToAdfs = false) { HttpSnifferClientFactory factory = null; IPublicClientApplication pca; if (directToAdfs) { pca = PublicClientApplicationBuilder .Create(Adfs2019LabConstants.PublicClientId) .WithRedirectUri(Adfs2019LabConstants.ClientRedirectUri) .WithAdfsAuthority(Adfs2019LabConstants.Authority) .WithTestLogging() .Build(); } else { pca = PublicClientApplicationBuilder .Create(labResponse.App.AppId) .WithRedirectUri(SeleniumWebUI.FindFreeLocalhostRedirectUri()) .WithAuthority(labResponse.Lab.Authority + "common") .WithTestLogging(out factory) .Build(); } var userCacheAccess = pca.UserTokenCache.RecordAccess(); Trace.WriteLine("Part 1 - Acquire a token interactively, no login hint"); AuthenticationResult result = await pca .AcquireTokenInteractive(s_scopes) .WithCustomWebUi(CreateSeleniumCustomWebUI(labResponse.User, Prompt.SelectAccount, false, directToAdfs)) .ExecuteAsync(new CancellationTokenSource(_interactiveAuthTimeout).Token) .ConfigureAwait(false); Assert.IsTrue(result.AuthenticationResultMetadata.DurationTotalInMs > 0); Assert.IsTrue(result.AuthenticationResultMetadata.DurationInHttpInMs > 0); userCacheAccess.AssertAccessCounts(0, 1); IAccount account = await MsalAssert.AssertSingleAccountAsync(labResponse, pca, result).ConfigureAwait(false); userCacheAccess.AssertAccessCounts(1, 1); // the assert calls GetAccounts Assert.IsFalse(userCacheAccess.LastAfterAccessNotificationArgs.IsApplicationCache); Trace.WriteLine("Part 2 - Clear the cache"); await pca.RemoveAsync(account).ConfigureAwait(false); userCacheAccess.AssertAccessCounts(1, 2); Assert.IsFalse((await pca.GetAccountsAsync().ConfigureAwait(false)).Any()); userCacheAccess.AssertAccessCounts(2, 2); Assert.IsFalse(userCacheAccess.LastAfterAccessNotificationArgs.IsApplicationCache); if (factory?.RequestsAndResponses != null) { factory.RequestsAndResponses.Clear(); } Trace.WriteLine("Part 3 - Acquire a token interactively again, with login hint"); result = await pca .AcquireTokenInteractive(s_scopes) .WithCustomWebUi(CreateSeleniumCustomWebUI(labResponse.User, Prompt.ForceLogin, true, directToAdfs)) .WithPrompt(Prompt.ForceLogin) .WithLoginHint(labResponse.User.Upn) .ExecuteAsync(new CancellationTokenSource(_interactiveAuthTimeout).Token) .ConfigureAwait(false); userCacheAccess.AssertAccessCounts(2, 3); AssertCcsRoutingInformationIsSent(factory, labResponse); account = await MsalAssert.AssertSingleAccountAsync(labResponse, pca, result).ConfigureAwait(false); userCacheAccess.AssertAccessCounts(3, 3); Assert.IsFalse(userCacheAccess.LastAfterAccessNotificationArgs.IsApplicationCache); if (factory?.RequestsAndResponses != null) { factory.RequestsAndResponses.Clear(); } Trace.WriteLine("Part 4 - Acquire a token silently"); result = await pca .AcquireTokenSilent(s_scopes, account) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Trace.WriteLine("Part 5 - Acquire a token silently with force refresh"); result = await pca .AcquireTokenSilent(s_scopes, account) .WithForceRefresh(true) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); await MsalAssert.AssertSingleAccountAsync(labResponse, pca, result).ConfigureAwait(false); Assert.IsFalse(userCacheAccess.LastAfterAccessNotificationArgs.IsApplicationCache); AssertCcsRoutingInformationIsSent(factory, labResponse); return(result); }
private void AssertTelemetry(HttpSnifferClientFactory factory, string currentTelemetryHeader, int placement = 0) { var(req, res) = factory.RequestsAndResponses.Skip(placement).Single(); Assert.AreEqual(currentTelemetryHeader, req.Headers.GetValues("x-client-current-telemetry").First()); }