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);
        }
Пример #3
0
        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);
        }
Пример #4
0
 public MockHttpTestHarness(string authorityUri)
 {
     _mockHttpAndServiceBundle = new MockHttpAndServiceBundle();
     Authority = Authority.CreateAuthority(ServiceBundle, authorityUri);
     Cache     = new TokenCache(ServiceBundle);
 }
Пример #5
0
        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);
            }
        }
Пример #6
0
        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"]);
            }
        }
Пример #7
0
        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);
            }
        }
Пример #8
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);
                }
            }
        }
Пример #11
0
        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);
            }
        }
Пример #13
0
        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);
                }
            }
        }
Пример #14
0
        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
            }
        }
Пример #15
0
        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);
            }
        }