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); } }
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 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); } }
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()); }
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()]); }