public void VerifyAuthorizationPendingErrorDoesNotLogError() { // When calling DeviceCodeFlow, we poll for the authorization and if the user hasn't entered the code in yet // then we receive an error for authorization_pending. This is thrown as an exception and logged as // errors. This error is noisy and so it should be suppressed for this one case. // This test verifies that the error for authorization_pending is not logged as an error. var logCallbacks = new List <_LogData>(); using (var harness = new MockHttpAndServiceBundle(logCallback: (level, message, pii) => { if (level == LogLevel.Error) { Assert.Fail( "Received an error message {0} and the stack trace is {1}", message, new StackTrace(true)); } logCallbacks.Add( new _LogData { Level = level, Message = message, IsPii = pii }); })) { const int NumberOfAuthorizationPendingRequestsToInject = 2; var parameters = CreateAuthenticationParametersAndSetupMocks( harness, NumberOfAuthorizationPendingRequestsToInject, out HashSet <string> expectedScopes); var deviceCodeParameters = new AcquireTokenWithDeviceCodeParameters { DeviceCodeResultCallback = result => Task.FromResult(0) }; var request = new DeviceCodeRequest(harness.ServiceBundle, parameters, deviceCodeParameters); Task <AuthenticationResult> task = request.RunAsync(CancellationToken.None); task.Wait(); // Ensure we got logs so the log callback is working. Assert.IsTrue(logCallbacks.Count > 0, "There should be data in logCallbacks"); // Ensure we have authorization_pending data in the logs List <_LogData> authPendingLogs = logCallbacks.Where(x => x.Message.Contains(OAuth2Error.AuthorizationPending)).ToList(); Assert.AreEqual(2, authPendingLogs.Count, "authorization_pending logs should exist"); // Ensure the authorization_pending logs are Info level and not Error Assert.AreEqual( 2, authPendingLogs.Where(x => x.Level == LogLevel.Info).ToList().Count, "authorization_pending logs should be INFO"); // Ensure we don't have Error level logs in this scenario. string errorLogs = string.Join( "--", logCallbacks .Where(x => x.Level == LogLevel.Error) .Select(x => x.Message) .ToArray()); Assert.IsFalse( logCallbacks.Any(x => x.Level == LogLevel.Error), "Error level logs should not exist but got: " + errorLogs); } }
private AuthenticationRequestParameters CreateAuthenticationParametersAndSetupMocks( MockHttpAndServiceBundle harness, int numAuthorizationPendingResults, out HashSet <string> expectedScopes) { var cache = new TokenCache(harness.ServiceBundle); var parameters = harness.CreateAuthenticationRequestParameters( MsalTestConstants.AuthorityHomeTenant, null, cache, null, extraQueryParameters: MsalTestConstants.ExtraQueryParams, claims: MsalTestConstants.Claims); TestCommon.MockInstanceDiscoveryAndOpenIdRequest(harness.HttpManager); expectedScopes = new HashSet <string>(); expectedScopes.UnionWith(MsalTestConstants.Scope); expectedScopes.Add(OAuth2Value.ScopeOfflineAccess); expectedScopes.Add(OAuth2Value.ScopeProfile); expectedScopes.Add(OAuth2Value.ScopeOpenId); IDictionary <string, string> extraQueryParamsAndClaims = MsalTestConstants.ExtraQueryParams.ToDictionary(e => e.Key, e => e.Value); extraQueryParamsAndClaims.Add(OAuth2Parameter.Claims, MsalTestConstants.Claims); // Mock Handler for device code request harness.HttpManager.AddMockHandler( new MockHttpMessageHandler { ExpectedMethod = HttpMethod.Post, ExpectedPostData = new Dictionary <string, string>() { { OAuth2Parameter.ClientId, MsalTestConstants.ClientId }, { OAuth2Parameter.Scope, expectedScopes.AsSingleString() } }, ResponseMessage = CreateDeviceCodeResponseSuccessMessage(), ExpectedQueryParams = extraQueryParamsAndClaims }); for (int i = 0; i < numAuthorizationPendingResults; i++) { harness.HttpManager.AddMockHandler( new MockHttpMessageHandler { ExpectedMethod = HttpMethod.Post, ExpectedUrl = "https://login.microsoftonline.com/home/oauth2/v2.0/token", ResponseMessage = MockHelpers.CreateFailureMessage( HttpStatusCode.Forbidden, "{\"error\":\"authorization_pending\"," + "\"error_description\":\"AADSTS70016: Pending end-user authorization." + "\\r\\nTrace ID: f6c2c73f-a21d-474e-a71f-d8b121a58205\\r\\nCorrelation ID: " + "36fe3e82-442f-4418-b9f4-9f4b9295831d\\r\\nTimestamp: 2015-09-24 19:51:51Z\"," + "\"error_codes\":[70016],\"timestamp\":\"2015-09-24 19:51:51Z\",\"trace_id\":" + "\"f6c2c73f-a21d-474e-a71f-d8b121a58205\",\"correlation_id\":" + "\"36fe3e82-442f-4418-b9f4-9f4b9295831d\"}") }); } if (numAuthorizationPendingResults > 0) { // Mock Handler for devicecode->token exchange request harness.HttpManager.AddMockHandler( new MockHttpMessageHandler { ExpectedMethod = HttpMethod.Post, ExpectedPostData = new Dictionary <string, string>() { { OAuth2Parameter.ClientId, MsalTestConstants.ClientId }, { OAuth2Parameter.Scope, expectedScopes.AsSingleString() } }, ResponseMessage = MockHelpers.CreateSuccessTokenResponseMessage() }); } return(parameters); }
private (Client.Internal.Requests.AuthenticationRequestParameters requestParams, IWebTokenRequestResultWrapper webTokenResponseWrapper) SetupSilentCall(MockHttpAndServiceBundle harness) { var wamAccountProvider = new WebAccountProvider("id", "*****@*****.**", null); var requestParams = harness.CreateAuthenticationRequestParameters( TestConstants.AuthorityHomeTenant); requestParams.Account = new Account( $"{TestConstants.Uid}.{TestConstants.Utid}", TestConstants.DisplayableId, null, new Dictionary <string, string>() { { TestConstants.ClientId, "wam_id_1" } }); // account has wam_id! var webAccount = new WebAccount(wamAccountProvider, "*****@*****.**", WebAccountState.Connected); var webTokenRequest = new WebTokenRequest(wamAccountProvider); IWebTokenRequestResultWrapper webTokenResponseWrapper = Substitute.For <IWebTokenRequestResultWrapper>(); _webAccountProviderFactory.GetAccountProviderAsync(null).ReturnsForAnyArgs(Task.FromResult(wamAccountProvider)); _wamProxy.FindAccountAsync(Arg.Any <WebAccountProvider>(), "wam_id_1").Returns(Task.FromResult(webAccount)); _aadPlugin.CreateWebTokenRequestAsync( wamAccountProvider, requestParams, isForceLoginPrompt: false, isAccountInWam: true, isInteractive: false) .Returns(Task.FromResult(webTokenRequest)); _wamProxy.GetTokenSilentlyAsync(webAccount, webTokenRequest). Returns(Task.FromResult(webTokenResponseWrapper)); return(requestParams, webTokenResponseWrapper); }
public MockHttpTestHarness(string authorityUri) { _mockHttpAndServiceBundle = new MockHttpAndServiceBundle(); Authority = Authority.CreateAuthority(ServiceBundle, authorityUri); Cache = new TokenCache(ServiceBundle); }
public async Task AcquireTokenSilent_ValidATs_ParallelRequests_Async() { // Arrange const int NumberOfRequests = 10; using (MockHttpAndServiceBundle harness = CreateTestHarness()) { PublicClientApplication pca = PublicClientApplicationBuilder .Create(TestConstants.ClientId) .WithHttpManager(harness.HttpManager) .BuildConcrete(); ConfigureCacheSerialization(pca); var actualUsers = new List <string>(); for (int i = 1; i <= NumberOfRequests; i++) { var user = GetUpn(i); _tokenCacheHelper.PopulateCache(pca.UserTokenCacheInternal.Accessor, GetUid(i), "utid", displayableId: user); actualUsers.Add(user); } byte[] bytes = (pca.UserTokenCacheInternal as ITokenCacheSerializer).SerializeMsalV3(); _inMemoryCache = Encoding.UTF8.GetString(bytes); pca.UserTokenCacheInternal.Accessor.AssertItemCount( expectedAtCount: NumberOfRequests * 2, expectedRtCount: NumberOfRequests, expectedAccountCount: NumberOfRequests, expectedIdtCount: NumberOfRequests, expectedAppMetadataCount: 1); IEnumerable <IAccount> accounts = await pca.GetAccountsAsync().ConfigureAwait(false); AuthenticationResult[] results; // Act using (new PerformanceValidator( NumberOfRequests * (2 * CacheAccessPenaltyMs + 100), "AcquireTokenSilent should take roughly 100ms for its internal logic, " + "plus the time needed to access the cache and all the thread context switches")) { // execute won't start here because IEnumerable is lazy IEnumerable <Task <AuthenticationResult> > tasks = accounts.Select(acc => pca.AcquireTokenSilent(TestConstants.s_scope, acc).ExecuteAsync()); // execution starts here Task <AuthenticationResult>[] taskArray = tasks.ToArray(); // wait for all requests to complete results = await Task.WhenAll(taskArray).ConfigureAwait(false); } // Assert CollectionAssert.AreEquivalent( actualUsers, results.Select(r => r.Account.Username).ToArray()); // Expecting the number of cache accesses to be equal to the number of request plus one for GetAccounts Assert.AreEqual(NumberOfRequests + 1, _afterAccessCalls); Assert.AreEqual(NumberOfRequests + 1, _beforeAccessCalls); // Acquire token silent with valid ATs in the cache -> no data is written Assert.AreEqual(0, _beforeWriteCalls); Assert.AreEqual(0, _afterAccessWriteCalls); } }
public async Task ATS_AccountWithWamId_Async() { // Arrange using (MockHttpAndServiceBundle harness = CreateTestHarness()) { _webAccountProviderFactory.ClearReceivedCalls(); var wamAccountProvider = new WebAccountProvider("id", "*****@*****.**", null); var extraQP = new Dictionary <string, string>() { { "extraQp1", "extraVal1" }, { "instance_aware", "true" } }; var requestParams = harness.CreateAuthenticationRequestParameters( TestConstants.AuthorityHomeTenant, extraQueryParameters: extraQP, validateAuthority: true); // AAD requestParams.UserConfiguredAuthority = Authority.CreateAuthority("https://login.microsoftonline.com/organizations"); requestParams.Account = new Account( $"{TestConstants.Uid}.{TestConstants.Utid}", TestConstants.DisplayableId, null, new Dictionary <string, string>() { { TestConstants.ClientId, "wam_id_1" } }); // account has wam_id! var webAccount = new WebAccount(wamAccountProvider, "*****@*****.**", WebAccountState.Connected); var webTokenRequest = new WebTokenRequest(wamAccountProvider); var webTokenResponseWrapper = Substitute.For <IWebTokenRequestResultWrapper>(); webTokenResponseWrapper.ResponseStatus.Returns(WebTokenRequestStatus.Success); var webTokenResponse = new WebTokenResponse(); webTokenResponseWrapper.ResponseData.Returns(new List <WebTokenResponse>() { webTokenResponse }); _webAccountProviderFactory.GetAccountProviderAsync(null).ReturnsForAnyArgs(Task.FromResult(wamAccountProvider)); _wamProxy.FindAccountAsync(Arg.Any <WebAccountProvider>(), "wam_id_1").Returns(Task.FromResult(webAccount)); _aadPlugin.CreateWebTokenRequestAsync( wamAccountProvider, requestParams, isForceLoginPrompt: false, isAccountInWam: true, isInteractive: false) .Returns(Task.FromResult(webTokenRequest)); var atsParams = new AcquireTokenSilentParameters(); _wamProxy.GetTokenSilentlyAsync(webAccount, webTokenRequest). Returns(Task.FromResult(webTokenResponseWrapper)); _aadPlugin.ParseSuccessfullWamResponse(webTokenResponse, out _).Returns(_msalTokenResponse); // Act var result = await _wamBroker.AcquireTokenSilentAsync(requestParams, atsParams).ConfigureAwait(false); // Assert Assert.AreSame(_msalTokenResponse, result); Assert.AreEqual("yes", webTokenRequest.Properties["validateAuthority"]); Assert.AreEqual("extraVal1", webTokenRequest.Properties["extraQp1"]); // Although at the time of writing, MSAL does not support instance aware ... // WAM does support it but the param is different - discovery=home Assert.AreEqual("home", webTokenRequest.Properties["discover"]); Assert.AreEqual("https://login.microsoftonline.com/organizations/", webTokenRequest.Properties["authority"]); } }
public async Task TelemetryAcceptanceTestAsync() { using (_harness = CreateTestHarness()) { _harness.HttpManager.AddInstanceDiscoveryMockHandler(); _app = PublicClientApplicationBuilder.Create(TestConstants.ClientId) .WithHttpManager(_harness.HttpManager) .WithDefaultRedirectUri() .WithLogging((lvl, msg, pii) => Trace.WriteLine($"[MSAL_LOG][{lvl}] {msg}")) .BuildConcrete(); Trace.WriteLine("Step 1. Acquire Token Interactive successful"); var result = await RunAcquireTokenInteractiveAsync(AcquireTokenInteractiveOutcome.Success).ConfigureAwait(false); AssertCurrentTelemetry(result.HttpRequest, ApiIds.AcquireTokenInteractive, CacheRefreshReason.NotApplicable); AssertPreviousTelemetry(result.HttpRequest, expectedSilentCount: 0); Trace.WriteLine("Step 2. Acquire Token Silent successful - AT served from cache"); result = await RunAcquireTokenSilentAsync(AcquireTokenSilentOutcome.SuccessFromCache).ConfigureAwait(false); Assert.IsNull(result.HttpRequest, "No calls are made to the token endpoint"); Trace.WriteLine("Step 3. Acquire Token Silent successful - via refresh_token flow"); result = await RunAcquireTokenSilentAsync(AcquireTokenSilentOutcome.SuccessViaRefreshGrant).ConfigureAwait(false); AssertCurrentTelemetry(result.HttpRequest, ApiIds.AcquireTokenSilent, CacheRefreshReason.NoCachedAccessToken); AssertPreviousTelemetry(result.HttpRequest, expectedSilentCount: 1); Trace.WriteLine("Step 4. Acquire Token Silent with force_refresh = true and failure = invalid_grant"); result = await RunAcquireTokenSilentAsync(AcquireTokenSilentOutcome.FailInvalidGrant, forceRefresh : true).ConfigureAwait(false); AssertCurrentTelemetry(result.HttpRequest, ApiIds.AcquireTokenSilent, CacheRefreshReason.ForceRefreshOrClaims); AssertPreviousTelemetry(result.HttpRequest, expectedSilentCount: 0); // invalid grant error puts MSAL in a throttled state - simulate some time passing for this _harness.ServiceBundle.ThrottlingManager.SimulateTimePassing( UiRequiredProvider.s_uiRequiredExpiration.Add(TimeSpan.FromSeconds(1))); Guid step4Correlationid = result.Correlationid; Trace.WriteLine("Step 5. Acquire Token Silent with force_refresh = true and failure = interaction_required"); result = await RunAcquireTokenSilentAsync(AcquireTokenSilentOutcome.FailInteractionRequired, forceRefresh : true).ConfigureAwait(false); AssertCurrentTelemetry(result.HttpRequest, ApiIds.AcquireTokenSilent, CacheRefreshReason.ForceRefreshOrClaims); AssertPreviousTelemetry( result.HttpRequest, expectedSilentCount: 0, expectedFailedApiIds: new[] { ApiIds.AcquireTokenSilent }, // from step 4 expectedCorrelationIds: new[] { step4Correlationid }, expectedErrors: new[] { "invalid_grant" }); Guid step5CorrelationId = result.Correlationid; Trace.WriteLine("Step 6. Acquire Token Interactive - some /authorization error -> token endpoint not hit"); result = await RunAcquireTokenInteractiveAsync(AcquireTokenInteractiveOutcome.AuthorizationError).ConfigureAwait(false); Assert.IsNull(result.HttpRequest, "No calls are made to the token endpoint"); Guid step6CorrelationId = result.Correlationid; Trace.WriteLine("Step 7. Acquire Token Interactive -> HTTP 5xx error (i.e. AAD is down)"); result = await RunAcquireTokenInteractiveAsync(AcquireTokenInteractiveOutcome.AADUnavailableError).ConfigureAwait(false); Guid step7CorrelationId = result.Correlationid; // we can assert telemetry here, as it will be sent to AAD. However, AAD is down, so it will not record it. AssertCurrentTelemetry(result.HttpRequest, ApiIds.AcquireTokenInteractive, CacheRefreshReason.NotApplicable); AssertPreviousTelemetry( result.HttpRequest, expectedSilentCount: 0, expectedFailedApiIds: new[] { ApiIds.AcquireTokenSilent, ApiIds.AcquireTokenInteractive }, expectedCorrelationIds: new[] { step5CorrelationId, step6CorrelationId }, expectedErrors: new[] { "interaction_required", "user_cancelled" }); // the 5xx error puts MSAL in a throttling state, so "wait" until this clears _harness.ServiceBundle.ThrottlingManager.SimulateTimePassing( HttpStatusProvider.s_throttleDuration.Add(TimeSpan.FromSeconds(1))); Trace.WriteLine("Step 8. Acquire Token Interactive -> Success"); result = await RunAcquireTokenInteractiveAsync(AcquireTokenInteractiveOutcome.Success).ConfigureAwait(false); AssertCurrentTelemetry(result.HttpRequest, ApiIds.AcquireTokenInteractive, CacheRefreshReason.NotApplicable); AssertPreviousTelemetry( result.HttpRequest, expectedSilentCount: 0, expectedFailedApiIds: new[] { ApiIds.AcquireTokenSilent, ApiIds.AcquireTokenInteractive, ApiIds.AcquireTokenInteractive }, expectedCorrelationIds: new[] { step5CorrelationId, step6CorrelationId, step7CorrelationId }, expectedErrors: new[] { "interaction_required", "user_cancelled", "service_not_available" }); Trace.WriteLine("Step 9. Acquire Token Silent with force-refresh false -> successful"); result = await RunAcquireTokenSilentAsync(AcquireTokenSilentOutcome.SuccessViaRefreshGrant, false).ConfigureAwait(false); AssertCurrentTelemetry(result.HttpRequest, ApiIds.AcquireTokenSilent, CacheRefreshReason.NoCachedAccessToken); AssertPreviousTelemetry(result.HttpRequest, expectedSilentCount: 0); } }
public void UnifiedCache_MsalStoresToAndReadRtFromAdalCache() { using (var mocks = new MockHttpAndServiceBundle()) { var httpManager = mocks.HttpManager; httpManager.AddInstanceDiscoveryMockHandler(); var app = PublicClientApplicationBuilder.Create(TestConstants.ClientId) .WithAuthority(new Uri(ClientApplicationBase.DefaultAuthority), true) .WithHttpManager(httpManager) .WithTelemetry(new TraceTelemetryConfig()) .WithUserTokenLegacyCachePersistenceForTest( new TestLegacyCachePersistance()) .BuildConcrete(); MsalMockHelpers.ConfigureMockWebUI( app.ServiceBundle.PlatformProxy, AuthorizationResult.FromUri(app.AppConfig.RedirectUri + "?code=some-code")); HttpResponseMessage response = MockHelpers.CreateSuccessTokenResponseMessageWithUid( TestConstants.Uid, TestConstants.Utid, "*****@*****.**"); httpManager.AddResponseMockHandlerForPost(response); AuthenticationResult result = app.AcquireTokenInteractive(TestConstants.s_scope).ExecuteAsync(CancellationToken.None).Result; Assert.IsNotNull(result); // make sure Msal stored RT in Adal cache IDictionary <AdalTokenCacheKey, AdalResultWrapper> adalCacheDictionary = AdalCacheOperations.Deserialize(app.ServiceBundle.DefaultLogger, app.UserTokenCacheInternal.LegacyPersistence.LoadCache()); Assert.IsTrue(adalCacheDictionary.Count == 1); var requestContext = new RequestContext(app.ServiceBundle, Guid.NewGuid()); AuthenticationRequestParameters reqParams = new AuthenticationRequestParameters( mocks.ServiceBundle, app.UserTokenCacheInternal, new Client.ApiConfig.Parameters.AcquireTokenCommonParameters(), requestContext); var accounts = app.UserTokenCacheInternal.GetAccountsAsync(reqParams).Result; foreach (IAccount account in accounts) { app.UserTokenCacheInternal.RemoveMsalAccountWithNoLocks(account, requestContext); } Assert.AreEqual(0, httpManager.QueueSize); httpManager.AddMockHandler( new MockHttpMessageHandler() { ExpectedMethod = HttpMethod.Post, ExpectedPostData = new Dictionary <string, string>() { { "grant_type", "refresh_token" } }, ResponseMessage = MockHelpers.CreateSuccessTokenResponseMessage( TestConstants.UniqueId, TestConstants.DisplayableId, TestConstants.s_scope.ToArray()) }); // Using RT from Adal cache for silent call AuthenticationResult result1 = app .AcquireTokenSilent(TestConstants.s_scope, result.Account) .WithAuthority(TestConstants.AuthorityCommonTenant) .WithForceRefresh(false) .ExecuteAsync(CancellationToken.None) .Result; Assert.IsNotNull(result1); } }
public void NoCacheLookup() { MyReceiver myReceiver = new MyReceiver(); using (MockHttpAndServiceBundle harness = CreateTestHarness(telemetryCallback: myReceiver.HandleTelemetryEvents)) { TokenCache cache = new TokenCache(harness.ServiceBundle); MsalAccessTokenCacheItem atItem = new MsalAccessTokenCacheItem( TestConstants.ProductionPrefNetworkEnvironment, TestConstants.ClientId, TestConstants.s_scope.AsSingleString(), TestConstants.Utid, null, new DateTimeOffset(DateTime.UtcNow + TimeSpan.FromSeconds(3599)), new DateTimeOffset(DateTime.UtcNow + TimeSpan.FromSeconds(7200)), MockHelpers.CreateClientInfo()); string atKey = atItem.GetKey().ToString(); atItem.Secret = atKey; ((ITokenCacheInternal)cache).Accessor.SaveAccessToken(atItem); MockWebUI ui = new MockWebUI() { MockResult = AuthorizationResult.FromUri(TestConstants.AuthorityHomeTenant + "?code=some-code") }; MockInstanceDiscoveryAndOpenIdRequest(harness.HttpManager); harness.HttpManager.AddSuccessTokenResponseMockHandlerForPost(TestConstants.AuthorityHomeTenant); AuthenticationRequestParameters parameters = harness.CreateAuthenticationRequestParameters( TestConstants.AuthorityHomeTenant, TestConstants.s_scope, cache, extraQueryParameters: new Dictionary <string, string> { { "extra", "qp" } }); parameters.RedirectUri = new Uri("some://uri"); parameters.LoginHint = TestConstants.DisplayableId; AcquireTokenInteractiveParameters interactiveParameters = new AcquireTokenInteractiveParameters { Prompt = Prompt.SelectAccount, ExtraScopesToConsent = TestConstants.s_scopeForAnotherResource.ToArray(), }; InteractiveRequest request = new InteractiveRequest( harness.ServiceBundle, parameters, interactiveParameters, ui); Task <AuthenticationResult> task = request.RunAsync(CancellationToken.None); task.Wait(); AuthenticationResult result = task.Result; Assert.IsNotNull(result); Assert.AreEqual(1, ((ITokenCacheInternal)cache).Accessor.GetAllRefreshTokens().Count()); Assert.AreEqual(2, ((ITokenCacheInternal)cache).Accessor.GetAllAccessTokens().Count()); Assert.AreEqual(result.AccessToken, "some-access-token"); Assert.IsNotNull( myReceiver.EventsReceived.Find( anEvent => // Expect finding such an event anEvent[EventBase.EventNameKey].EndsWith("ui_event") && anEvent[UiEvent.UserCancelledKey] == "false")); Assert.IsNotNull( myReceiver.EventsReceived.Find( anEvent => // Expect finding such an event anEvent[EventBase.EventNameKey].EndsWith("api_event") && anEvent[ApiEvent.PromptKey] == "select_account")); Assert.IsNotNull( myReceiver.EventsReceived.Find( anEvent => // Expect finding such an event anEvent[EventBase.EventNameKey].EndsWith("ui_event") && anEvent[UiEvent.AccessDeniedKey] == "false")); } }
public void VerifyAuthorizationResultTest() { using (MockHttpAndServiceBundle harness = CreateTestHarness()) { MockInstanceDiscoveryAndOpenIdRequest(harness.HttpManager); MockWebUI webUi = new MockWebUI() { MockResult = AuthorizationResult.FromUri(TestConstants.AuthorityHomeTenant + "?error=" + OAuth2Error.LoginRequired), }; AuthenticationRequestParameters parameters = harness.CreateAuthenticationRequestParameters( TestConstants.AuthorityHomeTenant, TestConstants.s_scope, new TokenCache(harness.ServiceBundle), extraQueryParameters: new Dictionary <string, string> { { "extra", "qp" } }); parameters.RedirectUri = new Uri("some://uri"); parameters.LoginHint = TestConstants.DisplayableId; AcquireTokenInteractiveParameters interactiveParameters = new AcquireTokenInteractiveParameters { Prompt = Prompt.ForceLogin, ExtraScopesToConsent = TestConstants.s_scopeForAnotherResource.ToArray(), }; InteractiveRequest request = new InteractiveRequest( harness.ServiceBundle, parameters, interactiveParameters, webUi); try { request.ExecuteAsync(CancellationToken.None).Wait(); Assert.Fail("MsalException should have been thrown here"); } catch (Exception exc) { Assert.IsTrue(exc.InnerException is MsalUiRequiredException); Assert.AreEqual( MsalError.NoPromptFailedError, ((MsalUiRequiredException)exc.InnerException).ErrorCode); Assert.AreEqual( UiRequiredExceptionClassification.PromptNeverFailed, ((MsalUiRequiredException)exc.InnerException).Classification); } webUi = new MockWebUI { MockResult = AuthorizationResult.FromUri( TestConstants.AuthorityHomeTenant + "?error=invalid_request&error_description=some error description") }; request = new InteractiveRequest( harness.ServiceBundle, parameters, interactiveParameters, webUi); try { request.ExecuteAsync(CancellationToken.None).Wait(CancellationToken.None); Assert.Fail("MsalException should have been thrown here"); } catch (Exception exc) { Assert.IsTrue(exc.InnerException is MsalException); Assert.AreEqual("invalid_request", ((MsalException)exc.InnerException).ErrorCode); Assert.AreEqual("some error description", ((MsalException)exc.InnerException).Message); } } }
public override void TestInitialize() { base.TestInitialize(); _harness = CreateTestHarness(); }
public void SuccessfulValidationUsingOnPremiseDrsTest() { using (var harness = new MockHttpAndServiceBundle()) { // add mock response for on-premise DRS request harness.HttpManager.AddMockHandler( new MockHttpMessageHandler { ExpectedMethod = HttpMethod.Get, ExpectedUrl = "https://enterpriseregistration.fabrikam.com/enrollmentserver/contract", ExpectedQueryParams = new Dictionary <string, string> { { "api-version", "1.0" } }, ResponseMessage = MockHelpers.CreateSuccessResponseMessage( ResourceHelper.GetTestResourceRelativePath(File.ReadAllText("drs-response.json"))) }); // add mock response for on-premise webfinger request harness.HttpManager.AddMockHandler( new MockHttpMessageHandler { ExpectedMethod = HttpMethod.Get, ExpectedUrl = "https://fs.fabrikam.com/adfs/.well-known/webfinger", ExpectedQueryParams = new Dictionary <string, string> { { "resource", "https://fs.contoso.com" }, { "rel", "http://schemas.microsoft.com/rel/trusted-realm" } }, ResponseMessage = MockHelpers.CreateSuccessWebFingerResponseMessage() }); // add mock response for tenant endpoint discovery harness.HttpManager.AddMockHandler( new MockHttpMessageHandler { ExpectedMethod = HttpMethod.Get, ExpectedUrl = "https://fs.contoso.com/adfs/.well-known/openid-configuration", ResponseMessage = MockHelpers.CreateSuccessResponseMessage( ResourceHelper.GetTestResourceRelativePath(File.ReadAllText("OpenidConfiguration-OnPremise.json"))) }); Authority instance = Authority.CreateAuthority(harness.ServiceBundle, MsalTestConstants.OnPremiseAuthority); Assert.IsNotNull(instance); Assert.AreEqual(instance.AuthorityInfo.AuthorityType, AuthorityType.Adfs); var endpoints = harness.ServiceBundle.AuthorityEndpointResolutionManager.ResolveEndpointsAsync( instance.AuthorityInfo, MsalTestConstants.FabrikamDisplayableId, new RequestContext(harness.ServiceBundle, Guid.NewGuid())) .GetAwaiter().GetResult(); Assert.AreEqual("https://fs.contoso.com/adfs/oauth2/authorize/", endpoints.AuthorizationEndpoint); Assert.AreEqual("https://fs.contoso.com/adfs/oauth2/token/", endpoints.TokenEndpoint); Assert.AreEqual("https://fs.contoso.com/adfs", endpoints.SelfSignedJwtAudience); // attempt to do authority validation again. NO network call should be made instance = Authority.CreateAuthority(harness.ServiceBundle, MsalTestConstants.OnPremiseAuthority); Assert.IsNotNull(instance); Assert.AreEqual(instance.AuthorityInfo.AuthorityType, AuthorityType.Adfs); endpoints = harness.ServiceBundle.AuthorityEndpointResolutionManager.ResolveEndpointsAsync( instance.AuthorityInfo, MsalTestConstants.FabrikamDisplayableId, new RequestContext(harness.ServiceBundle, Guid.NewGuid())) .GetAwaiter().GetResult(); Assert.AreEqual("https://fs.contoso.com/adfs/oauth2/authorize/", endpoints.AuthorizationEndpoint); Assert.AreEqual("https://fs.contoso.com/adfs/oauth2/token/", endpoints.TokenEndpoint); Assert.AreEqual("https://fs.contoso.com/adfs", endpoints.SelfSignedJwtAudience); } }
private static async Task RunPpeTestAsync(bool validateAuthority, bool authorityIsValid) { using (var harness = new MockHttpAndServiceBundle()) { MockHttpMessageHandler discoveryHandler = null; if (authorityIsValid) { discoveryHandler = MockHelpers.CreateInstanceDiscoveryMockHandler( "https://login.microsoftonline.com/common/discovery/instance", TestConstants.DiscoveryJsonResponse); } else { discoveryHandler = new MockHttpMessageHandler() { ExpectedUrl = "https://login.microsoftonline.com/common/discovery/instance", ExpectedMethod = HttpMethod.Get, ResponseMessage = new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent(TestConstants.DiscoveryFailedResponse) } }; } var tokenHttpCallHandler = new MockHttpMessageHandler() { ExpectedUrl = "https://eastus.login.windows-ppe.net/17b189bc-2b81-4ec5-aa51-3e628cbc931b/oauth2/v2.0/token", ExpectedMethod = HttpMethod.Post, ResponseMessage = CreateResponse(true) }; if (authorityIsValid || !validateAuthority) // no calls because authority validation will fail { harness.HttpManager.AddMockHandler(discoveryHandler); harness.HttpManager.AddMockHandler(tokenHttpCallHandler); } else { harness.HttpManager.AddMockHandler(discoveryHandler); } var app = ConfidentialClientApplicationBuilder .Create(TestConstants.ClientId) .WithAuthority("https://login.windows-ppe.net/common", validateAuthority) .WithHttpManager(harness.HttpManager) .WithAzureRegion("eastus") .WithClientSecret(TestConstants.ClientSecret) .Build(); if (!authorityIsValid && validateAuthority) { var ex = await AssertException.TaskThrowsAsync <MsalServiceException>(() => app .AcquireTokenForClient(TestConstants.s_scope) .WithAuthority("https://login.windows-ppe.net/17b189bc-2b81-4ec5-aa51-3e628cbc931b") .ExecuteAsync()).ConfigureAwait(false); Assert.AreEqual(MsalError.InvalidInstance, ex.ErrorCode); var qp = CoreHelpers.ParseKeyValueList(discoveryHandler.ActualRequestMessage.RequestUri.Query.Substring(1), '&', true, null); Assert.AreEqual("https://login.windows-ppe.net/17b189bc-2b81-4ec5-aa51-3e628cbc931b/oauth2/v2.0/authorize", qp["authorization_endpoint"]); } else { AuthenticationResult result = await app .AcquireTokenForClient(TestConstants.s_scope) .WithAuthority("https://login.windows-ppe.net/17b189bc-2b81-4ec5-aa51-3e628cbc931b") .ExecuteAsync() .ConfigureAwait(false); Assert.AreEqual("eastus", result.ApiEvent.RegionUsed); Assert.AreEqual(TokenSource.IdentityProvider, result.AuthenticationResultMetadata.TokenSource); Assert.AreEqual( "https://login.microsoftonline.com/common/discovery/instance?api-version=1.1&authorization_endpoint=https%3A%2F%2Flogin.windows-ppe.net%2F17b189bc-2b81-4ec5-aa51-3e628cbc931b%2Foauth2%2Fv2.0%2Fauthorize", discoveryHandler.ActualRequestMessage.RequestUri.AbsoluteUri, "Authority validation is made on https://login.microsoftonline.com/ and it validates the auth_endpoint of the non-regional authority"); result = await app .AcquireTokenForClient(TestConstants.s_scope) .WithAuthority("https://login.windows-ppe.net/17b189bc-2b81-4ec5-aa51-3e628cbc931b") .ExecuteAsync() .ConfigureAwait(false); Assert.AreEqual("eastus", result.ApiEvent.RegionUsed); Assert.AreEqual(TokenSource.Cache, result.AuthenticationResultMetadata.TokenSource); } } }
public async Task ATS_NonExpired_NeedsRefresh_AADUnavailableResponse_Async() { // Arrange using (MockHttpAndServiceBundle harness = base.CreateTestHarness()) { Trace.WriteLine("1. Setup an app with a token cache with one AT"); PublicClientApplication app = SetupPca(harness); Trace.WriteLine("2. Configure AT so that it shows it needs to be refreshed"); UpdateATWithRefreshOn(app.UserTokenCacheInternal.Accessor); TokenCacheAccessRecorder cacheAccess = app.UserTokenCache.RecordAccess(); Trace.WriteLine("3. Configure AAD to respond with a 500 error"); harness.HttpManager.AddAllMocks(TokenResponseType.Invalid_AADUnavailable503); harness.HttpManager.AddTokenResponse(TokenResponseType.Invalid_AADUnavailable503); // Act var account = new Account(TestConstants.s_userIdentifier, TestConstants.DisplayableId, null); AuthenticationResult result = await app .AcquireTokenSilent( TestConstants.s_scope.ToArray(), account) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); // The following can be indeterministic due to background threading nature // So it is verified on check and wait basis Assert.IsTrue(YieldTillSatisfied(() => harness.HttpManager.QueueSize == 0), "Background refresh 1 did not execute."); // Assert Assert.AreEqual(0, harness.HttpManager.QueueSize); Assert.AreEqual(CacheRefreshReason.ProactivelyRefreshed, result.AuthenticationResultMetadata.CacheRefreshReason); cacheAccess.WaitTo_AssertAcessCounts(1, 0); // the refresh failed, no new data is written to the cache // reset throttling, otherwise MSAL would block similar requests for 2 minutes // and we would still get a cached response SingletonThrottlingManager.GetInstance().ResetCache(); // Now let AAD respond with tokens harness.HttpManager.AddTokenResponse(TokenResponseType.Valid_UserFlows); result = await app .AcquireTokenSilent( TestConstants.s_scope.ToArray(), account) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.AreEqual(CacheRefreshReason.ProactivelyRefreshed, result.AuthenticationResultMetadata.CacheRefreshReason); Assert.IsTrue(YieldTillSatisfied( () => harness.HttpManager.QueueSize == 0), "Background refresh 2 did not execute."); Assert.IsTrue( YieldTillSatisfied(() => cacheAccess.AfterAccessTotalCount == 3), "The background refresh executed, but the cache was not updated"); cacheAccess.WaitTo_AssertAcessCounts(2, 1); // new tokens written to cache } }
public async Task FetchTransferTokenAsync() { // Arrange using (MockHttpAndServiceBundle harness = CreateTestHarness()) { var msaProvider = new WebAccountProvider("id", "*****@*****.**", null); Client.Internal.Requests.AuthenticationRequestParameters requestParams = harness.CreateAuthenticationRequestParameters( TestConstants.AuthorityHomeTenant, validateAuthority: true); requestParams.AppConfig.WindowsBrokerOptions = new WindowsBrokerOptions() { MsaPassthrough = true }; var msaRequest = new WebTokenRequest(msaProvider); // step 1 - msa request _msaPlugin.CreateWebTokenRequestAsync(msaProvider, requestParams, false, true, false) .Returns(Task.FromResult(msaRequest)); var webTokenResponseWrapper = Substitute.For <IWebTokenRequestResultWrapper>(); webTokenResponseWrapper.ResponseStatus.Returns(WebTokenRequestStatus.Success); WebAccount accountFromMsaProvider = new WebAccount(msaProvider, "*****@*****.**", WebAccountState.Connected); var webTokenResponse = new WebTokenResponse("v1_token", accountFromMsaProvider); webTokenResponseWrapper.ResponseData.Returns(new List <WebTokenResponse>() { webTokenResponse }); _wamProxy.RequestTokenForWindowAsync(IntPtr.Zero, msaRequest).Returns(webTokenResponseWrapper); // step 2 - we have v1 token and a WebAccount, now get a transfer token var transferTokenRequest = new WebTokenRequest(msaProvider); _msaPlugin .CreateWebTokenRequestAsync( msaProvider, TestConstants.ClientId, MsaPassthroughHandler.TransferTokenScopes) .Returns(Task.FromResult(transferTokenRequest)); var webTokenResponseWrapper2 = Substitute.For <IWebTokenRequestResultWrapper>(); var transferTokenRequestResult = Substitute.For <IWebTokenRequestResultWrapper>(); transferTokenRequestResult.ResponseStatus.Returns(WebTokenRequestStatus.Success); //WebAccount accountFromMsaProvider = new WebAccount(msaProvider, "*****@*****.**", WebAccountState.Connected); var transferTokenResponse = new WebTokenResponse("transfer_token"); webTokenResponseWrapper2.ResponseData.Returns(new List <WebTokenResponse>() { transferTokenResponse }); _msaPlugin.ParseSuccessfullWamResponse(Arg.Any <WebTokenResponse>(), out Arg.Any <Dictionary <string, string> >()) .Returns(x => { x[1] = new Dictionary <string, string>(); (x[1] as Dictionary <string, string>).Add("code", "actual_transfer_token"); return(new MsalTokenResponse()); }); _wamProxy.RequestTokenForWindowAsync(IntPtr.Zero, transferTokenRequest).Returns(webTokenResponseWrapper2); // Act var transferToken = await _msaPassthroughHandler.TryFetchTransferTokenAsync(requestParams, msaProvider) .ConfigureAwait(false); // Assert Assert.AreEqual("actual_transfer_token", transferToken); } }