|
15 | 15 | */
|
16 | 16 | package org.springframework.security.oauth2.server.authorization.authentication;
|
17 | 17 |
|
18 |
| -import java.security.Principal; |
19 |
| -import java.time.Duration; |
20 |
| -import java.time.Instant; |
21 |
| -import java.time.temporal.ChronoUnit; |
22 |
| -import java.util.HashMap; |
23 |
| -import java.util.HashSet; |
24 |
| -import java.util.Map; |
25 |
| -import java.util.Set; |
26 |
| - |
27 | 18 | import org.junit.Before;
|
28 | 19 | import org.junit.Test;
|
29 | 20 | import org.mockito.ArgumentCaptor;
|
30 |
| - |
31 | 21 | import org.springframework.security.authentication.TestingAuthenticationToken;
|
32 | 22 | import org.springframework.security.core.Authentication;
|
33 |
| -import org.springframework.security.oauth2.core.AuthorizationGrantType; |
34 |
| -import org.springframework.security.oauth2.core.ClientAuthenticationMethod; |
35 |
| -import org.springframework.security.oauth2.core.OAuth2AuthenticationException; |
36 |
| -import org.springframework.security.oauth2.core.OAuth2ErrorCodes; |
37 |
| -import org.springframework.security.oauth2.core.OAuth2TokenType; |
| 23 | +import org.springframework.security.oauth2.core.*; |
38 | 24 | import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
39 | 25 | import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
40 | 26 | import org.springframework.security.oauth2.core.oidc.OidcIdToken;
|
|
45 | 31 | import org.springframework.security.oauth2.jwt.Jwt;
|
46 | 32 | import org.springframework.security.oauth2.jwt.JwtClaimsSet;
|
47 | 33 | import org.springframework.security.oauth2.jwt.JwtEncoder;
|
48 |
| -import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; |
49 |
| -import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; |
50 |
| -import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations; |
| 34 | +import org.springframework.security.oauth2.server.authorization.*; |
51 | 35 | import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
52 | 36 | 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 | 37 |
|
57 |
| -import static org.assertj.core.api.Assertions.assertThat; |
58 |
| -import static org.assertj.core.api.Assertions.assertThatThrownBy; |
59 |
| -import static org.assertj.core.api.Assertions.entry; |
| 38 | +import java.security.Principal; |
| 39 | +import java.time.Duration; |
| 40 | +import java.time.Instant; |
| 41 | +import java.time.temporal.ChronoUnit; |
| 42 | +import java.util.*; |
| 43 | + |
| 44 | +import static org.assertj.core.api.Assertions.*; |
60 | 45 | import static org.mockito.ArgumentMatchers.any;
|
61 | 46 | import static org.mockito.ArgumentMatchers.eq;
|
62 |
| -import static org.mockito.Mockito.mock; |
63 |
| -import static org.mockito.Mockito.times; |
64 |
| -import static org.mockito.Mockito.verify; |
65 |
| -import static org.mockito.Mockito.when; |
| 47 | +import static org.mockito.Mockito.*; |
66 | 48 |
|
67 | 49 | /**
|
68 | 50 | * Tests for {@link OAuth2AuthorizationCodeAuthenticationProvider}.
|
@@ -222,6 +204,62 @@ public void authenticateWhenInvalidatedCodeThenThrowOAuth2AuthenticationExceptio
|
222 | 204 | .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);
|
223 | 205 | }
|
224 | 206 |
|
| 207 | + // gh-290 |
| 208 | + @Test |
| 209 | + public void authenticateWhenExpiredCodeThenThrowOAuth2AuthenticationException() throws InterruptedException { |
| 210 | + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); |
| 211 | + Instant now = Instant.now(); |
| 212 | + OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode( |
| 213 | + AUTHORIZATION_CODE, now, now.plusNanos(1)); |
| 214 | + OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) |
| 215 | + .token(authorizationCode) |
| 216 | + .build(); |
| 217 | + when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) |
| 218 | + .thenReturn(authorization); |
| 219 | + |
| 220 | + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient); |
| 221 | + OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( |
| 222 | + OAuth2AuthorizationRequest.class.getName()); |
| 223 | + OAuth2AuthorizationCodeAuthenticationToken authentication = |
| 224 | + new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); |
| 225 | + |
| 226 | + // Busy wait a brief moment for token to expire. TODO: Add a way to mock the underlying java.time.Clock? |
| 227 | + while (!Instant.now().isAfter(authorizationCode.getExpiresAt())) { |
| 228 | + Thread.sleep(0, 1); |
| 229 | + } |
| 230 | + |
| 231 | + assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) |
| 232 | + .isInstanceOf(OAuth2AuthenticationException.class) |
| 233 | + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) |
| 234 | + .extracting("errorCode") |
| 235 | + .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); |
| 236 | + } |
| 237 | + |
| 238 | + // gh-290 |
| 239 | + @Test |
| 240 | + public void authenticateWhenCodeIsBeforeUseThenThrowOAuth2AuthenticationException() throws InterruptedException { |
| 241 | + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); |
| 242 | + OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode( |
| 243 | + AUTHORIZATION_CODE, Instant.now(), Instant.now().plusSeconds(240)); |
| 244 | + OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) |
| 245 | + .token(authorizationCode, (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, Collections.singletonMap("nbf", Instant.now().plusSeconds(120)))) |
| 246 | + .build(); |
| 247 | + when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) |
| 248 | + .thenReturn(authorization); |
| 249 | + |
| 250 | + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient); |
| 251 | + OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( |
| 252 | + OAuth2AuthorizationRequest.class.getName()); |
| 253 | + OAuth2AuthorizationCodeAuthenticationToken authentication = |
| 254 | + new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); |
| 255 | + |
| 256 | + assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) |
| 257 | + .isInstanceOf(OAuth2AuthenticationException.class) |
| 258 | + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) |
| 259 | + .extracting("errorCode") |
| 260 | + .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); |
| 261 | + } |
| 262 | + |
225 | 263 | @Test
|
226 | 264 | public void authenticateWhenValidCodeThenReturnAccessToken() {
|
227 | 265 | RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
|
|
0 commit comments