예제 #1
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);
            }
        }
예제 #2
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);
            }
        }
예제 #3
0
        public async Task ClientCreds_NonExpired_NeedsRefresh_ValidResponse_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 valid token to the refresh RT flow");
                harness.HttpManager.AddAllMocks(TokenResponseType.Valid);

                // 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);
            }
        }
예제 #4
0
        public void JitterIsAddedToRefreshOn()
        {
            var at = TokenCacheHelper.CreateAccessTokenItem();
            var refreshOnFromCache = DateTimeOffset.UtcNow - TimeSpan.FromMinutes(10);

            at = at.WithRefreshOn(refreshOnFromCache);

            Func <Task <AuthenticationResult> > fetchAction = () =>
            {
                return(Task.FromResult <AuthenticationResult>(null));
            };

            List <DateTimeOffset?> refreshOnWithJitterList = new List <DateTimeOffset?>();

            for (int i = 1; i <= 10; i++)
            {
                SilentRequestHelper.NeedsRefresh(at, out DateTimeOffset? refreshOnWithJitter);
                refreshOnWithJitterList.Add(refreshOnWithJitter);

                Assert.IsTrue(refreshOnWithJitter.HasValue);
                CoreAssert.IsWithinRange(
                    refreshOnFromCache,
                    refreshOnWithJitter.Value,
                    TimeSpan.FromSeconds(Constants.DefaultJitterRangeInSeconds));
            }
            Assert.IsTrue(refreshOnWithJitterList.Distinct().Count() >= 8, "Jitter is random, so we can only have 1-2 identical values");
        }
        public void ClearCache_AppCache_Test()
        {
            var accessor = new InMemoryPartitionedAppTokenCacheAccessor(new NullLogger(), null);

            accessor.SaveAccessToken(TokenCacheHelper.CreateAccessTokenItem());

            Assert.AreEqual(1, accessor.AccessTokenCacheDictionary.Count);

            accessor.Clear();

            Assert.AreEqual(0, accessor.AccessTokenCacheDictionary.Count);
        }
        public void ValidatePopRequestAndToken()
        {
            using (var harness = CreateTestHarness())
            {
                // Arrange
                Uri uri = new Uri("https://www.contoso.com/path1/path2?queryParam1=a&queryParam2=b");
                PoPAuthenticationConfiguration popConfig = new PoPAuthenticationConfiguration(uri);
                popConfig.HttpMethod = HttpMethod.Post;

                var popCryptoProvider = Substitute.For <IPoPCryptoProvider>();
                var serviceBundle     = Substitute.For <IServiceBundle>();
                popCryptoProvider.CannonicalPublicKeyJwk.Returns(JWK);
                popCryptoProvider.CryptographicAlgorithm.Returns("RS256");
                popConfig.PopCryptoProvider = popCryptoProvider;
                const string             AtSecret = "secret";
                MsalAccessTokenCacheItem msalAccessTokenCacheItem = TokenCacheHelper.CreateAccessTokenItem();
                msalAccessTokenCacheItem.Secret = AtSecret;

                // Act
                PopAuthenticationScheme authenticationScheme = new PopAuthenticationScheme(popConfig, harness.ServiceBundle);
                var tokenParams    = authenticationScheme.GetTokenRequestParams();
                var popTokenString = authenticationScheme.FormatAccessToken(msalAccessTokenCacheItem);
                JwtSecurityToken decodedPopToken = new JwtSecurityToken(popTokenString);

                // Assert
                Assert.AreEqual("PoP", authenticationScheme.AuthorizationHeaderPrefix);
                Assert.AreEqual(JWT, authenticationScheme.KeyId);
                Assert.AreEqual(2, tokenParams.Count);
                Assert.AreEqual("pop", tokenParams["token_type"]);

                // This is the base64 URL encoding of the JWK containing only the KeyId
                Assert.AreEqual("eyJraWQiOiJOemJMc1hoOHVEQ2NkLTZNTndYRjRXXzdub1dYRlpBZkhreFpzUkdDOVhzIn0", tokenParams["req_cnf"]);
                Assert.AreEqual("RS256", decodedPopToken.Header.Alg);
                Assert.AreEqual(JWT, decodedPopToken.Header.Kid);
                Assert.AreEqual("pop", decodedPopToken.Header.Typ);
                Assert.AreEqual("RS256", decodedPopToken.SignatureAlgorithm);

                AssertSimpleClaim(decodedPopToken, "at", AtSecret);
                AssertSimpleClaim(decodedPopToken, "m", HttpMethod.Post.ToString());
                AssertSimpleClaim(decodedPopToken, "u", "www.contoso.com");
                AssertSimpleClaim(decodedPopToken, "p", "/path1/path2");

                string nonce = AssertSimpleClaim(decodedPopToken, "nonce");
                Assert.IsFalse(string.IsNullOrEmpty(nonce));
                string jwk = AssertSimpleClaim(decodedPopToken, "cnf");
                var    jwkFromPopAssertion = JToken.Parse(jwk);

                var initialJwk = JToken.Parse(JWK);
                Assert.IsTrue(jwkFromPopAssertion["jwk"].DeepEquals(initialJwk));
            }
        }
        public void HasAccessOrRefreshTokens_AppCache_Test()
        {
            var accessor = new InMemoryPartitionedAppTokenCacheAccessor(new NullLogger(), null);

            // Assert: false with empty collection
            Assert.IsFalse(accessor.HasAccessOrRefreshTokens());

            accessor.SaveAccessToken(TokenCacheHelper.CreateAccessTokenItem(isExpired: true));

            // Assert: false with expired token
            Assert.IsFalse(accessor.HasAccessOrRefreshTokens());

            accessor.SaveAccessToken(TokenCacheHelper.CreateAccessTokenItem());

            // Assert: true with valid token
            Assert.IsTrue(accessor.HasAccessOrRefreshTokens());
        }
예제 #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
            }
        }
        public void GetIdToken_Test()
        {
            var accessor = new InMemoryPartitionedUserTokenCacheAccessor(new NullLogger(), null);
            var idt1     = TokenCacheHelper.CreateIdTokenCacheItem("tenant1", "homeAccountId");
            var idt2     = TokenCacheHelper.CreateIdTokenCacheItem("tenant1", "homeAccountId2");
            var idt3     = TokenCacheHelper.CreateIdTokenCacheItem("tenant2", "homeAccountId");
            var at2      = TokenCacheHelper.CreateAccessTokenItem("scope1", "tenant1", "homeAccountId2");
            var at3      = TokenCacheHelper.CreateAccessTokenItem("scope1", "tenant2", "homeAccountId");

            // Assert: Null non-existing item
            Assert.IsNull(accessor.GetIdToken(at2));

            accessor.SaveIdToken(idt1);
            accessor.SaveIdToken(idt2);
            accessor.SaveIdToken(idt3);

            // Assert: Get token by key
            Assert.AreEqual(idt2.GetKey(), accessor.GetIdToken(at2).GetKey());
            Assert.AreEqual(idt3.GetKey(), accessor.GetIdToken(at3).GetKey());
        }
        public void DeleteAccessToken_Test(bool isAppCache)
        {
            var accessor = CreateTokenCacheAccessor(isAppCache);
            var at1      = TokenCacheHelper.CreateAccessTokenItem("scope1", "tenant1", "homeAccountId");
            var at2      = TokenCacheHelper.CreateAccessTokenItem("scope2", "tenant1", "homeAccountId");
            var at3      = TokenCacheHelper.CreateAccessTokenItem("scope1", "tenant2", "homeAccountId2");

            // Assert: Delete on empty collection doesn't throw
            accessor.DeleteAccessToken(at1);

            accessor.SaveAccessToken(at1);
            accessor.SaveAccessToken(at2);
            accessor.SaveAccessToken(at3);

            Assert.AreEqual(3, accessor.GetAllAccessTokens().Count);

            // Assert: Delete an existing item
            accessor.DeleteAccessToken(at1);

            Assert.AreEqual(2, accessor.GetAllAccessTokens().Count);
        }
        public void ClearCache_UserCache_Test()
        {
            var accessor = new InMemoryPartitionedUserTokenCacheAccessor(new NullLogger(), null);

            accessor.SaveAccessToken(TokenCacheHelper.CreateAccessTokenItem());
            accessor.SaveRefreshToken(TokenCacheHelper.CreateRefreshTokenItem());
            accessor.SaveIdToken(TokenCacheHelper.CreateIdTokenCacheItem());
            accessor.SaveAccount(TokenCacheHelper.CreateAccountItem());

            Assert.AreEqual(1, accessor.AccessTokenCacheDictionary.Count);
            Assert.AreEqual(1, accessor.RefreshTokenCacheDictionary.Count);
            Assert.AreEqual(1, accessor.IdTokenCacheDictionary.Count);
            Assert.AreEqual(1, accessor.AccountCacheDictionary.Count);

            accessor.Clear();

            Assert.AreEqual(0, accessor.AccessTokenCacheDictionary.Count);
            Assert.AreEqual(0, accessor.RefreshTokenCacheDictionary.Count);
            Assert.AreEqual(0, accessor.IdTokenCacheDictionary.Count);
            Assert.AreEqual(0, accessor.AccountCacheDictionary.Count);
        }
        public void NoSupportedMethods_AppCache_Test()
        {
            var accessor = new InMemoryPartitionedAppTokenCacheAccessor(new NullLogger(), null);

            Assert.ThrowsException <NotSupportedException>(() =>
                                                           accessor.SaveRefreshToken(TokenCacheHelper.CreateRefreshTokenItem())
                                                           );

            Assert.ThrowsException <NotSupportedException>(() =>
                                                           accessor.SaveIdToken(TokenCacheHelper.CreateIdTokenCacheItem())
                                                           );

            Assert.ThrowsException <NotSupportedException>(() =>
                                                           accessor.SaveAccount(TokenCacheHelper.CreateAccountItem())
                                                           );

            Assert.ThrowsException <NotSupportedException>(() =>
                                                           accessor.GetIdToken(TokenCacheHelper.CreateAccessTokenItem())
                                                           );

            Assert.ThrowsException <NotSupportedException>(() =>
                                                           accessor.GetAccount(TokenCacheHelper.CreateAccountItem().GetKey())
                                                           );

            Assert.ThrowsException <NotSupportedException>(() =>
                                                           accessor.DeleteRefreshToken(TokenCacheHelper.CreateRefreshTokenItem())
                                                           );

            Assert.ThrowsException <NotSupportedException>(() =>
                                                           accessor.DeleteIdToken(TokenCacheHelper.CreateIdTokenCacheItem())
                                                           );

            Assert.ThrowsException <NotSupportedException>(() =>
                                                           accessor.DeleteAccount(TokenCacheHelper.CreateAccountItem())
                                                           );

            Assert.AreEqual(0, accessor.GetAllRefreshTokens().Count);
            Assert.AreEqual(0, accessor.GetAllIdTokens().Count);
            Assert.AreEqual(0, accessor.GetAllAccounts().Count);
        }
        public void GetAllAccessTokens_Test(bool isAppCache)
        {
            var    accessor      = CreateTokenCacheAccessor(isAppCache);
            var    at1           = TokenCacheHelper.CreateAccessTokenItem("scope1", "tenant1", "homeAccountId");
            var    at2           = TokenCacheHelper.CreateAccessTokenItem("scope2", "tenant1", "homeAccountId");
            var    at3           = TokenCacheHelper.CreateAccessTokenItem("scope1", "tenant2", "homeAccountId2");
            string partitionKey1 = GetPartitionKey(isAppCache, at1);
            string partitionKey2 = GetPartitionKey(isAppCache, at3);

            // Assert: Returns empty collection
            Assert.AreEqual(0, accessor.GetAllAccessTokens().Count);
            Assert.AreEqual(0, accessor.GetAllAccessTokens(partitionKey1).Count);

            accessor.SaveAccessToken(at1);
            accessor.SaveAccessToken(at2);
            accessor.SaveAccessToken(at3);

            // Assert: Get all tokens and get all tokens by partition key
            Assert.AreEqual(3, accessor.GetAllAccessTokens().Count);
            Assert.AreEqual(2, accessor.GetAllAccessTokens(partitionKey1).Count);
            Assert.AreEqual(1, accessor.GetAllAccessTokens(partitionKey2).Count);
        }
        public void SaveAccessToken_Test(bool isAppCache)
        {
            var accessor = CreateTokenCacheAccessor(isAppCache);

            var at1 = TokenCacheHelper.CreateAccessTokenItem("scope1", "tenant1", "homeAccountId");

            // Assert: Save with new item
            accessor.SaveAccessToken(at1);

            Assert.AreEqual(1, accessor.GetAllAccessTokens().Count);
            Assert.AreEqual(1, GetAccessTokenCache(accessor, isAppCache).Count);
            string partitionKey1 = GetPartitionKey(isAppCache, at1);

            Assert.IsNotNull(GetAccessTokenCache(accessor, isAppCache)[partitionKey1][at1.GetKey().ToString()]);

            var at2 = TokenCacheHelper.CreateAccessTokenItem("scope2", "tenant1", "homeAccountId");

            // Assert: Save under the existing partition
            accessor.SaveAccessToken(at2);

            Assert.AreEqual(2, accessor.GetAllAccessTokens().Count);
            Assert.AreEqual(1, GetAccessTokenCache(accessor, isAppCache).Count);
            Assert.IsNotNull(GetAccessTokenCache(accessor, isAppCache)[partitionKey1][at2.GetKey().ToString()]);

            var at3 = TokenCacheHelper.CreateAccessTokenItem("scope1", "tenant2", "homeAccountId2");

            // Assert: Save under a new partition
            accessor.SaveAccessToken(at3);
            // Assert: Save overwrites the existing token
            accessor.SaveAccessToken(at3);

            Assert.AreEqual(3, accessor.GetAllAccessTokens().Count);
            Assert.AreEqual(2, GetAccessTokenCache(accessor, isAppCache).Count);
            string partitionKey2 = GetPartitionKey(isAppCache, at3);

            Assert.IsNotNull(GetAccessTokenCache(accessor, isAppCache)[partitionKey2][at3.GetKey().ToString()]);
        }