Skip to content

Commit 31d5ac3

Browse files
Check user code expiry and invalidity
Fixes gh-1894 and gh-1977 Signed-off-by: Antoine Lauzon <[email protected]>
1 parent 40d503a commit 31d5ac3

File tree

2 files changed

+88
-2
lines changed

2 files changed

+88
-2
lines changed

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationProvider.java

+15-1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,21 @@ public Authentication authenticate(Authentication authentication) throws Authent
109109
this.logger.trace("Retrieved authorization with user code");
110110
}
111111

112+
OAuth2Authorization.Token<OAuth2UserCode> userCode = authorization.getToken(OAuth2UserCode.class);
113+
if (userCode.isInvalidated()) {
114+
if (this.logger.isTraceEnabled()) {
115+
this.logger.trace("User code is invalided");
116+
}
117+
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
118+
}
119+
120+
if (userCode.isExpired()) {
121+
if (this.logger.isTraceEnabled()) {
122+
this.logger.trace("User code is expired");
123+
}
124+
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
125+
}
126+
112127
Authentication principal = (Authentication) deviceVerificationAuthentication.getPrincipal();
113128
if (!isPrincipalAuthenticated(principal)) {
114129
if (this.logger.isTraceEnabled()) {
@@ -161,7 +176,6 @@ public Authentication authenticate(Authentication authentication) throws Authent
161176
requestedScopes, currentAuthorizedScopes);
162177
}
163178

164-
OAuth2Authorization.Token<OAuth2UserCode> userCode = authorization.getToken(OAuth2UserCode.class);
165179
// @formatter:off
166180
authorization = OAuth2Authorization.from(authorization)
167181
.principalName(principal.getName())

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationProviderTests.java

+73-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.time.temporal.ChronoUnit;
2121
import java.util.Collections;
2222
import java.util.Map;
23+
import java.util.function.Consumer;
2324
import java.util.function.Function;
2425

2526
import org.junit.jupiter.api.BeforeEach;
@@ -145,10 +146,72 @@ public void authenticateWhenAuthorizationNotFoundThenThrowOAuth2AuthenticationEx
145146
verifyNoInteractions(this.registeredClientRepository, this.authorizationConsentService);
146147
}
147148

149+
@Test
150+
public void authenticateWhenUserCodeIsInvalidedThenThrowOAuth2AuthenticationException() {
151+
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
152+
// @formatter:off
153+
OAuth2Authorization authorization = TestOAuth2Authorizations
154+
.authorization(registeredClient)
155+
.token(createDeviceCode())
156+
.token(createUserCode(), withInvalidated())
157+
.attribute(OAuth2ParameterNames.SCOPE, registeredClient.getScopes())
158+
.build();
159+
// @formatter:on
160+
given(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);
161+
Authentication authentication = createAuthentication();
162+
// @formatter:off
163+
assertThatExceptionOfType(OAuth2AuthenticationException.class)
164+
.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))
165+
.extracting(OAuth2AuthenticationException::getError)
166+
.extracting(OAuth2Error::getErrorCode)
167+
.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);
168+
// @formatter:on
169+
170+
verify(this.authorizationService).findByToken(USER_CODE,
171+
OAuth2DeviceVerificationAuthenticationProvider.USER_CODE_TOKEN_TYPE);
172+
verifyNoMoreInteractions(this.authorizationService);
173+
verifyNoInteractions(this.registeredClientRepository, this.authorizationConsentService);
174+
}
175+
176+
@Test
177+
public void authenticateWhenUserCodeIsExpiredThenThrowOAuth2AuthenticationException() {
178+
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
179+
// @formatter:off
180+
OAuth2Authorization authorization = TestOAuth2Authorizations
181+
.authorization(registeredClient)
182+
// Device code would also be expired but not relevant for this test
183+
.token(createDeviceCode())
184+
.token(createExpiredUserCode())
185+
.attribute(OAuth2ParameterNames.SCOPE, registeredClient.getScopes())
186+
.build();
187+
// @formatter:on
188+
given(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);
189+
Authentication authentication = createAuthentication();
190+
// @formatter:off
191+
assertThatExceptionOfType(OAuth2AuthenticationException.class)
192+
.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))
193+
.extracting(OAuth2AuthenticationException::getError)
194+
.extracting(OAuth2Error::getErrorCode)
195+
.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);
196+
// @formatter:on
197+
198+
verify(this.authorizationService).findByToken(USER_CODE,
199+
OAuth2DeviceVerificationAuthenticationProvider.USER_CODE_TOKEN_TYPE);
200+
verifyNoMoreInteractions(this.authorizationService);
201+
verifyNoInteractions(this.registeredClientRepository, this.authorizationConsentService);
202+
}
203+
148204
@Test
149205
public void authenticateWhenPrincipalNotAuthenticatedThenReturnUnauthenticated() {
150206
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
151-
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();
207+
// @formatter:off
208+
OAuth2Authorization authorization = TestOAuth2Authorizations
209+
.authorization(registeredClient)
210+
.token(createDeviceCode())
211+
.token(createUserCode())
212+
.attribute(OAuth2ParameterNames.SCOPE, registeredClient.getScopes())
213+
.build();
214+
// @formatter:on
152215
TestingAuthenticationToken principal = new TestingAuthenticationToken("user", null);
153216
Authentication authentication = new OAuth2DeviceVerificationAuthenticationToken(principal, USER_CODE,
154217
Collections.emptyMap());
@@ -331,6 +394,15 @@ private static OAuth2UserCode createUserCode() {
331394
return new OAuth2UserCode(USER_CODE, issuedAt, issuedAt.plus(30, ChronoUnit.MINUTES));
332395
}
333396

397+
private static OAuth2UserCode createExpiredUserCode() {
398+
Instant issuedAt = Instant.now().minus(45, ChronoUnit.MINUTES);
399+
return new OAuth2UserCode(USER_CODE, issuedAt, issuedAt.plus(30, ChronoUnit.MINUTES));
400+
}
401+
402+
private static Consumer<Map<String, Object>> withInvalidated() {
403+
return (metadata) -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true);
404+
}
405+
334406
private static Function<OAuth2Authorization.Token<? extends OAuth2Token>, Boolean> isInvalidated() {
335407
return (token) -> token.getMetadata(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME);
336408
}

0 commit comments

Comments
 (0)