diff --git a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/web/reactive/function/OAuth2AccessTokenResponseBodyExtractor.java b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/web/reactive/function/OAuth2AccessTokenResponseBodyExtractor.java index 60b9b05849..6f760ac119 100644 --- a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/web/reactive/function/OAuth2AccessTokenResponseBodyExtractor.java +++ b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/web/reactive/function/OAuth2AccessTokenResponseBodyExtractor.java @@ -55,15 +55,15 @@ class OAuth2AccessTokenResponseBodyExtractor @Override public Mono extract(ReactiveHttpInputMessage inputMessage, Context context) { - ParameterizedTypeReference> type = new ParameterizedTypeReference>() {}; - BodyExtractor>, ReactiveHttpInputMessage> delegate = BodyExtractors.toMono(type); + ParameterizedTypeReference> type = new ParameterizedTypeReference>() {}; + BodyExtractor>, ReactiveHttpInputMessage> delegate = BodyExtractors.toMono(type); return delegate.extract(inputMessage, context) - .map(json -> parse(json)) + .map(OAuth2AccessTokenResponseBodyExtractor::parse) .flatMap(OAuth2AccessTokenResponseBodyExtractor::oauth2AccessTokenResponse) .map(OAuth2AccessTokenResponseBodyExtractor::oauth2AccessTokenResponse); } - private static TokenResponse parse(Map json) { + private static TokenResponse parse(Map json) { try { return TokenResponse.parse(new JSONObject(json)); } diff --git a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/web/reactive/function/OAuth2BodyExtractorsTests.java b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/web/reactive/function/OAuth2BodyExtractorsTests.java index 8b9b63f01e..0cab2af135 100644 --- a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/web/reactive/function/OAuth2BodyExtractorsTests.java +++ b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/web/reactive/function/OAuth2BodyExtractorsTests.java @@ -120,4 +120,33 @@ public class OAuth2BodyExtractorsTests { assertThat(result.getRefreshToken().getTokenValue()).isEqualTo("tGzv3JOkF0XG5Qx2TlKWIA"); assertThat(result.getAdditionalParameters()).containsEntry("example_parameter", "example_value"); } + + + @Test + // gh-6087 + public void oauth2AccessTokenResponseWhenMultipleAttributeTypesThenCreated() throws Exception { + BodyExtractor, ReactiveHttpInputMessage> extractor = OAuth2BodyExtractors + .oauth2AccessTokenResponse(); + + MockClientHttpResponse response = new MockClientHttpResponse(HttpStatus.OK); + response.getHeaders().setContentType(MediaType.APPLICATION_JSON); + response.setBody("{\n" + + " \"access_token\":\"2YotnFZFEjr1zCsicMWpAA\",\n" + + " \"token_type\":\"Bearer\",\n" + + " \"expires_in\":3600,\n" + + " \"refresh_token\":\"tGzv3JOkF0XG5Qx2TlKWIA\",\n" + + " \"subjson\":{}, \n" + + " \"list\":[] \n" + + " }"); + + Instant now = Instant.now(); + OAuth2AccessTokenResponse result = extractor.extract(response, this.context).block(); + + assertThat(result.getAccessToken().getTokenValue()).isEqualTo("2YotnFZFEjr1zCsicMWpAA"); + assertThat(result.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER); + assertThat(result.getAccessToken().getExpiresAt()).isBetween(now.plusSeconds(3600), now.plusSeconds(3600 + 2)); + assertThat(result.getRefreshToken().getTokenValue()).isEqualTo("tGzv3JOkF0XG5Qx2TlKWIA"); + assertThat(result.getAdditionalParameters().get("subjson")).isInstanceOfAny(Map.class); + assertThat(result.getAdditionalParameters().get("list")).isInstanceOfAny(List.class); + } }