From 3d9616fba6c078e048a9c944897b93e768239e54 Mon Sep 17 00:00:00 2001 From: Khaled Hamlaoui Date: Thu, 21 Oct 2021 16:11:40 +0200 Subject: [PATCH] Allow custom OAuth2ErrorHttpMessageConverter with OAuth2ErrorResponseErrorHandler Closes gh-10425 --- .../http/OAuth2ErrorResponseErrorHandler.java | 15 ++++++++++- .../OAuth2ErrorResponseErrorHandlerTests.java | 27 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/http/OAuth2ErrorResponseErrorHandler.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/http/OAuth2ErrorResponseErrorHandler.java index 3f23c7d5836..5e14fb66a19 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/http/OAuth2ErrorResponseErrorHandler.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/http/OAuth2ErrorResponseErrorHandler.java @@ -23,10 +23,12 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.client.ClientHttpResponse; +import org.springframework.http.converter.HttpMessageConverter; import org.springframework.security.oauth2.core.OAuth2AuthorizationException; 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.util.Assert; import org.springframework.util.StringUtils; import org.springframework.web.client.DefaultResponseErrorHandler; import org.springframework.web.client.ResponseErrorHandler; @@ -41,7 +43,7 @@ */ public class OAuth2ErrorResponseErrorHandler implements ResponseErrorHandler { - private final OAuth2ErrorHttpMessageConverter oauth2ErrorConverter = new OAuth2ErrorHttpMessageConverter(); + private HttpMessageConverter oauth2ErrorConverter = new OAuth2ErrorHttpMessageConverter(); private final ResponseErrorHandler defaultErrorHandler = new DefaultResponseErrorHandler(); @@ -89,4 +91,15 @@ private BearerTokenError getBearerToken(String wwwAuthenticateHeader) { } } + /** + * Sets the {@link HttpMessageConverter} for an OAuth 2.0 Error. + * @param oauth2ErrorConverter A {@link HttpMessageConverter} for an + * {@link OAuth2Error OAuth 2.0 Error}. + * @since 5.7 + */ + public final void setErrorConverter(HttpMessageConverter oauth2ErrorConverter) { + Assert.notNull(oauth2ErrorConverter, "oauth2ErrorConverter cannot be null"); + this.oauth2ErrorConverter = oauth2ErrorConverter; + } + } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/http/OAuth2ErrorResponseErrorHandlerTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/http/OAuth2ErrorResponseErrorHandlerTests.java index f5a84834949..aed0aad7fa8 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/http/OAuth2ErrorResponseErrorHandlerTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/http/OAuth2ErrorResponseErrorHandlerTests.java @@ -23,12 +23,19 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.client.ClientHttpResponse; +import org.springframework.http.converter.HttpMessageConverter; import org.springframework.mock.http.MockHttpInputMessage; import org.springframework.mock.http.client.MockClientHttpResponse; import org.springframework.security.oauth2.core.OAuth2AuthorizationException; +import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.web.client.UnknownHttpStatusCodeException; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; /** * Tests for {@link OAuth2ErrorResponseErrorHandler}. @@ -53,6 +60,26 @@ public void handleErrorWhenErrorResponseBodyThenHandled() { .withMessage("[unauthorized_client] The client is not authorized"); } + @Test + public void handleErrorWhenOAuth2ErrorConverterSetThenCalled() throws IOException { + HttpMessageConverter oauth2ErrorConverter = mock(HttpMessageConverter.class); + this.errorHandler.setErrorConverter(oauth2ErrorConverter); + // @formatter:off + String errorResponse = "{\n" + + " \"errorCode\": \"unauthorized_client\",\n" + + " \"errorSummary\": \"The client is not authorized\"\n" + + "}\n"; + // @formatter:on + MockClientHttpResponse response = new MockClientHttpResponse(errorResponse.getBytes(), HttpStatus.BAD_REQUEST); + given(oauth2ErrorConverter.read(any(), any())) + .willReturn(new OAuth2Error("unauthorized_client", "The client is not authorized", null)); + + assertThatExceptionOfType(OAuth2AuthorizationException.class) + .isThrownBy(() -> this.errorHandler.handleError(response)) + .withMessage("[unauthorized_client] The client is not authorized"); + verify(oauth2ErrorConverter).read(eq(OAuth2Error.class), eq(response)); + } + @Test public void handleErrorWhenErrorResponseWwwAuthenticateHeaderThenHandled() { String wwwAuthenticateHeader = "Bearer realm=\"auth-realm\" error=\"insufficient_scope\" error_description=\"The access token expired\"";