public async void ShouldRefreshActionstepToken() { using (var authDelegatingHandler = new AuthDelegatingHandler() { InnerHandler = _handler }) using (var httpClient = new HttpClient(authDelegatingHandler)) using (var memoryCache = new MemoryCache(new MemoryCacheOptions())) { var tokenHandler = new TestTokenSetRepository(); var fakeClock = new FakeClock(Instant.FromUtc(2019, 05, 07, 2, 3)); var testTokenRepository = new TestTokenSetRepository(); var options = new ActionstepServiceConfigurationOptions("clientId", "clientSecret"); var actionstepService = new ActionstepService(new NullLogger <ActionstepService>(), httpClient, options, testTokenRepository, fakeClock, memoryCache); // Expired token var tokenSet = new TokenSet( accessToken: "testAccessToken", tokenType: "bearer", expiresIn: 60 * 20, apiEndpoint: new Uri("https://api.uri/api/"), orgKey: "testOrgKey", refreshToken: "testRefreshToken", userId: "1", id: "tokenId", receivedAt: fakeClock.GetCurrentInstant().Minus(Duration.FromDays(365))); var response = await actionstepService.RefreshAccessTokenIfExpired(tokenSet); Assert.NotNull(response); Assert.Equal("updatedAccessToken", response.AccessToken); Assert.Equal(3600, response.ExpiresIn); Assert.Equal("updatedRefreshToken", response.RefreshToken); Assert.Equal("testOrg", response.OrgKey); Assert.Equal(new Uri("https://test-endpoint/api/"), response.ApiEndpoint); Assert.Equal("tokenId", response.Id); Assert.Equal(2, testTokenRepository.AddOrUpdateTokenSetCount); } }
public async Task SecondTokenRefreshWaitsForFirst() { using (var authDelegatingHandler = new AuthDelegatingHandler() { InnerHandler = _handler }) using (var httpClient = new HttpClient(authDelegatingHandler)) using (var memoryCache = new MemoryCache(new MemoryCacheOptions())) { var tokenHandler = new TestTokenSetRepository(); var fakeClock = new FakeClock(Instant.FromUtc(2019, 05, 07, 2, 3)); var testTokenRepository = new TestTokenSetRepository(); var options = new ActionstepServiceConfigurationOptions("clientId", "clientSecret"); var actionstepService = new ActionstepService(new NullLogger <ActionstepService>(), httpClient, options, testTokenRepository, fakeClock, memoryCache); var now = fakeClock.GetCurrentInstant(); /// We're using the TokenType purely as a mechanism to talk to <see cref="TestTokenSetRepository"/>. /// This allows us to delay one refresh while queueing another to ensure the locking/retry works correctly /// with concurrent refresh requests. var tokenTypeTest1 = "bearer-test1"; var tokenTypeTest2 = "bearer-test2"; // First, store a locked expired token var tokenId = "locking test token"; var tokenSet1 = new TokenSet( "testAccessToken1", tokenTypeTest1, 3600, new Uri("https://api.uri/api/auto-increment/"), "testOrgKey", "testRefreshToken1", now.Minus(Duration.FromMinutes(120)), "testUser", tokenId); tokenSet1.LockForRefresh(now.Plus(Duration.FromMinutes(60))); var storedTokenSet1 = await testTokenRepository.AddOrUpdateTokenSet(tokenSet1); // Now we set up a second expired token with the same id. var tokenSet2 = new TokenSet( "testAccessToken2", tokenTypeTest2, 3600, new Uri("https://api.uri/api/auto-increment/"), "testOrgKey", "testRefreshToken2", now.Minus(Duration.FromMinutes(120)), "testUser", tokenId); // Try to refresh using the second token (which has the same tokenId). It should be blocked by the locked token1 from above. var tokenSet2RefreshTask = actionstepService.RefreshAccessTokenIfExpired(tokenSet2, forceRefresh: false); /// Delay to give the <see cref="RefreshAccessTokenIfExpired"/> a chance to await Task.Delay(50); // Now store a valid token of test type 1, this will unlock the token tokenSet1 = new TokenSet( "testAccessToken1", tokenTypeTest1, 3600, new Uri("https://api.uri/api/auto-increment/"), "testOrgKey", "testRefreshToken1", now, "testUser", tokenId); await testTokenRepository.AddOrUpdateTokenSet(tokenSet1); tokenSet2RefreshTask.Wait(); // The token from the refresh should be the one we put in the repository. Assert.Equal(tokenTypeTest1, tokenSet2RefreshTask.Result.TokenType); } }