|
19 | 19 | import java.time.Duration;
|
20 | 20 | import java.time.Instant;
|
21 | 21 | import java.time.temporal.ChronoUnit;
|
| 22 | +import java.util.Collections; |
22 | 23 | import java.util.HashMap;
|
23 | 24 | import java.util.HashSet;
|
24 | 25 | import java.util.Map;
|
|
45 | 46 | import org.springframework.security.oauth2.jwt.Jwt;
|
46 | 47 | import org.springframework.security.oauth2.jwt.JwtClaimsSet;
|
47 | 48 | import org.springframework.security.oauth2.jwt.JwtEncoder;
|
| 49 | +import org.springframework.security.oauth2.server.authorization.JwtEncodingContext; |
48 | 50 | import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
|
| 51 | +import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode; |
49 | 52 | import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
|
| 53 | +import org.springframework.security.oauth2.server.authorization.OAuth2TokenCustomizer; |
50 | 54 | import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;
|
51 | 55 | import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
52 | 56 | import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
|
53 |
| -import org.springframework.security.oauth2.server.authorization.JwtEncodingContext; |
54 |
| -import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode; |
55 |
| -import org.springframework.security.oauth2.server.authorization.OAuth2TokenCustomizer; |
56 | 57 |
|
57 | 58 | import static org.assertj.core.api.Assertions.assertThat;
|
58 | 59 | import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
@@ -222,6 +223,62 @@ public void authenticateWhenInvalidatedCodeThenThrowOAuth2AuthenticationExceptio
|
222 | 223 | .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);
|
223 | 224 | }
|
224 | 225 |
|
| 226 | + // gh-290 |
| 227 | + @Test |
| 228 | + public void authenticateWhenExpiredCodeThenThrowOAuth2AuthenticationException() throws InterruptedException { |
| 229 | + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); |
| 230 | + Instant now = Instant.now(); |
| 231 | + OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode( |
| 232 | + AUTHORIZATION_CODE, now, now.plusNanos(1)); |
| 233 | + OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) |
| 234 | + .token(authorizationCode) |
| 235 | + .build(); |
| 236 | + when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) |
| 237 | + .thenReturn(authorization); |
| 238 | + |
| 239 | + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient); |
| 240 | + OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( |
| 241 | + OAuth2AuthorizationRequest.class.getName()); |
| 242 | + OAuth2AuthorizationCodeAuthenticationToken authentication = |
| 243 | + new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); |
| 244 | + |
| 245 | + // Busy wait a brief moment for token to expire. TODO: Add a way to mock the underlying java.time.Clock? |
| 246 | + while (!Instant.now().isAfter(authorizationCode.getExpiresAt())) { |
| 247 | + Thread.sleep(0, 1); |
| 248 | + } |
| 249 | + |
| 250 | + assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) |
| 251 | + .isInstanceOf(OAuth2AuthenticationException.class) |
| 252 | + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) |
| 253 | + .extracting("errorCode") |
| 254 | + .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); |
| 255 | + } |
| 256 | + |
| 257 | + // gh-290 |
| 258 | + @Test |
| 259 | + public void authenticateWhenCodeIsBeforeUseThenThrowOAuth2AuthenticationException() throws InterruptedException { |
| 260 | + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); |
| 261 | + OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode( |
| 262 | + AUTHORIZATION_CODE, Instant.now(), Instant.now().plusSeconds(240)); |
| 263 | + OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) |
| 264 | + .token(authorizationCode, (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, Collections.singletonMap("nbf", Instant.now().plusSeconds(120)))) |
| 265 | + .build(); |
| 266 | + when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) |
| 267 | + .thenReturn(authorization); |
| 268 | + |
| 269 | + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient); |
| 270 | + OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( |
| 271 | + OAuth2AuthorizationRequest.class.getName()); |
| 272 | + OAuth2AuthorizationCodeAuthenticationToken authentication = |
| 273 | + new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); |
| 274 | + |
| 275 | + assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) |
| 276 | + .isInstanceOf(OAuth2AuthenticationException.class) |
| 277 | + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) |
| 278 | + .extracting("errorCode") |
| 279 | + .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); |
| 280 | + } |
| 281 | + |
225 | 282 | @Test
|
226 | 283 | public void authenticateWhenValidCodeThenReturnAccessToken() {
|
227 | 284 | RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
|
|
0 commit comments