From 27502676a1b49adf317c74abe86c3449ff762975 Mon Sep 17 00:00:00 2001 From: Jamie Tanna Date: Fri, 26 Nov 2021 09:44:46 +0000 Subject: [PATCH] Allow Multipart form POSTs to include access tokens As part of changes in 6db58cbf8a26296a15eb9f9c187176f099acaa77, we modified the logic for whether an access token could be sent as a parameter in a request's body. However, this doesn't account for Multipart POSTs, for instance where an image and an access token may be provided together. This isn't called out as an option for RFC 6750, but is being used in practice, so we should aim to support it. --- .../web/DefaultBearerTokenResolver.java | 8 ++++-- .../web/DefaultBearerTokenResolverTests.java | 27 ++++++++++++++++--- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/DefaultBearerTokenResolver.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/DefaultBearerTokenResolver.java index eb93ad5ed5c..2485d714e54 100644 --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/DefaultBearerTokenResolver.java +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/DefaultBearerTokenResolver.java @@ -128,14 +128,18 @@ private static String resolveFromRequestParameters(HttpServletRequest request) { private boolean isParameterTokenSupportedForRequest(final HttpServletRequest request) { return (("POST".equals(request.getMethod()) - && MediaType.APPLICATION_FORM_URLENCODED_VALUE.equals(request.getContentType())) + && isFormRequest(request)) || "GET".equals(request.getMethod())); } private boolean isParameterTokenEnabledForRequest(final HttpServletRequest request) { return ((this.allowFormEncodedBodyParameter && "POST".equals(request.getMethod()) - && MediaType.APPLICATION_FORM_URLENCODED_VALUE.equals(request.getContentType())) + && isFormRequest(request)) || (this.allowUriQueryParameter && "GET".equals(request.getMethod()))); } + private static boolean isFormRequest(final HttpServletRequest request) { + return MediaType.APPLICATION_FORM_URLENCODED_VALUE.equals(request.getContentType()) + || MediaType.MULTIPART_FORM_DATA_VALUE.equals(request.getContentType()); + } } diff --git a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/DefaultBearerTokenResolverTests.java b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/DefaultBearerTokenResolverTests.java index 9132ab2bd65..265cd25a148 100644 --- a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/DefaultBearerTokenResolverTests.java +++ b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/DefaultBearerTokenResolverTests.java @@ -149,13 +149,13 @@ public void resolveWhenRequestContainsTwoAccessTokenFormParametersThenAuthentica // gh-10326 @Test - public void resolveWhenParameterIsPresentInMultipartRequestAndFormParameterSupportedThenTokenIsNotResolved() { - this.resolver.setAllowFormEncodedBodyParameter(true); + public void resolveWhenRequestContainsTwoAccessTokenMultiPartFormParametersThenAuthenticationExceptionIsThrown() { MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("POST"); request.setContentType("multipart/form-data"); - request.addParameter("access_token", TEST_TOKEN); - assertThat(this.resolver.resolve(request)).isNull(); + request.addParameter("access_token", "token1", "token2"); + assertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.resolver.resolve(request)) + .withMessageContaining("Found multiple bearer tokens in the request"); } @Test @@ -177,6 +177,25 @@ public void resolveWhenFormParameterIsPresentAndNotSupportedThenTokenIsNotResolv assertThat(this.resolver.resolve(request)).isNull(); } + @Test + public void resolveWhenMultiPartFormParameterIsPresentAndSupportedThenTokenIsResolved() { + this.resolver.setAllowFormEncodedBodyParameter(true); + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setMethod("POST"); + request.setContentType("multipart/form-data"); + request.addParameter("access_token", TEST_TOKEN); + assertThat(this.resolver.resolve(request)).isEqualTo(TEST_TOKEN); + } + + @Test + public void resolveWhenMultiPartFormParameterIsPresentAndNotSupportedThenTokenIsNotResolved() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setMethod("POST"); + request.setContentType("multipart/form-data"); + request.addParameter("access_token", TEST_TOKEN); + assertThat(this.resolver.resolve(request)).isNull(); + } + @Test public void resolveWhenQueryParameterIsPresentAndSupportedThenTokenIsResolved() { this.resolver.setAllowUriQueryParameter(true);