Skip to content

Commit 1125b37

Browse files
sealtejgrandja
authored andcommitted
Add authenticationDetailsSource to OAuth2TokenRevocationEndpointFilter
Closes gh-1634
1 parent 6ceb56f commit 1125b37

File tree

2 files changed

+76
-17
lines changed

2 files changed

+76
-17
lines changed

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenRevocationEndpointFilter.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2023 the original author or authors.
2+
* Copyright 2020-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,6 +25,8 @@
2525
import org.springframework.core.log.LogMessage;
2626
import org.springframework.http.HttpMethod;
2727
import org.springframework.http.HttpStatus;
28+
import org.springframework.security.authentication.AbstractAuthenticationToken;
29+
import org.springframework.security.authentication.AuthenticationDetailsSource;
2830
import org.springframework.security.authentication.AuthenticationManager;
2931
import org.springframework.security.core.Authentication;
3032
import org.springframework.security.core.context.SecurityContextHolder;
@@ -37,6 +39,7 @@
3739
import org.springframework.security.web.authentication.AuthenticationConverter;
3840
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
3941
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
42+
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
4043
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
4144
import org.springframework.security.web.util.matcher.RequestMatcher;
4245
import org.springframework.util.Assert;
@@ -66,6 +69,8 @@ public final class OAuth2TokenRevocationEndpointFilter extends OncePerRequestFil
6669

6770
private final RequestMatcher tokenRevocationEndpointMatcher;
6871

72+
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
73+
6974
private AuthenticationConverter authenticationConverter;
7075

7176
private AuthenticationSuccessHandler authenticationSuccessHandler = this::sendRevocationSuccessResponse;
@@ -109,6 +114,11 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
109114

110115
try {
111116
Authentication tokenRevocationAuthentication = this.authenticationConverter.convert(request);
117+
if (tokenRevocationAuthentication instanceof AbstractAuthenticationToken) {
118+
((AbstractAuthenticationToken) tokenRevocationAuthentication)
119+
.setDetails(this.authenticationDetailsSource.buildDetails(request));
120+
}
121+
112122
Authentication tokenRevocationAuthenticationResult = this.authenticationManager
113123
.authenticate(tokenRevocationAuthentication);
114124
this.authenticationSuccessHandler.onAuthenticationSuccess(request, response,
@@ -123,6 +133,19 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
123133
}
124134
}
125135

136+
/**
137+
* Sets the {@link AuthenticationDetailsSource} used for building an authentication
138+
* details instance from {@link HttpServletRequest}.
139+
* @param authenticationDetailsSource the {@link AuthenticationDetailsSource} used for
140+
* building an authentication details instance from {@link HttpServletRequest}
141+
* @since 1.4
142+
*/
143+
public void setAuthenticationDetailsSource(
144+
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {
145+
Assert.notNull(authenticationDetailsSource, "authenticationDetailsSource cannot be null");
146+
this.authenticationDetailsSource = authenticationDetailsSource;
147+
}
148+
126149
/**
127150
* Sets the {@link AuthenticationConverter} used when attempting to extract a Revoke
128151
* Token Request from {@link HttpServletRequest} to an instance of

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenRevocationEndpointFilterTests.java

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2022 the original author or authors.
2+
* Copyright 2020-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,33 +15,23 @@
1515
*/
1616
package org.springframework.security.oauth2.server.authorization.web;
1717

18-
import java.time.Duration;
19-
import java.time.Instant;
20-
import java.util.Arrays;
21-
import java.util.HashSet;
22-
import java.util.function.Consumer;
23-
2418
import jakarta.servlet.FilterChain;
2519
import jakarta.servlet.http.HttpServletRequest;
2620
import jakarta.servlet.http.HttpServletResponse;
2721
import org.junit.jupiter.api.AfterEach;
2822
import org.junit.jupiter.api.BeforeEach;
2923
import org.junit.jupiter.api.Test;
30-
3124
import org.springframework.http.HttpStatus;
3225
import org.springframework.http.converter.HttpMessageConverter;
3326
import org.springframework.mock.http.client.MockClientHttpResponse;
3427
import org.springframework.mock.web.MockHttpServletRequest;
3528
import org.springframework.mock.web.MockHttpServletResponse;
29+
import org.springframework.security.authentication.AuthenticationDetailsSource;
3630
import org.springframework.security.authentication.AuthenticationManager;
3731
import org.springframework.security.core.Authentication;
3832
import org.springframework.security.core.context.SecurityContext;
3933
import org.springframework.security.core.context.SecurityContextHolder;
40-
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
41-
import org.springframework.security.oauth2.core.OAuth2AccessToken;
42-
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
43-
import org.springframework.security.oauth2.core.OAuth2Error;
44-
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
34+
import org.springframework.security.oauth2.core.*;
4535
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
4636
import org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;
4737
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
@@ -52,14 +42,19 @@
5242
import org.springframework.security.web.authentication.AuthenticationConverter;
5343
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
5444
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
45+
import org.springframework.security.web.authentication.WebAuthenticationDetails;
46+
47+
import java.time.Duration;
48+
import java.time.Instant;
49+
import java.util.Arrays;
50+
import java.util.HashSet;
51+
import java.util.function.Consumer;
5552

5653
import static org.assertj.core.api.Assertions.assertThat;
5754
import static org.assertj.core.api.Assertions.assertThatThrownBy;
5855
import static org.mockito.ArgumentMatchers.any;
5956
import static org.mockito.BDDMockito.given;
60-
import static org.mockito.Mockito.mock;
61-
import static org.mockito.Mockito.verify;
62-
import static org.mockito.Mockito.verifyNoInteractions;
57+
import static org.mockito.Mockito.*;
6358

6459
/**
6560
* Tests for {@link OAuth2TokenRevocationEndpointFilter}.
@@ -102,6 +97,13 @@ public void constructorWhenTokenRevocationEndpointUriNullThenThrowIllegalArgumen
10297
.hasMessage("tokenRevocationEndpointUri cannot be empty");
10398
}
10499

100+
@Test
101+
public void setAuthenticationDetailsSourceWhenNullThenThrowIllegalArgumentException() {
102+
assertThatThrownBy(() -> this.filter.setAuthenticationDetailsSource(null))
103+
.isInstanceOf(IllegalArgumentException.class)
104+
.hasMessage("authenticationDetailsSource cannot be null");
105+
}
106+
105107
@Test
106108
public void setAuthenticationConverterWhenNullThenThrowIllegalArgumentException() {
107109
assertThatThrownBy(() -> this.filter.setAuthenticationConverter(null))
@@ -198,6 +200,40 @@ public void doFilterWhenTokenRevocationRequestValidThenSuccessResponse() throws
198200
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
199201
}
200202

203+
@Test
204+
public void doFilterWhenCustomAuthenticationDetailsSourceThenUsed() throws Exception {
205+
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
206+
Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,
207+
ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());
208+
209+
MockHttpServletRequest request = createTokenRevocationRequest();
210+
211+
AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource = mock(
212+
AuthenticationDetailsSource.class);
213+
WebAuthenticationDetails webAuthenticationDetails = new WebAuthenticationDetails(request);
214+
given(authenticationDetailsSource.buildDetails(any())).willReturn(webAuthenticationDetails);
215+
this.filter.setAuthenticationDetailsSource(authenticationDetailsSource);
216+
217+
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "token",
218+
Instant.now(), Instant.now().plus(Duration.ofHours(1)),
219+
new HashSet<>(Arrays.asList("scope1", "scope2")));
220+
OAuth2TokenRevocationAuthenticationToken tokenRevocationAuthentication = new OAuth2TokenRevocationAuthenticationToken(
221+
accessToken, clientPrincipal);
222+
223+
given(this.authenticationManager.authenticate(any())).willReturn(tokenRevocationAuthentication);
224+
225+
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
226+
securityContext.setAuthentication(clientPrincipal);
227+
SecurityContextHolder.setContext(securityContext);
228+
229+
MockHttpServletResponse response = new MockHttpServletResponse();
230+
FilterChain filterChain = mock(FilterChain.class);
231+
232+
this.filter.doFilter(request, response, filterChain);
233+
234+
verify(authenticationDetailsSource).buildDetails(any());
235+
}
236+
201237
@Test
202238
public void doFilterWhenCustomAuthenticationConverterThenUsed() throws Exception {
203239
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();

0 commit comments

Comments
 (0)