Example #1
0
        public async Task Interactive_SSHCert_Async()
        {
            LabResponse labResponse = await LabUserHelper.GetDefaultUserAsync().ConfigureAwait(false);

            IPublicClientApplication pca = PublicClientApplicationBuilder
                                           .Create(labResponse.AppId)
                                           .WithRedirectUri(SeleniumWebUI.FindFreeLocalhostRedirectUri())
                                           .Build();

            TokenCacheAccessRecorder userCacheAccess = pca.UserTokenCache.RecordAccess();

            Trace.WriteLine("Part 1 - Acquire an SSH cert interactively ");
            string jwk = CreateJwk();

            AuthenticationResult result = await pca
                                          .AcquireTokenInteractive(s_scopes)
                                          .WithCustomWebUi(CreateSeleniumCustomWebUI(labResponse.User, Prompt.ForceLogin))
                                          .WithSSHCertificateAuthenticationScheme(jwk, "key1")
                                          .WithExtraQueryParameters(GetTestSliceParams()) // TODO: remove this once feature is in PROD
                                          .ExecuteAsync(new CancellationTokenSource(_interactiveAuthTimeout).Token)
                                          .ConfigureAwait(false);

            userCacheAccess.AssertAccessCounts(0, 1);
            Assert.AreEqual("ssh-cert", result.TokenType);
            IAccount account = await MsalAssert.AssertSingleAccountAsync(labResponse, pca, result).ConfigureAwait(false);

            userCacheAccess.AssertAccessCounts(1, 1); // the assert calls GetAccounts

            Trace.WriteLine("Part 2 - Acquire a token silent with the same keyID - should be served from the cache");
            result = await pca
                     .AcquireTokenSilent(s_scopes, account)
                     .WithSSHCertificateAuthenticationScheme(jwk, "key1")
                     .WithExtraQueryParameters(GetTestSliceParams()) // TODO: remove this once feature is in PROD
                     .ExecuteAsync(new CancellationTokenSource(_interactiveAuthTimeout).Token)
                     .ConfigureAwait(false);

            userCacheAccess.AssertAccessCounts(2, 1);

            account = await MsalAssert.AssertSingleAccountAsync(labResponse, pca, result).ConfigureAwait(false);

            userCacheAccess.AssertAccessCounts(3, 1);

            Trace.WriteLine("Part 3 - Acquire a token silent with a different keyID - should not sbe served from the cache");
            result = await pca
                     .AcquireTokenSilent(s_scopes, account)
                     .WithSSHCertificateAuthenticationScheme(jwk, "key2")
                     .WithExtraQueryParameters(GetTestSliceParams()) // TODO: remove this once feature is in PROD
                     .ExecuteAsync(new CancellationTokenSource(_interactiveAuthTimeout).Token)
                     .ConfigureAwait(false);

            Assert.AreEqual("ssh-cert", result.TokenType);
            userCacheAccess.AssertAccessCounts(4, 2);
            await MsalAssert.AssertSingleAccountAsync(labResponse, pca, result).ConfigureAwait(false);
        }
Example #2
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, DateTime.UtcNow - TimeSpan.FromMinutes(1));
                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);

                // Assert
                Assert.IsNotNull(result, "ATS still succeeds even though AAD is unavaible");
                Assert.AreEqual(0, harness.HttpManager.QueueSize);
                cacheAccess.AssertAccessCounts(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);

                result = await app
                         .AcquireTokenSilent(
                    TestConstants.s_scope.ToArray(),
                    account)
                         .ExecuteAsync(CancellationToken.None)
                         .ConfigureAwait(false);

                Assert.IsNotNull(result);
                cacheAccess.AssertAccessCounts(2, 1); // new tokens written to cache
            }
        }
        public async Task ClientCreds_Expired_NeedsRefresh_AADInvalidResponse_Async()
        {
            // Arrange
            using (MockHttpAndServiceBundle harness = base.CreateTestHarness())
            {
                Trace.WriteLine("1. Setup an app with a token cache with one AT");
                ConfidentialClientApplication app = SetupCca(harness);

                Trace.WriteLine("2. Configure AT so that it shows it needs to be refreshed, but is also expired");
                UpdateATWithRefreshOn(app.AppTokenCacheInternal.Accessor, DateTime.UtcNow - TimeSpan.FromMinutes(1), true);
                TokenCacheAccessRecorder cacheAccess = app.AppTokenCache.RecordAccess();

                Trace.WriteLine("3. Configure AAD to be unavaiable");
                AddHttpMocks(TokenResponseType.Invalid_AADUnavailable, harness.HttpManager, pca: false);

                // Act
                MsalServiceException ex = await AssertException.TaskThrowsAsync <MsalServiceException>(() => app
                                                                                                       .AcquireTokenForClient(TestConstants.s_scope)
                                                                                                       .ExecuteAsync())
                                          .ConfigureAwait(false);

                Assert.IsFalse(ex is MsalUiRequiredException, "5xx exceptions do not translate to MsalUIRequired");
                Assert.AreEqual(504, ex.StatusCode);
                cacheAccess.AssertAccessCounts(1, 0);
            }
        }
        public async Task ClientCreds_NonExpired_NeedsRefresh_ValidResponse_Async()
        {
            // Arrange
            using (MockHttpAndServiceBundle harness = base.CreateTestHarness())
            {
                Trace.WriteLine("1. Setup an app with a token cache with one AT");
                ConfidentialClientApplication app = SetupCca(harness);

                Trace.WriteLine("2. Configure AT so that it shows it needs to be refreshed");
                UpdateATWithRefreshOn(app.AppTokenCacheInternal.Accessor, DateTime.UtcNow - TimeSpan.FromMinutes(1));
                TokenCacheAccessRecorder cacheAccess = app.AppTokenCache.RecordAccess();

                Trace.WriteLine("3. Configure AAD to respond with valid token to the refresh RT flow");
                AddHttpMocks(TokenResponseType.Valid, harness.HttpManager, pca: false);

                // Act
                Trace.WriteLine("4. ATS - should perform an RT refresh");
                AuthenticationResult result = await app.AcquireTokenForClient(TestConstants.s_scope)
                                              .ExecuteAsync()
                                              .ConfigureAwait(false);

                // Assert
                Assert.IsNotNull(result);
                Assert.AreEqual(0, harness.HttpManager.QueueSize,
                                "MSAL should have refreshed the token because the original AT was marked for refresh");
                cacheAccess.AssertAccessCounts(1, 1);
            }
        }
Example #5
0
        public async Task ClientCreds_Expired_NeedsRefresh_AADInvalidResponse_Async()
        {
            // Arrange
            using (MockHttpAndServiceBundle harness = base.CreateTestHarness())
            {
                Trace.WriteLine("1. Setup an app with a token cache with one AT = expired and needs refersh");
                ConfidentialClientApplication app = SetupCca(harness);
                var atItem = TokenCacheHelper.CreateAccessTokenItem();
                UpdateATWithRefreshOn(atItem, DateTime.UtcNow - TimeSpan.FromMinutes(1), true);
                TokenCacheHelper.PopulateDefaultAppTokenCache(app, atItem);

                TokenCacheAccessRecorder cacheAccess = app.AppTokenCache.RecordAccess();

                Trace.WriteLine("2. Configure AAD to be unavaiable");
                harness.HttpManager.AddAllMocks(TokenResponseType.Invalid_AADUnavailable503);
                harness.HttpManager.AddTokenResponse(TokenResponseType.Invalid_AADUnavailable503);

                // Act
                MsalServiceException ex = await AssertException.TaskThrowsAsync <MsalServiceException>(() => app
                                                                                                       .AcquireTokenForClient(TestConstants.s_scope)
                                                                                                       .ExecuteAsync())
                                          .ConfigureAwait(false);

                Assert.IsFalse(ex is MsalUiRequiredException, "5xx exceptions do not translate to MsalUIRequired");
                Assert.AreEqual(503, ex.StatusCode);
                cacheAccess.AssertAccessCounts(1, 0);
            }
        }
Example #6
0
        public async Task ClientCreds_NonExpired_NeedsRefresh_AADInvalidResponse_Async()
        {
            // Arrange
            using (MockHttpAndServiceBundle harness = base.CreateTestHarness())
            {
                Trace.WriteLine("1. Setup an app");
                ConfidentialClientApplication app = SetupCca(harness);

                Trace.WriteLine("2. Configure AT so that it shows it needs to be refreshed");

                MsalAccessTokenCacheItem atItem = TokenCacheHelper.CreateAccessTokenItem();
                UpdateATWithRefreshOn(atItem, DateTime.UtcNow - TimeSpan.FromMinutes(1));
                TokenCacheHelper.PopulateDefaultAppTokenCache(app, atItem);

                TokenCacheAccessRecorder cacheAccess = app.AppTokenCache.RecordAccess();

                Trace.WriteLine("3. Configure AAD to respond with the typical Invalid Grant error");
                harness.HttpManager.AddAllMocks(TokenResponseType.InvalidGrant);

                // Act
                await AssertException.TaskThrowsAsync <MsalUiRequiredException>(() =>
                                                                                app.AcquireTokenForClient(TestConstants.s_scope)
                                                                                .ExecuteAsync())
                .ConfigureAwait(false);

                cacheAccess.AssertAccessCounts(1, 0);
            }
        }
        public async Task ATS_NonExpired_NeedsRefresh_ValidResponse_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, DateTime.UtcNow - TimeSpan.FromMinutes(1));
                TokenCacheAccessRecorder cacheAccess = app.UserTokenCache.RecordAccess();

                Trace.WriteLine("3. Configure AAD to respond with valid token to the refresh RT flow");
                AddHttpMocks(TokenResponseType.Valid, harness.HttpManager, pca: true);

                // Act
                Trace.WriteLine("4. ATS - should perform an RT refresh");
                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);

                // Assert
                Assert.IsNotNull(result);
                Assert.AreEqual(0, harness.HttpManager.QueueSize,
                                "MSAL should have refreshed the token because the original AT was marked for refresh");
                cacheAccess.AssertAccessCounts(1, 1);
                MsalAccessTokenCacheItem ati = app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Single();
                Assert.IsTrue(ati.RefreshOn > DateTime.UtcNow + TimeSpan.FromMinutes(10));

                // New ATS does not refresh the AT because RefreshIn is in the future
                Trace.WriteLine("5. ATS - should fetch from the cache only");
                result = await app
                         .AcquireTokenSilent(
                    TestConstants.s_scope.ToArray(),
                    account)
                         .ExecuteAsync(CancellationToken.None)
                         .ConfigureAwait(false);

                Assert.IsNotNull(result);

                cacheAccess.AssertAccessCounts(2, 1);
            }
        }
Example #8
0
        public async Task ClientCreds_NonExpired_NeedsRefresh_AADUnavailableResponse_Async()
        {
            // Arrange
            using (MockHttpAndServiceBundle harness = base.CreateTestHarness())
            {
                Trace.WriteLine("1. Setup an app ");
                ConfidentialClientApplication app = SetupCca(harness);

                Trace.WriteLine("2. Configure AT so that it shows it needs to be refreshed");

                MsalAccessTokenCacheItem atItem = TokenCacheHelper.CreateAccessTokenItem();
                UpdateATWithRefreshOn(atItem, DateTime.UtcNow - TimeSpan.FromMinutes(1));
                TokenCacheHelper.PopulateDefaultAppTokenCache(app, atItem);

                TokenCacheAccessRecorder cacheAccess = app.AppTokenCache.RecordAccess();

                //UpdateATWithRefreshOn(app.AppTokenCacheInternal.Accessor, DateTime.UtcNow - TimeSpan.FromMinutes(1));


                Trace.WriteLine("3. Configure AAD to respond with an error");
                harness.HttpManager.AddAllMocks(TokenResponseType.Invalid_AADUnavailable503);
                harness.HttpManager.AddTokenResponse(TokenResponseType.Invalid_AADUnavailable503);

                // Act
                AuthenticationResult result = await app.AcquireTokenForClient(TestConstants.s_scope)
                                              .ExecuteAsync()
                                              .ConfigureAwait(false);

                // Assert
                Assert.IsNotNull(result, "ClientCreds should still succeeds even though AAD is unavaible");
                Assert.AreEqual(0, harness.HttpManager.QueueSize);
                cacheAccess.AssertAccessCounts(1, 0); // the refresh failed, no new data is written to the cache

                // Now let AAD respond with tokens
                harness.HttpManager.AddTokenResponse(TokenResponseType.Valid);

                result = await app.AcquireTokenForClient(TestConstants.s_scope)
                         .ExecuteAsync()
                         .ConfigureAwait(false);

                Assert.IsNotNull(result);
                cacheAccess.AssertAccessCounts(2, 1); // new tokens written to cache
            }
        }