|
11 | 11 | import static org.junit.jupiter.api.Assertions.assertNotNull; |
12 | 12 | import static org.junit.jupiter.api.Assertions.assertEquals; |
13 | 13 | import static org.junit.jupiter.api.Assertions.assertInstanceOf; |
| 14 | +import static org.mockito.ArgumentMatchers.any; |
| 15 | +import static org.mockito.Mockito.*; |
14 | 16 |
|
15 | 17 | import java.io.IOException; |
16 | 18 | import java.net.URISyntaxException; |
17 | 19 | import java.nio.file.Files; |
18 | 20 | import java.nio.file.Paths; |
19 | 21 | import java.util.Collections; |
| 22 | +import java.util.HashMap; |
20 | 23 | import java.util.concurrent.CompletableFuture; |
21 | 24 | import java.util.concurrent.ExecutionException; |
22 | 25 |
|
@@ -116,6 +119,79 @@ void confidentialAppAcquireTokenSilently_claimsSkipCache() throws Throwable { |
116 | 119 | assertInstanceOf(MsalInteractionRequiredException.class, ex.getCause()); |
117 | 120 | } |
118 | 121 |
|
| 122 | + @Test |
| 123 | + void testTokenRefreshReasons() throws Exception { |
| 124 | + DefaultHttpClient httpClientMock = mock(DefaultHttpClient.class); |
| 125 | + |
| 126 | + ConfidentialClientApplication cca = |
| 127 | + ConfidentialClientApplication.builder("clientId", ClientCredentialFactory.createFromSecret("password")) |
| 128 | + .authority("https://login.microsoftonline.com/tenant/") |
| 129 | + .instanceDiscovery(false) |
| 130 | + .validateAuthority(false) |
| 131 | + .httpClient(httpClientMock) |
| 132 | + .build(); |
| 133 | + |
| 134 | + HashMap<String, String> responseParameters = new HashMap<>(); |
| 135 | + |
| 136 | + //Acquire a token that expired an hour ago |
| 137 | + responseParameters.put("access_token", "expiredToken"); |
| 138 | + responseParameters.put("id_token", TestHelper.createIdToken(new HashMap<>())); |
| 139 | + responseParameters.put("expires_in", "-3600"); |
| 140 | + |
| 141 | + ClientCredentialParameters clientCredentialParameters = ClientCredentialParameters.builder(Collections.singleton("someScopes")).build(); |
| 142 | + when(httpClientMock.send(any(HttpRequest.class))).thenReturn(TestHelper.expectedResponse(200, TestHelper.getSuccessfulTokenResponse(responseParameters))); |
| 143 | + IAuthenticationResult result = cca.acquireToken(clientCredentialParameters).get(); |
| 144 | + |
| 145 | + //There should be one token in the cache, and no refresh behavior should have happened yet |
| 146 | + assertEquals(1, cca.tokenCache.accessTokens.size()); |
| 147 | + assertEquals("expiredToken", result.accessToken()); |
| 148 | + assertEquals(CacheRefreshReason.NOT_APPLICABLE, result.metadata().cacheRefreshReason()); |
| 149 | + verify(httpClientMock, times(1)).send(any()); |
| 150 | + |
| 151 | + //Attempt to retrieve the cached token, however it is expired and should be refreshed. |
| 152 | + // In this test, it will be replaced with a token that expires in 1 minute |
| 153 | + responseParameters.put("access_token", "nearlyExpiredToken"); |
| 154 | + responseParameters.put("expires_in", "60"); |
| 155 | + |
| 156 | + SilentParameters silentParameters = SilentParameters.builder(Collections.singleton("someScopes"), result.account()).build(); |
| 157 | + when(httpClientMock.send(any(HttpRequest.class))).thenReturn(TestHelper.expectedResponse(200, TestHelper.getSuccessfulTokenResponse(responseParameters))); |
| 158 | + result = cca.acquireTokenSilently(silentParameters).get(); |
| 159 | + |
| 160 | + //Ensure there is still one token in the cache, however it is the new refreshed token rather than the token from the first mocked call |
| 161 | + assertEquals(1, cca.tokenCache.accessTokens.size()); |
| 162 | + assertEquals("nearlyExpiredToken", result.accessToken()); |
| 163 | + assertEquals(CacheRefreshReason.EXPIRED, result.metadata().cacheRefreshReason()); |
| 164 | + verify(httpClientMock, times(2)).send(any()); |
| 165 | + |
| 166 | + //Attempt to retrieve the cached token, however it is within the 5-minute buffer and should be refreshed. |
| 167 | + // In this test, it will be replaced with a token that expires in 1 hour |
| 168 | + responseParameters.put("access_token", "normalToken"); |
| 169 | + responseParameters.put("expires_in", "3600"); |
| 170 | + |
| 171 | + silentParameters = SilentParameters.builder(Collections.singleton("someScopes"), result.account()).build(); |
| 172 | + when(httpClientMock.send(any(HttpRequest.class))).thenReturn(TestHelper.expectedResponse(200, TestHelper.getSuccessfulTokenResponse(responseParameters))); |
| 173 | + result = cca.acquireTokenSilently(silentParameters).get(); |
| 174 | + |
| 175 | + //Ensure there is still one token in the cache, however it is the new refreshed token rather than the token from the second mocked call |
| 176 | + assertEquals(1, cca.tokenCache.accessTokens.size()); |
| 177 | + assertEquals("normalToken", result.accessToken()); |
| 178 | + assertEquals(CacheRefreshReason.EXPIRED, result.metadata().cacheRefreshReason()); |
| 179 | + verify(httpClientMock, times(3)).send(any()); |
| 180 | + |
| 181 | + //Finally, force the token to be refreshed |
| 182 | + responseParameters.put("access_token", "forcedRefreshToken"); |
| 183 | + |
| 184 | + silentParameters = SilentParameters.builder(Collections.singleton("someScopes"), result.account()).forceRefresh(true).build(); |
| 185 | + when(httpClientMock.send(any(HttpRequest.class))).thenReturn(TestHelper.expectedResponse(200, TestHelper.getSuccessfulTokenResponse(responseParameters))); |
| 186 | + result = cca.acquireTokenSilently(silentParameters).get(); |
| 187 | + |
| 188 | + //Ensure there is still one token in the cache, however it is the new refreshed token rather than the token from the third mocked call |
| 189 | + assertEquals(1, cca.tokenCache.accessTokens.size()); |
| 190 | + assertEquals("forcedRefreshToken", result.accessToken()); |
| 191 | + assertEquals(CacheRefreshReason.FORCE_REFRESH, result.metadata().cacheRefreshReason()); |
| 192 | + verify(httpClientMock, times(4)).send(any()); |
| 193 | + } |
| 194 | + |
119 | 195 | String readResource(String resource) { |
120 | 196 | try { |
121 | 197 | return new String(Files.readAllBytes(Paths.get(getClass().getResource(resource).toURI()))); |
|
0 commit comments