Sfoglia il codice sorgente

OAuth2AccessTokenResponseHttpMessageConverter handles JSON object parameters

Fixes gh-6463
Joe Grandja 5 anni fa
parent
commit
fb2bbd74dc

+ 11 - 4
oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/OAuth2AccessTokenResponseHttpMessageConverter.java

@@ -18,6 +18,7 @@ package org.springframework.security.oauth2.core.http.converter;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 import org.springframework.core.ParameterizedTypeReference;
 import org.springframework.core.convert.converter.Converter;
@@ -45,8 +46,8 @@ import org.springframework.util.Assert;
 public class OAuth2AccessTokenResponseHttpMessageConverter extends AbstractHttpMessageConverter<OAuth2AccessTokenResponse> {
 	private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
 
-	private static final ParameterizedTypeReference<Map<String, String>> PARAMETERIZED_RESPONSE_TYPE =
-			new ParameterizedTypeReference<Map<String, String>>() {};
+	private static final ParameterizedTypeReference<Map<String, Object>> PARAMETERIZED_RESPONSE_TYPE =
+			new ParameterizedTypeReference<Map<String, Object>>() {};
 
 	private GenericHttpMessageConverter<Object> jsonMessageConverter = HttpMessageConverters.getJsonMessageConverter();
 
@@ -70,10 +71,16 @@ public class OAuth2AccessTokenResponseHttpMessageConverter extends AbstractHttpM
 			throws HttpMessageNotReadableException {
 
 		try {
+			// gh-6463
+			// Parse parameter values as Object in order to handle potential JSON Object and then convert values to String
 			@SuppressWarnings("unchecked")
-			Map<String, String> tokenResponseParameters = (Map<String, String>) this.jsonMessageConverter.read(
+			Map<String, Object> tokenResponseParameters = (Map<String, Object>) this.jsonMessageConverter.read(
 					PARAMETERIZED_RESPONSE_TYPE.getType(), null, inputMessage);
-			return this.tokenResponseConverter.convert(tokenResponseParameters);
+			return this.tokenResponseConverter.convert(
+					tokenResponseParameters.entrySet().stream()
+							.collect(Collectors.toMap(
+									Map.Entry::getKey,
+									entry -> entry.getValue().toString())));
 		} catch (Exception ex) {
 			throw new HttpMessageNotReadableException("An error occurred reading the OAuth 2.0 Access Token Response: " +
 					ex.getMessage(), ex, inputMessage);

+ 34 - 1
oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/http/converter/OAuth2AccessTokenResponseHttpMessageConverterTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2018 the original author or authors.
+ * Copyright 2002-2020 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.
@@ -96,6 +96,39 @@ public class OAuth2AccessTokenResponseHttpMessageConverterTests {
 
 	}
 
+	// gh-6463
+	@Test
+	public void readInternalWhenSuccessfulTokenResponseWithObjectThenReadOAuth2AccessTokenResponse() {
+		String tokenResponse = "{\n" +
+				"	\"access_token\": \"access-token-1234\",\n" +
+				"   \"token_type\": \"bearer\",\n" +
+				"   \"expires_in\": 3600,\n" +
+				"   \"scope\": \"read write\",\n" +
+				"   \"refresh_token\": \"refresh-token-1234\",\n" +
+				"   \"custom_object_1\": {\"name1\": \"value1\"},\n" +
+				"   \"custom_object_2\": [\"value1\", \"value2\"],\n" +
+				"   \"custom_parameter_1\": \"custom-value-1\",\n" +
+				"   \"custom_parameter_2\": \"custom-value-2\"\n" +
+				"}\n";
+
+		MockClientHttpResponse response = new MockClientHttpResponse(
+				tokenResponse.getBytes(), HttpStatus.OK);
+
+		OAuth2AccessTokenResponse accessTokenResponse = this.messageConverter.readInternal(
+				OAuth2AccessTokenResponse.class, response);
+
+		assertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo("access-token-1234");
+		assertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);
+		assertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBeforeOrEqualTo(Instant.now().plusSeconds(3600));
+		assertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly("read", "write");
+		assertThat(accessTokenResponse.getRefreshToken().getTokenValue()).isEqualTo("refresh-token-1234");
+		assertThat(accessTokenResponse.getAdditionalParameters()).containsExactly(
+				entry("custom_object_1", "{name1=value1}"),
+				entry("custom_object_2", "[value1, value2]"),
+				entry("custom_parameter_1", "custom-value-1"),
+				entry("custom_parameter_2", "custom-value-2"));
+	}
+
 	@Test
 	public void readInternalWhenConversionFailsThenThrowHttpMessageNotReadableException() {
 		Converter tokenResponseConverter = mock(Converter.class);