Allow customizing client authentication failures with AuthenticationEntryPoint
Signed-off-by: Joe Grandja <10884212+jgrandja@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2020-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.oauth2.server.authorization.authentication;
|
||||
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link OAuth2AuthenticationException} that holds an
|
||||
* {@link OAuth2ClientAuthenticationToken} and is used by an
|
||||
* {@code AuthenticationFailureHandler} when handling a failed authentication attempt by
|
||||
* an OAuth 2.0 Client.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 1.5
|
||||
* @see OAuth2ClientAuthenticationToken
|
||||
*/
|
||||
public class OAuth2ClientAuthenticationException extends OAuth2AuthenticationException {
|
||||
|
||||
private final OAuth2ClientAuthenticationToken clientAuthentication;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2ClientAuthenticationException} using the provided
|
||||
* parameters.
|
||||
* @param error the {@link OAuth2Error OAuth 2.0 Error}
|
||||
* @param clientAuthentication the {@link OAuth2ClientAuthenticationToken OAuth 2.0
|
||||
* Client Authentication} request
|
||||
*/
|
||||
public OAuth2ClientAuthenticationException(OAuth2Error error,
|
||||
OAuth2ClientAuthenticationToken clientAuthentication) {
|
||||
super(error);
|
||||
Assert.notNull(clientAuthentication, "clientAuthentication cannot be null");
|
||||
this.clientAuthentication = clientAuthentication;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2ClientAuthenticationException} using the provided
|
||||
* parameters.
|
||||
* @param error the {@link OAuth2Error OAuth 2.0 Error}
|
||||
* @param cause the root cause
|
||||
* @param clientAuthentication the {@link OAuth2ClientAuthenticationToken OAuth 2.0
|
||||
* Client Authentication} request
|
||||
*/
|
||||
public OAuth2ClientAuthenticationException(OAuth2Error error, Throwable cause,
|
||||
OAuth2ClientAuthenticationToken clientAuthentication) {
|
||||
super(error, cause);
|
||||
Assert.notNull(clientAuthentication, "clientAuthentication cannot be null");
|
||||
this.clientAuthentication = clientAuthentication;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link OAuth2ClientAuthenticationToken OAuth 2.0 Client Authentication}
|
||||
* request.
|
||||
* @return the {@link OAuth2ClientAuthenticationToken}
|
||||
*/
|
||||
public OAuth2ClientAuthenticationToken getClientAuthentication() {
|
||||
return this.clientAuthentication;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,29 +24,25 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.server.ServletServerHttpResponse;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.authentication.AuthenticationDetailsSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||
import org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.ClientSecretAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.JwtClientAssertionAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationException;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.PublicClientAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.X509ClientCertificateAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.server.authorization.web.authentication.ClientSecretBasicAuthenticationConverter;
|
||||
import org.springframework.security.oauth2.server.authorization.web.authentication.ClientSecretPostAuthenticationConverter;
|
||||
import org.springframework.security.oauth2.server.authorization.web.authentication.JwtClientAssertionAuthenticationConverter;
|
||||
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ClientAuthenticationFailureHandler;
|
||||
import org.springframework.security.oauth2.server.authorization.web.authentication.PublicClientAuthenticationConverter;
|
||||
import org.springframework.security.oauth2.server.authorization.web.authentication.X509ClientCertificateAuthenticationConverter;
|
||||
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||
@@ -54,7 +50,6 @@ import org.springframework.security.web.authentication.AuthenticationFailureHand
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.authentication.DelegatingAuthenticationConverter;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
@@ -75,6 +70,7 @@ import org.springframework.web.filter.OncePerRequestFilter;
|
||||
* @see ClientSecretAuthenticationProvider
|
||||
* @see PublicClientAuthenticationConverter
|
||||
* @see PublicClientAuthenticationProvider
|
||||
* @see OAuth2ClientAuthenticationFailureHandler
|
||||
* @see <a target="_blank" href=
|
||||
* "https://datatracker.ietf.org/doc/html/rfc6749#section-2.3">Section 2.3 Client
|
||||
* Authentication</a>
|
||||
@@ -88,17 +84,13 @@ public final class OAuth2ClientAuthenticationFilter extends OncePerRequestFilter
|
||||
|
||||
private final RequestMatcher requestMatcher;
|
||||
|
||||
private final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter();
|
||||
|
||||
private final AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
|
||||
|
||||
private final BasicAuthenticationEntryPoint basicAuthenticationEntryPoint = new BasicAuthenticationEntryPoint();
|
||||
|
||||
private AuthenticationConverter authenticationConverter;
|
||||
|
||||
private AuthenticationSuccessHandler authenticationSuccessHandler = this::onAuthenticationSuccess;
|
||||
|
||||
private AuthenticationFailureHandler authenticationFailureHandler = this::onAuthenticationFailure;
|
||||
private AuthenticationFailureHandler authenticationFailureHandler = new OAuth2ClientAuthenticationFailureHandler();
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2ClientAuthenticationFilter} using the provided
|
||||
@@ -114,7 +106,6 @@ public final class OAuth2ClientAuthenticationFilter extends OncePerRequestFilter
|
||||
Assert.notNull(requestMatcher, "requestMatcher cannot be null");
|
||||
this.authenticationManager = authenticationManager;
|
||||
this.requestMatcher = requestMatcher;
|
||||
this.basicAuthenticationEntryPoint.setRealmName("default");
|
||||
// @formatter:off
|
||||
this.authenticationConverter = new DelegatingAuthenticationConverter(
|
||||
Arrays.asList(
|
||||
@@ -138,16 +129,16 @@ public final class OAuth2ClientAuthenticationFilter extends OncePerRequestFilter
|
||||
Authentication authenticationRequest = null;
|
||||
try {
|
||||
authenticationRequest = this.authenticationConverter.convert(request);
|
||||
if (authenticationRequest == null) {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT);
|
||||
}
|
||||
if (authenticationRequest instanceof AbstractAuthenticationToken authenticationToken) {
|
||||
authenticationToken.setDetails(this.authenticationDetailsSource.buildDetails(request));
|
||||
}
|
||||
if (authenticationRequest != null) {
|
||||
validateClientIdentifier(authenticationRequest);
|
||||
Authentication authenticationResult = this.authenticationManager.authenticate(authenticationRequest);
|
||||
this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, authenticationResult);
|
||||
}
|
||||
validateClientIdentifier(authenticationRequest);
|
||||
Authentication authenticationResult = this.authenticationManager.authenticate(authenticationRequest);
|
||||
this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, authenticationResult);
|
||||
filterChain.doFilter(request, response);
|
||||
|
||||
}
|
||||
catch (OAuth2AuthenticationException ex) {
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
@@ -160,8 +151,8 @@ public final class OAuth2ClientAuthenticationFilter extends OncePerRequestFilter
|
||||
else {
|
||||
this.authenticationFailureHandler.onAuthenticationFailure(request, response, ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -211,35 +202,6 @@ public final class OAuth2ClientAuthenticationFilter extends OncePerRequestFilter
|
||||
}
|
||||
}
|
||||
|
||||
private void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException authenticationException) throws IOException {
|
||||
|
||||
SecurityContextHolder.clearContext();
|
||||
|
||||
if (authenticationException instanceof OAuth2ClientAuthenticationException clientAuthenticationException) {
|
||||
OAuth2ClientAuthenticationToken clientAuthentication = clientAuthenticationException
|
||||
.getClientAuthentication();
|
||||
if (ClientAuthenticationMethod.CLIENT_SECRET_BASIC
|
||||
.equals(clientAuthentication.getClientAuthenticationMethod())) {
|
||||
this.basicAuthenticationEntryPoint.commence(request, response, authenticationException);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
OAuth2Error error = ((OAuth2AuthenticationException) authenticationException).getError();
|
||||
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
|
||||
if (OAuth2ErrorCodes.INVALID_CLIENT.equals(error.getErrorCode())) {
|
||||
httpResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
else {
|
||||
httpResponse.setStatusCode(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
// We don't want to reveal too much information to the caller so just return the
|
||||
// error code
|
||||
OAuth2Error errorResponse = new OAuth2Error(error.getErrorCode());
|
||||
this.errorHttpResponseConverter.write(errorResponse, null, httpResponse);
|
||||
}
|
||||
|
||||
private static void validateClientIdentifier(Authentication authentication) {
|
||||
if (!(authentication instanceof OAuth2ClientAuthenticationToken)) {
|
||||
return;
|
||||
@@ -261,21 +223,4 @@ public final class OAuth2ClientAuthenticationFilter extends OncePerRequestFilter
|
||||
}
|
||||
}
|
||||
|
||||
private static final class OAuth2ClientAuthenticationException extends OAuth2AuthenticationException {
|
||||
|
||||
private final OAuth2ClientAuthenticationToken clientAuthentication;
|
||||
|
||||
private OAuth2ClientAuthenticationException(OAuth2Error error, Throwable cause,
|
||||
OAuth2ClientAuthenticationToken clientAuthentication) {
|
||||
super(error, cause);
|
||||
Assert.notNull(clientAuthentication, "clientAuthentication cannot be null");
|
||||
this.clientAuthentication = clientAuthentication;
|
||||
}
|
||||
|
||||
private OAuth2ClientAuthenticationToken getClientAuthentication() {
|
||||
return this.clientAuthentication;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright 2020-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.oauth2.server.authorization.web.authentication;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.server.ServletServerHttpResponse;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||
import org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationException;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An implementation of an {@link AuthenticationFailureHandler} used for handling a failed
|
||||
* authentication attempt by an OAuth 2.0 Client and delegating to an
|
||||
* {@link AuthenticationEntryPoint} based on the {@link ClientAuthenticationMethod} used
|
||||
* by the client.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 1.5
|
||||
* @see AuthenticationFailureHandler
|
||||
* @see AuthenticationEntryPoint
|
||||
* @see OAuth2ClientAuthenticationFilter
|
||||
* @see OAuth2ClientAuthenticationException
|
||||
*/
|
||||
public final class OAuth2ClientAuthenticationFailureHandler implements AuthenticationFailureHandler {
|
||||
|
||||
private final Map<ClientAuthenticationMethod, AuthenticationEntryPoint> authenticationEntryPoints;
|
||||
|
||||
private AuthenticationEntryPoint defaultAuthenticationEntryPoint = new DefaultAuthenticationEntryPoint();
|
||||
|
||||
public OAuth2ClientAuthenticationFailureHandler() {
|
||||
this.authenticationEntryPoints = new HashMap<>();
|
||||
BasicAuthenticationEntryPoint basicAuthenticationEntryPoint = new BasicAuthenticationEntryPoint();
|
||||
basicAuthenticationEntryPoint.setRealmName("default");
|
||||
this.authenticationEntryPoints.put(ClientAuthenticationMethod.CLIENT_SECRET_BASIC,
|
||||
basicAuthenticationEntryPoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException authenticationException) throws IOException, ServletException {
|
||||
SecurityContextHolder.clearContext();
|
||||
AuthenticationEntryPoint authenticationEntryPoint = this.defaultAuthenticationEntryPoint;
|
||||
if (authenticationException instanceof OAuth2ClientAuthenticationException clientAuthenticationException) {
|
||||
OAuth2ClientAuthenticationToken clientAuthentication = clientAuthenticationException
|
||||
.getClientAuthentication();
|
||||
AuthenticationEntryPoint clientAuthenticationMethodEntryPoint = this.authenticationEntryPoints
|
||||
.get(clientAuthentication.getClientAuthenticationMethod());
|
||||
if (clientAuthenticationMethodEntryPoint != null) {
|
||||
// Override the default
|
||||
authenticationEntryPoint = clientAuthenticationMethodEntryPoint;
|
||||
}
|
||||
}
|
||||
authenticationEntryPoint.commence(request, response, authenticationException);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationEntryPoint} used for the specified
|
||||
* {@link ClientAuthenticationMethod}.
|
||||
* @param authenticationEntryPoint the {@link AuthenticationEntryPoint}
|
||||
* @param clientAuthenticationMethod the {@link ClientAuthenticationMethod}
|
||||
*/
|
||||
public void setAuthenticationEntryPointFor(AuthenticationEntryPoint authenticationEntryPoint,
|
||||
ClientAuthenticationMethod clientAuthenticationMethod) {
|
||||
Assert.notNull(authenticationEntryPoint, "authenticationEntryPoint cannot be null");
|
||||
Assert.notNull(clientAuthenticationMethod, "clientAuthenticationMethod cannot be null");
|
||||
this.authenticationEntryPoints.put(clientAuthenticationMethod, authenticationEntryPoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default {@link AuthenticationEntryPoint} used when unable to determine the
|
||||
* {@link ClientAuthenticationMethod} used by the client.
|
||||
* @param defaultAuthenticationEntryPoint the default {@link AuthenticationEntryPoint}
|
||||
*/
|
||||
public void setDefaultAuthenticationEntryPoint(AuthenticationEntryPoint defaultAuthenticationEntryPoint) {
|
||||
Assert.notNull(defaultAuthenticationEntryPoint, "defaultAuthenticationEntryPoint cannot be null");
|
||||
this.defaultAuthenticationEntryPoint = defaultAuthenticationEntryPoint;
|
||||
}
|
||||
|
||||
private static final class DefaultAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||
|
||||
private final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter();
|
||||
|
||||
@Override
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException exception) throws IOException {
|
||||
OAuth2Error error = ((OAuth2AuthenticationException) exception).getError();
|
||||
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
|
||||
if (OAuth2ErrorCodes.INVALID_CLIENT.equals(error.getErrorCode())) {
|
||||
httpResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
else {
|
||||
httpResponse.setStatusCode(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
// We don't want to reveal too much information to the caller
|
||||
// so just return the error code
|
||||
OAuth2Error errorResponse = new OAuth2Error(error.getErrorCode());
|
||||
this.errorHttpResponseConverter.write(errorResponse, null, httpResponse);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -45,8 +45,10 @@ import org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMe
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
||||
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
|
||||
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ClientAuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetails;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
|
||||
@@ -142,7 +144,7 @@ public class OAuth2ClientAuthenticationFilterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doFilterWhenRequestMatchesAndEmptyCredentialsThenNotProcessed() throws Exception {
|
||||
public void doFilterWhenRequestMatchesAndEmptyCredentialsThenInvalidClientError() throws Exception {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("POST", this.filterProcessesUrl);
|
||||
request.setServletPath(this.filterProcessesUrl);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
@@ -150,8 +152,13 @@ public class OAuth2ClientAuthenticationFilterTests {
|
||||
|
||||
this.filter.doFilter(request, response, filterChain);
|
||||
|
||||
verify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
verifyNoInteractions(filterChain);
|
||||
verifyNoInteractions(this.authenticationManager);
|
||||
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatus.UNAUTHORIZED.value());
|
||||
OAuth2Error error = readError(response);
|
||||
assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -237,6 +244,65 @@ public class OAuth2ClientAuthenticationFilterTests {
|
||||
assertThat(response.getHeader(HttpHeaders.WWW_AUTHENTICATE)).isEqualTo("Basic realm=\"default\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doFilterWhenRequestMatchesAndBadCredentialsAndCustomRealmConfiguredThenReturnChallenge()
|
||||
throws Exception {
|
||||
given(this.authenticationConverter.convert(any(HttpServletRequest.class)))
|
||||
.willReturn(new OAuth2ClientAuthenticationToken("clientId", ClientAuthenticationMethod.CLIENT_SECRET_BASIC,
|
||||
"invalid-secret", null));
|
||||
given(this.authenticationManager.authenticate(any(Authentication.class)))
|
||||
.willThrow(new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT));
|
||||
|
||||
BasicAuthenticationEntryPoint basicAuthenticationEntryPoint = new BasicAuthenticationEntryPoint();
|
||||
basicAuthenticationEntryPoint.setRealmName("oauth2-client");
|
||||
OAuth2ClientAuthenticationFailureHandler clientAuthenticationFailureHandler = new OAuth2ClientAuthenticationFailureHandler();
|
||||
clientAuthenticationFailureHandler.setAuthenticationEntryPointFor(basicAuthenticationEntryPoint,
|
||||
ClientAuthenticationMethod.CLIENT_SECRET_BASIC);
|
||||
this.filter.setAuthenticationFailureHandler(clientAuthenticationFailureHandler);
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("POST", this.filterProcessesUrl);
|
||||
request.setServletPath(this.filterProcessesUrl);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterChain filterChain = mock(FilterChain.class);
|
||||
|
||||
this.filter.doFilter(request, response, filterChain);
|
||||
|
||||
verifyNoInteractions(filterChain);
|
||||
verify(this.authenticationManager).authenticate(any());
|
||||
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatus.UNAUTHORIZED.value());
|
||||
assertThat(response.getHeader(HttpHeaders.WWW_AUTHENTICATE)).isEqualTo("Basic realm=\"oauth2-client\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doFilterWhenRequestMatchesAndNoCredentialsAndDefaultAuthenticationEntryPointOverriddenThenReturnChallenge()
|
||||
throws Exception {
|
||||
given(this.authenticationConverter.convert(any(HttpServletRequest.class))).willReturn(null);
|
||||
|
||||
BasicAuthenticationEntryPoint basicAuthenticationEntryPoint = new BasicAuthenticationEntryPoint();
|
||||
basicAuthenticationEntryPoint.setRealmName("oauth2-client");
|
||||
OAuth2ClientAuthenticationFailureHandler clientAuthenticationFailureHandler = new OAuth2ClientAuthenticationFailureHandler();
|
||||
clientAuthenticationFailureHandler.setAuthenticationEntryPointFor(basicAuthenticationEntryPoint,
|
||||
ClientAuthenticationMethod.CLIENT_SECRET_BASIC);
|
||||
clientAuthenticationFailureHandler.setDefaultAuthenticationEntryPoint(basicAuthenticationEntryPoint);
|
||||
this.filter.setAuthenticationFailureHandler(clientAuthenticationFailureHandler);
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("POST", this.filterProcessesUrl);
|
||||
request.setServletPath(this.filterProcessesUrl);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterChain filterChain = mock(FilterChain.class);
|
||||
|
||||
this.filter.doFilter(request, response, filterChain);
|
||||
|
||||
verifyNoInteractions(filterChain);
|
||||
verifyNoInteractions(this.authenticationManager);
|
||||
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatus.UNAUTHORIZED.value());
|
||||
assertThat(response.getHeader(HttpHeaders.WWW_AUTHENTICATE)).isEqualTo("Basic realm=\"oauth2-client\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doFilterWhenRequestMatchesAndValidCredentialsThenProcessed() throws Exception {
|
||||
final String remoteAddress = "remote-address";
|
||||
|
||||
Reference in New Issue
Block a user