Polish token revocation
Issue gh-490
This commit is contained in:
@@ -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.
|
||||||
@@ -48,11 +48,11 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
|
|||||||
* @see OAuth2TokenRevocationEndpointFilter
|
* @see OAuth2TokenRevocationEndpointFilter
|
||||||
*/
|
*/
|
||||||
public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth2Configurer {
|
public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth2Configurer {
|
||||||
private final List<AuthenticationProvider> authenticationProviders = new LinkedList<>();
|
private RequestMatcher requestMatcher;
|
||||||
private AuthenticationConverter revocationRequestConverter;
|
private AuthenticationConverter revocationRequestConverter;
|
||||||
|
private final List<AuthenticationProvider> authenticationProviders = new LinkedList<>();
|
||||||
private AuthenticationSuccessHandler revocationResponseHandler;
|
private AuthenticationSuccessHandler revocationResponseHandler;
|
||||||
private AuthenticationFailureHandler errorResponseHandler;
|
private AuthenticationFailureHandler errorResponseHandler;
|
||||||
private RequestMatcher requestMatcher;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restrict for internal use only.
|
* Restrict for internal use only.
|
||||||
@@ -130,13 +130,13 @@ public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth
|
|||||||
new OAuth2TokenRevocationEndpointFilter(
|
new OAuth2TokenRevocationEndpointFilter(
|
||||||
authenticationManager, providerSettings.getTokenRevocationEndpoint());
|
authenticationManager, providerSettings.getTokenRevocationEndpoint());
|
||||||
if (this.revocationRequestConverter != null) {
|
if (this.revocationRequestConverter != null) {
|
||||||
revocationEndpointFilter.setRevocationRequestConverter(this.revocationRequestConverter);
|
revocationEndpointFilter.setAuthenticationConverter(this.revocationRequestConverter);
|
||||||
}
|
}
|
||||||
if (this.revocationResponseHandler != null) {
|
if (this.revocationResponseHandler != null) {
|
||||||
revocationEndpointFilter.setRevocationResponseHandler(this.revocationResponseHandler);
|
revocationEndpointFilter.setAuthenticationSuccessHandler(this.revocationResponseHandler);
|
||||||
}
|
}
|
||||||
if (this.errorResponseHandler != null) {
|
if (this.errorResponseHandler != null) {
|
||||||
revocationEndpointFilter.setErrorResponseHandler(this.errorResponseHandler);
|
revocationEndpointFilter.setAuthenticationFailureHandler(this.errorResponseHandler);
|
||||||
}
|
}
|
||||||
builder.addFilterAfter(postProcess(revocationEndpointFilter), FilterSecurityInterceptor.class);
|
builder.addFilterAfter(postProcess(revocationEndpointFilter), FilterSecurityInterceptor.class);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -66,13 +66,12 @@ public final class OAuth2TokenRevocationEndpointFilter extends OncePerRequestFil
|
|||||||
|
|
||||||
private final AuthenticationManager authenticationManager;
|
private final AuthenticationManager authenticationManager;
|
||||||
private final RequestMatcher tokenRevocationEndpointMatcher;
|
private final RequestMatcher tokenRevocationEndpointMatcher;
|
||||||
private AuthenticationConverter revocationRequestConverter =
|
private AuthenticationConverter authenticationConverter =
|
||||||
new DefaultTokenRevocationAuthenticationConverter();
|
new DefaultTokenRevocationAuthenticationConverter();
|
||||||
private final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter =
|
private final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter =
|
||||||
new OAuth2ErrorHttpMessageConverter();
|
new OAuth2ErrorHttpMessageConverter();
|
||||||
|
private AuthenticationSuccessHandler authenticationSuccessHandler = this::sendRevocationSuccessResponse;
|
||||||
private AuthenticationSuccessHandler revocationResponseHandler = this::sendRevocationSuccessResponse;
|
private AuthenticationFailureHandler authenticationFailureHandler = this::sendErrorResponse;
|
||||||
private AuthenticationFailureHandler errorResponseHandler = this::sendErrorResponse;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an {@code OAuth2TokenRevocationEndpointFilter} using the provided parameters.
|
* Constructs an {@code OAuth2TokenRevocationEndpointFilter} using the provided parameters.
|
||||||
@@ -108,13 +107,13 @@ public final class OAuth2TokenRevocationEndpointFilter extends OncePerRequestFil
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Authentication tokenRevocationAuthentication = this.revocationRequestConverter.convert(request);
|
Authentication tokenRevocationAuthentication = this.authenticationConverter.convert(request);
|
||||||
|
Authentication tokenRevocationAuthenticationResult =
|
||||||
Authentication authentication = this.authenticationManager.authenticate(tokenRevocationAuthentication);
|
this.authenticationManager.authenticate(tokenRevocationAuthentication);
|
||||||
this.revocationResponseHandler.onAuthenticationSuccess(request, response, authentication);
|
this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, tokenRevocationAuthenticationResult);
|
||||||
} catch (OAuth2AuthenticationException ex) {
|
} catch (OAuth2AuthenticationException ex) {
|
||||||
SecurityContextHolder.clearContext();
|
SecurityContextHolder.clearContext();
|
||||||
this.errorResponseHandler.onAuthenticationFailure(request, response, ex);
|
this.authenticationFailureHandler.onAuthenticationFailure(request, response, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,35 +121,35 @@ public final class OAuth2TokenRevocationEndpointFilter extends OncePerRequestFil
|
|||||||
* Sets the {@link AuthenticationConverter} used when attempting to extract a Revoke Token Request from {@link HttpServletRequest}
|
* Sets the {@link AuthenticationConverter} used when attempting to extract a Revoke Token Request from {@link HttpServletRequest}
|
||||||
* to an instance of {@link OAuth2TokenRevocationAuthenticationToken} used for authenticating the client.
|
* to an instance of {@link OAuth2TokenRevocationAuthenticationToken} used for authenticating the client.
|
||||||
*
|
*
|
||||||
* @param revocationRequestConverter the {@link AuthenticationConverter} used when attempting to extract client credentials from {@link HttpServletRequest}
|
* @param authenticationConverter the {@link AuthenticationConverter} used when attempting to extract client credentials from {@link HttpServletRequest}
|
||||||
* @since 0.2.2
|
* @since 0.2.2
|
||||||
*/
|
*/
|
||||||
public void setRevocationRequestConverter(AuthenticationConverter revocationRequestConverter) {
|
public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {
|
||||||
Assert.notNull(revocationRequestConverter, "revocationRequestConverter cannot be null");
|
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
|
||||||
this.revocationRequestConverter = revocationRequestConverter;
|
this.authenticationConverter = authenticationConverter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2TokenRevocationAuthenticationToken}.
|
* Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2TokenRevocationAuthenticationToken}.
|
||||||
*
|
*
|
||||||
* @param revocationResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2TokenRevocationAuthenticationToken}
|
* @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2TokenRevocationAuthenticationToken}
|
||||||
* @since 0.2.2
|
* @since 0.2.2
|
||||||
*/
|
*/
|
||||||
public void setRevocationResponseHandler(AuthenticationSuccessHandler revocationResponseHandler) {
|
public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) {
|
||||||
Assert.notNull(revocationResponseHandler, "revocationResponseHandler cannot be null");
|
Assert.notNull(authenticationSuccessHandler, "authenticationSuccessHandler cannot be null");
|
||||||
this.revocationResponseHandler = revocationResponseHandler;
|
this.authenticationSuccessHandler = authenticationSuccessHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
|
* Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
|
||||||
* and returning the {@link OAuth2Error Error Response}.
|
* and returning the {@link OAuth2Error Error Response}.
|
||||||
*
|
*
|
||||||
* @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
|
* @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
|
||||||
* @since 0.2.2
|
* @since 0.2.2
|
||||||
*/
|
*/
|
||||||
public void setErrorResponseHandler(AuthenticationFailureHandler errorResponseHandler) {
|
public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
|
||||||
Assert.notNull(errorResponseHandler, "errorResponseHandler cannot be null");
|
Assert.notNull(authenticationFailureHandler, "authenticationFailureHandler cannot be null");
|
||||||
this.errorResponseHandler = errorResponseHandler;
|
this.authenticationFailureHandler = authenticationFailureHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendRevocationSuccessResponse(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
|
private void sendRevocationSuccessResponse(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
|
||||||
@@ -167,7 +166,7 @@ public final class OAuth2TokenRevocationEndpointFilter extends OncePerRequestFil
|
|||||||
|
|
||||||
private static void throwError(String errorCode, String parameterName) {
|
private static void throwError(String errorCode, String parameterName) {
|
||||||
OAuth2Error error = new OAuth2Error(errorCode, "OAuth 2.0 Token Revocation Parameter: " + parameterName,
|
OAuth2Error error = new OAuth2Error(errorCode, "OAuth 2.0 Token Revocation Parameter: " + parameterName,
|
||||||
"https://tools.ietf.org/html/rfc7009#section-2.1");
|
"https://datatracker.ietf.org/doc/html/rfc7009#section-2.1");
|
||||||
throw new OAuth2AuthenticationException(error);
|
throw new OAuth2AuthenticationException(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 authenticationConverter;
|
||||||
private static AuthenticationConverter revocationRequestConverter;
|
|
||||||
private static AuthenticationProvider authenticationProvider;
|
private static AuthenticationProvider authenticationProvider;
|
||||||
private static AuthenticationSuccessHandler revocationResponseHandler;
|
private static AuthenticationSuccessHandler authenticationSuccessHandler;
|
||||||
private static AuthenticationFailureHandler errorResponseHandler;
|
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();
|
authenticationConverter = mock(AuthenticationConverter.class);
|
||||||
revocationRequestConverter = mock(AuthenticationConverter.class);
|
|
||||||
authenticationProvider = mock(AuthenticationProvider.class);
|
authenticationProvider = mock(AuthenticationProvider.class);
|
||||||
revocationResponseHandler = mock(AuthenticationSuccessHandler.class);
|
authenticationSuccessHandler = mock(AuthenticationSuccessHandler.class);
|
||||||
errorResponseHandler = mock(AuthenticationFailureHandler.class);
|
authenticationFailureHandler = mock(AuthenticationFailureHandler.class);
|
||||||
db = new EmbeddedDatabaseBuilder()
|
db = new EmbeddedDatabaseBuilder()
|
||||||
.generateUniqueName(true)
|
.generateUniqueName(true)
|
||||||
.setType(EmbeddedDatabaseType.HSQL)
|
.setType(EmbeddedDatabaseType.HSQL)
|
||||||
@@ -173,42 +169,6 @@ 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();
|
|
||||||
this.registeredClientRepository.save(registeredClient);
|
|
||||||
Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(
|
|
||||||
registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());
|
|
||||||
|
|
||||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();
|
|
||||||
OAuth2AccessToken token = authorization.getAccessToken().getToken();
|
|
||||||
OAuth2TokenType tokenType = OAuth2TokenType.ACCESS_TOKEN;
|
|
||||||
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())
|
|
||||||
.params(getTokenRevocationRequestParameters(token, tokenType))
|
|
||||||
.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
|
|
||||||
registeredClient.getClientId(), registeredClient.getClientSecret())))
|
|
||||||
.andExpect(status().isOk());
|
|
||||||
|
|
||||||
verify(revocationRequestConverter).convert(any());
|
|
||||||
verify(authenticationProvider).authenticate(eq(tokenRevocationAuthenticationResult));
|
|
||||||
verify(revocationResponseHandler).onAuthenticationSuccess(any(), any(), eq(tokenRevocationAuthenticationResult));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertRevokeAccessTokenThenRevoked(String tokenRevocationEndpointUri) throws Exception {
|
|
||||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
|
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
|
||||||
this.registeredClientRepository.save(registeredClient);
|
this.registeredClientRepository.save(registeredClient);
|
||||||
|
|
||||||
@@ -217,7 +177,7 @@ 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)
|
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())))
|
||||||
@@ -230,6 +190,38 @@ public class OAuth2TokenRevocationTests {
|
|||||||
assertThat(refreshToken.isInvalidated()).isFalse();
|
assertThat(refreshToken.isInvalidated()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestWhenTokenRevocationEndpointCustomizedThenUsed() throws Exception {
|
||||||
|
this.spring.register(AuthorizationServerConfigurationCustomTokenRevocationEndpoint.class).autowire();
|
||||||
|
|
||||||
|
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
|
||||||
|
this.registeredClientRepository.save(registeredClient);
|
||||||
|
|
||||||
|
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();
|
||||||
|
OAuth2AccessToken token = authorization.getAccessToken().getToken();
|
||||||
|
OAuth2TokenType tokenType = OAuth2TokenType.ACCESS_TOKEN;
|
||||||
|
this.authorizationService.save(authorization);
|
||||||
|
|
||||||
|
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))
|
||||||
|
.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
|
||||||
|
registeredClient.getClientId(), registeredClient.getClientSecret())))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
|
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) {
|
||||||
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
|
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
|
||||||
parameters.set(OAuth2ParameterNames.TOKEN, token.getTokenValue());
|
parameters.set(OAuth2ParameterNames.TOKEN, token.getTokenValue());
|
||||||
@@ -302,22 +294,20 @@ public class OAuth2TokenRevocationTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
@Import(OAuth2AuthorizationServerConfiguration.class)
|
static class AuthorizationServerConfigurationCustomTokenRevocationEndpoint extends AuthorizationServerConfiguration {
|
||||||
static class AuthorizationServerConfigurationCustomEndpoints 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)
|
.revocationResponseHandler(authenticationSuccessHandler)
|
||||||
.errorResponseHandler(errorResponseHandler));
|
.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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -101,24 +101,24 @@ public class OAuth2TokenRevocationEndpointFilterTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setRevocationRequestConverterWhenNullThenThrowIllegalArgumentException() {
|
public void setAuthenticationConverterWhenNullThenThrowIllegalArgumentException() {
|
||||||
assertThatThrownBy(() -> this.filter.setRevocationRequestConverter(null))
|
assertThatThrownBy(() -> this.filter.setAuthenticationConverter(null))
|
||||||
.isInstanceOf(IllegalArgumentException.class)
|
.isInstanceOf(IllegalArgumentException.class)
|
||||||
.hasMessage("revocationRequestConverter cannot be null");
|
.hasMessage("authenticationConverter cannot be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setRevocationResponseHandlerWhenNullThenThrowIllegalArgumentException() {
|
public void setAuthenticationSuccessHandlerWhenNullThenThrowIllegalArgumentException() {
|
||||||
assertThatThrownBy(() -> this.filter.setRevocationResponseHandler(null))
|
assertThatThrownBy(() -> this.filter.setAuthenticationSuccessHandler(null))
|
||||||
.isInstanceOf(IllegalArgumentException.class)
|
.isInstanceOf(IllegalArgumentException.class)
|
||||||
.hasMessage("revocationResponseHandler cannot be null");
|
.hasMessage("authenticationSuccessHandler cannot be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setErrorResponseHandlerWhenNullThenThrowIllegalArgumentException() {
|
public void setAuthenticationFailureHandlerWhenNullThenThrowIllegalArgumentException() {
|
||||||
assertThatThrownBy(() -> this.filter.setErrorResponseHandler(null))
|
assertThatThrownBy(() -> this.filter.setAuthenticationFailureHandler(null))
|
||||||
.isInstanceOf(IllegalArgumentException.class)
|
.isInstanceOf(IllegalArgumentException.class)
|
||||||
.hasMessage("errorResponseHandler cannot be null");
|
.hasMessage("authenticationFailureHandler cannot be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -217,7 +217,7 @@ public class OAuth2TokenRevocationEndpointFilterTests {
|
|||||||
|
|
||||||
AuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);
|
AuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);
|
||||||
when(authenticationConverter.convert(any())).thenReturn(tokenRevocationAuthentication);
|
when(authenticationConverter.convert(any())).thenReturn(tokenRevocationAuthentication);
|
||||||
this.filter.setRevocationRequestConverter(authenticationConverter);
|
this.filter.setAuthenticationConverter(authenticationConverter);
|
||||||
|
|
||||||
when(this.authenticationManager.authenticate(any())).thenReturn(tokenRevocationAuthentication);
|
when(this.authenticationManager.authenticate(any())).thenReturn(tokenRevocationAuthentication);
|
||||||
|
|
||||||
@@ -236,9 +236,6 @@ public class OAuth2TokenRevocationEndpointFilterTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void doFilterWhenCustomAuthenticationSuccessHandlerThenUsed() throws Exception {
|
public void doFilterWhenCustomAuthenticationSuccessHandlerThenUsed() throws Exception {
|
||||||
AuthenticationSuccessHandler authenticationSuccessHandler = mock(AuthenticationSuccessHandler.class);
|
|
||||||
this.filter.setRevocationResponseHandler(authenticationSuccessHandler);
|
|
||||||
|
|
||||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
|
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
|
||||||
Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(
|
Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(
|
||||||
registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());
|
registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());
|
||||||
@@ -250,6 +247,9 @@ public class OAuth2TokenRevocationEndpointFilterTests {
|
|||||||
new OAuth2TokenRevocationAuthenticationToken(
|
new OAuth2TokenRevocationAuthenticationToken(
|
||||||
accessToken, clientPrincipal);
|
accessToken, clientPrincipal);
|
||||||
|
|
||||||
|
AuthenticationSuccessHandler authenticationSuccessHandler = mock(AuthenticationSuccessHandler.class);
|
||||||
|
this.filter.setAuthenticationSuccessHandler(authenticationSuccessHandler);
|
||||||
|
|
||||||
when(this.authenticationManager.authenticate(any())).thenReturn(tokenRevocationAuthentication);
|
when(this.authenticationManager.authenticate(any())).thenReturn(tokenRevocationAuthentication);
|
||||||
|
|
||||||
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
|
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
|
||||||
@@ -267,13 +267,13 @@ public class OAuth2TokenRevocationEndpointFilterTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void doFilterWhenCustomAuthenticationFailureHandlerThenUsed() throws Exception {
|
public void doFilterWhenCustomAuthenticationFailureHandlerThenUsed() throws Exception {
|
||||||
AuthenticationFailureHandler authenticationFailureHandler = mock(AuthenticationFailureHandler.class);
|
|
||||||
this.filter.setErrorResponseHandler(authenticationFailureHandler);
|
|
||||||
|
|
||||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
|
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
|
||||||
Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(
|
Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(
|
||||||
registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());
|
registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());
|
||||||
|
|
||||||
|
AuthenticationFailureHandler authenticationFailureHandler = mock(AuthenticationFailureHandler.class);
|
||||||
|
this.filter.setAuthenticationFailureHandler(authenticationFailureHandler);
|
||||||
|
|
||||||
when(this.authenticationManager.authenticate(any())).thenThrow(OAuth2AuthenticationException.class);
|
when(this.authenticationManager.authenticate(any())).thenThrow(OAuth2AuthenticationException.class);
|
||||||
|
|
||||||
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
|
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
|
||||||
|
|||||||
Reference in New Issue
Block a user