|
@@ -1,5 +1,5 @@
|
|
/*
|
|
/*
|
|
- * Copyright 2020-2021 the original author or authors.
|
|
|
|
|
|
+ * Copyright 2020-2022 the original author or authors.
|
|
*
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* you may not use this file except in compliance with the License.
|
|
@@ -31,8 +31,6 @@ import org.junit.Test;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.context.annotation.Bean;
|
|
import org.springframework.context.annotation.Bean;
|
|
import org.springframework.context.annotation.Import;
|
|
import org.springframework.context.annotation.Import;
|
|
-import org.springframework.core.Ordered;
|
|
|
|
-import org.springframework.core.annotation.Order;
|
|
|
|
import org.springframework.http.HttpHeaders;
|
|
import org.springframework.http.HttpHeaders;
|
|
import org.springframework.jdbc.core.JdbcOperations;
|
|
import org.springframework.jdbc.core.JdbcOperations;
|
|
import org.springframework.jdbc.core.JdbcTemplate;
|
|
import org.springframework.jdbc.core.JdbcTemplate;
|
|
@@ -66,7 +64,6 @@ import org.springframework.security.oauth2.server.authorization.client.JdbcRegis
|
|
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
|
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
|
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
|
|
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
|
|
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
|
|
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
|
|
-import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
|
|
|
|
import org.springframework.security.oauth2.server.authorization.jackson2.TestingAuthenticationTokenMixin;
|
|
import org.springframework.security.oauth2.server.authorization.jackson2.TestingAuthenticationTokenMixin;
|
|
import org.springframework.security.web.SecurityFilterChain;
|
|
import org.springframework.security.web.SecurityFilterChain;
|
|
import org.springframework.security.web.authentication.AuthenticationConverter;
|
|
import org.springframework.security.web.authentication.AuthenticationConverter;
|
|
@@ -81,8 +78,8 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|
import static org.mockito.ArgumentMatchers.any;
|
|
import static org.mockito.ArgumentMatchers.any;
|
|
import static org.mockito.ArgumentMatchers.eq;
|
|
import static org.mockito.ArgumentMatchers.eq;
|
|
import static org.mockito.Mockito.mock;
|
|
import static org.mockito.Mockito.mock;
|
|
-import static org.mockito.Mockito.when;
|
|
|
|
import static org.mockito.Mockito.verify;
|
|
import static org.mockito.Mockito.verify;
|
|
|
|
+import static org.mockito.Mockito.when;
|
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
|
|
|
|
|
@@ -95,11 +92,11 @@ public class OAuth2TokenRevocationTests {
|
|
private static final String DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI = "/oauth2/revoke";
|
|
private static final String DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI = "/oauth2/revoke";
|
|
private static EmbeddedDatabase db;
|
|
private static EmbeddedDatabase db;
|
|
private static JWKSource<SecurityContext> jwkSource;
|
|
private static JWKSource<SecurityContext> jwkSource;
|
|
- private static ProviderSettings providerSettings;
|
|
|
|
- private static AuthenticationConverter revocationRequestConverter;
|
|
|
|
|
|
+ private static AuthenticationConverter authenticationConverter;
|
|
private static AuthenticationProvider authenticationProvider;
|
|
private static AuthenticationProvider authenticationProvider;
|
|
- private static AuthenticationSuccessHandler revocationResponseHandler;
|
|
|
|
- private static AuthenticationFailureHandler errorResponseHandler;
|
|
|
|
|
|
+ private static AuthenticationSuccessHandler authenticationSuccessHandler;
|
|
|
|
+ private static AuthenticationFailureHandler authenticationFailureHandler;
|
|
|
|
+
|
|
@Rule
|
|
@Rule
|
|
public final SpringTestRule spring = new SpringTestRule();
|
|
public final SpringTestRule spring = new SpringTestRule();
|
|
|
|
|
|
@@ -119,11 +116,10 @@ public class OAuth2TokenRevocationTests {
|
|
public static void init() {
|
|
public static void init() {
|
|
JWKSet jwkSet = new JWKSet(TestJwks.DEFAULT_RSA_JWK);
|
|
JWKSet jwkSet = new JWKSet(TestJwks.DEFAULT_RSA_JWK);
|
|
jwkSource = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
|
|
jwkSource = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
|
|
- providerSettings = ProviderSettings.builder().tokenRevocationEndpoint("/test/revoke").build();
|
|
|
|
- revocationRequestConverter = mock(AuthenticationConverter.class);
|
|
|
|
|
|
+ authenticationConverter = mock(AuthenticationConverter.class);
|
|
authenticationProvider = mock(AuthenticationProvider.class);
|
|
authenticationProvider = mock(AuthenticationProvider.class);
|
|
- revocationResponseHandler = mock(AuthenticationSuccessHandler.class);
|
|
|
|
- errorResponseHandler = mock(AuthenticationFailureHandler.class);
|
|
|
|
|
|
+ authenticationSuccessHandler = mock(AuthenticationSuccessHandler.class);
|
|
|
|
+ authenticationFailureHandler = mock(AuthenticationFailureHandler.class);
|
|
db = new EmbeddedDatabaseBuilder()
|
|
db = new EmbeddedDatabaseBuilder()
|
|
.generateUniqueName(true)
|
|
.generateUniqueName(true)
|
|
.setType(EmbeddedDatabaseType.HSQL)
|
|
.setType(EmbeddedDatabaseType.HSQL)
|
|
@@ -173,42 +169,31 @@ public class OAuth2TokenRevocationTests {
|
|
public void requestWhenRevokeAccessTokenThenRevoked() throws Exception {
|
|
public void requestWhenRevokeAccessTokenThenRevoked() throws Exception {
|
|
this.spring.register(AuthorizationServerConfiguration.class).autowire();
|
|
this.spring.register(AuthorizationServerConfiguration.class).autowire();
|
|
|
|
|
|
- assertRevokeAccessTokenThenRevoked(DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- @Test
|
|
|
|
- public void requestWhenRevokeAccessTokenEndpointCustomizedThenUsed() throws Exception {
|
|
|
|
- this.spring.register(AuthorizationServerConfigurationCustomEndpoints.class).autowire();
|
|
|
|
-
|
|
|
|
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
|
|
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
|
|
this.registeredClientRepository.save(registeredClient);
|
|
this.registeredClientRepository.save(registeredClient);
|
|
- Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(
|
|
|
|
- registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());
|
|
|
|
|
|
|
|
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();
|
|
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();
|
|
OAuth2AccessToken token = authorization.getAccessToken().getToken();
|
|
OAuth2AccessToken token = authorization.getAccessToken().getToken();
|
|
OAuth2TokenType tokenType = OAuth2TokenType.ACCESS_TOKEN;
|
|
OAuth2TokenType tokenType = OAuth2TokenType.ACCESS_TOKEN;
|
|
this.authorizationService.save(authorization);
|
|
this.authorizationService.save(authorization);
|
|
|
|
|
|
- OAuth2TokenRevocationAuthenticationToken tokenRevocationAuthenticationResult =
|
|
|
|
- new OAuth2TokenRevocationAuthenticationToken(token, clientPrincipal);
|
|
|
|
-
|
|
|
|
- when(revocationRequestConverter.convert(any())).thenReturn(tokenRevocationAuthenticationResult);
|
|
|
|
- when(authenticationProvider.supports(eq(OAuth2TokenRevocationAuthenticationToken.class))).thenReturn(true);
|
|
|
|
- when(authenticationProvider.authenticate(any())).thenReturn(tokenRevocationAuthenticationResult);
|
|
|
|
-
|
|
|
|
- this.mvc.perform(post(providerSettings.getTokenRevocationEndpoint())
|
|
|
|
|
|
+ this.mvc.perform(post(DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI)
|
|
.params(getTokenRevocationRequestParameters(token, tokenType))
|
|
.params(getTokenRevocationRequestParameters(token, tokenType))
|
|
.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
|
|
.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
|
|
registeredClient.getClientId(), registeredClient.getClientSecret())))
|
|
registeredClient.getClientId(), registeredClient.getClientSecret())))
|
|
.andExpect(status().isOk());
|
|
.andExpect(status().isOk());
|
|
|
|
|
|
- verify(revocationRequestConverter).convert(any());
|
|
|
|
- verify(authenticationProvider).authenticate(eq(tokenRevocationAuthenticationResult));
|
|
|
|
- verify(revocationResponseHandler).onAuthenticationSuccess(any(), any(), eq(tokenRevocationAuthenticationResult));
|
|
|
|
|
|
+ OAuth2Authorization updatedAuthorization = this.authorizationService.findById(authorization.getId());
|
|
|
|
+ OAuth2Authorization.Token<OAuth2AccessToken> accessToken = updatedAuthorization.getAccessToken();
|
|
|
|
+ assertThat(accessToken.isInvalidated()).isTrue();
|
|
|
|
+ OAuth2Authorization.Token<OAuth2RefreshToken> refreshToken = updatedAuthorization.getRefreshToken();
|
|
|
|
+ assertThat(refreshToken.isInvalidated()).isFalse();
|
|
}
|
|
}
|
|
|
|
|
|
- private void assertRevokeAccessTokenThenRevoked(String tokenRevocationEndpointUri) throws Exception {
|
|
|
|
|
|
+ @Test
|
|
|
|
+ public void requestWhenTokenRevocationEndpointCustomizedThenUsed() throws Exception {
|
|
|
|
+ this.spring.register(AuthorizationServerConfigurationCustomTokenRevocationEndpoint.class).autowire();
|
|
|
|
+
|
|
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
|
|
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
|
|
this.registeredClientRepository.save(registeredClient);
|
|
this.registeredClientRepository.save(registeredClient);
|
|
|
|
|
|
@@ -217,17 +202,24 @@ public class OAuth2TokenRevocationTests {
|
|
OAuth2TokenType tokenType = OAuth2TokenType.ACCESS_TOKEN;
|
|
OAuth2TokenType tokenType = OAuth2TokenType.ACCESS_TOKEN;
|
|
this.authorizationService.save(authorization);
|
|
this.authorizationService.save(authorization);
|
|
|
|
|
|
- this.mvc.perform(post(tokenRevocationEndpointUri)
|
|
|
|
|
|
+ Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(
|
|
|
|
+ registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());
|
|
|
|
+ OAuth2TokenRevocationAuthenticationToken tokenRevocationAuthentication =
|
|
|
|
+ new OAuth2TokenRevocationAuthenticationToken(token, clientPrincipal);
|
|
|
|
+
|
|
|
|
+ when(authenticationConverter.convert(any())).thenReturn(tokenRevocationAuthentication);
|
|
|
|
+ when(authenticationProvider.supports(eq(OAuth2TokenRevocationAuthenticationToken.class))).thenReturn(true);
|
|
|
|
+ when(authenticationProvider.authenticate(any())).thenReturn(tokenRevocationAuthentication);
|
|
|
|
+
|
|
|
|
+ this.mvc.perform(post(DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI)
|
|
.params(getTokenRevocationRequestParameters(token, tokenType))
|
|
.params(getTokenRevocationRequestParameters(token, tokenType))
|
|
.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
|
|
.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
|
|
registeredClient.getClientId(), registeredClient.getClientSecret())))
|
|
registeredClient.getClientId(), registeredClient.getClientSecret())))
|
|
.andExpect(status().isOk());
|
|
.andExpect(status().isOk());
|
|
|
|
|
|
- OAuth2Authorization updatedAuthorization = this.authorizationService.findById(authorization.getId());
|
|
|
|
- OAuth2Authorization.Token<OAuth2AccessToken> accessToken = updatedAuthorization.getAccessToken();
|
|
|
|
- assertThat(accessToken.isInvalidated()).isTrue();
|
|
|
|
- OAuth2Authorization.Token<OAuth2RefreshToken> refreshToken = updatedAuthorization.getRefreshToken();
|
|
|
|
- assertThat(refreshToken.isInvalidated()).isFalse();
|
|
|
|
|
|
+ verify(authenticationConverter).convert(any());
|
|
|
|
+ verify(authenticationProvider).authenticate(eq(tokenRevocationAuthentication));
|
|
|
|
+ verify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), eq(tokenRevocationAuthentication));
|
|
}
|
|
}
|
|
|
|
|
|
private static MultiValueMap<String, String> getTokenRevocationRequestParameters(AbstractOAuth2Token token, OAuth2TokenType tokenType) {
|
|
private static MultiValueMap<String, String> getTokenRevocationRequestParameters(AbstractOAuth2Token token, OAuth2TokenType tokenType) {
|
|
@@ -302,22 +294,20 @@ public class OAuth2TokenRevocationTests {
|
|
}
|
|
}
|
|
|
|
|
|
@EnableWebSecurity
|
|
@EnableWebSecurity
|
|
- @Import(OAuth2AuthorizationServerConfiguration.class)
|
|
|
|
- static class AuthorizationServerConfigurationCustomEndpoints extends AuthorizationServerConfiguration {
|
|
|
|
|
|
+ static class AuthorizationServerConfigurationCustomTokenRevocationEndpoint extends AuthorizationServerConfiguration {
|
|
|
|
|
|
// @formatter:off
|
|
// @formatter:off
|
|
@Bean
|
|
@Bean
|
|
- @Order(Ordered.HIGHEST_PRECEDENCE)
|
|
|
|
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
|
|
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
|
|
OAuth2AuthorizationServerConfigurer<HttpSecurity> authorizationServerConfigurer =
|
|
OAuth2AuthorizationServerConfigurer<HttpSecurity> authorizationServerConfigurer =
|
|
new OAuth2AuthorizationServerConfigurer<>();
|
|
new OAuth2AuthorizationServerConfigurer<>();
|
|
authorizationServerConfigurer
|
|
authorizationServerConfigurer
|
|
.tokenRevocationEndpoint(tokenRevocationEndpoint ->
|
|
.tokenRevocationEndpoint(tokenRevocationEndpoint ->
|
|
tokenRevocationEndpoint
|
|
tokenRevocationEndpoint
|
|
- .revocationRequestConverter(revocationRequestConverter)
|
|
|
|
|
|
+ .revocationRequestConverter(authenticationConverter)
|
|
.authenticationProvider(authenticationProvider)
|
|
.authenticationProvider(authenticationProvider)
|
|
- .revocationResponseHandler(revocationResponseHandler)
|
|
|
|
- .errorResponseHandler(errorResponseHandler));
|
|
|
|
|
|
+ .revocationResponseHandler(authenticationSuccessHandler)
|
|
|
|
+ .errorResponseHandler(authenticationFailureHandler));
|
|
RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();
|
|
RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();
|
|
|
|
|
|
http
|
|
http
|
|
@@ -331,10 +321,6 @@ public class OAuth2TokenRevocationTests {
|
|
}
|
|
}
|
|
// @formatter:on
|
|
// @formatter:on
|
|
|
|
|
|
- @Bean
|
|
|
|
- ProviderSettings providerSettings() {
|
|
|
|
- return providerSettings;
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|