diff --git a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/OAuth2AuthorizationRequest.java b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/OAuth2AuthorizationRequest.java index 8522af771c..ff40a30785 100644 --- a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/OAuth2AuthorizationRequest.java +++ b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/OAuth2AuthorizationRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -440,7 +440,21 @@ public final class OAuth2AuthorizationRequest implements Serializable { Map parameters = getParameters(); // Not encoded this.parametersConsumer.accept(parameters); MultiValueMap queryParams = new LinkedMultiValueMap<>(); - parameters.forEach((k, v) -> queryParams.set(encodeQueryParam(k), encodeQueryParam(String.valueOf(v)))); // Encoded + parameters.forEach((k, v) -> { + String key = encodeQueryParam(k); + if (v instanceof Iterable) { + ((Iterable) v).forEach((value) -> queryParams.add(key, encodeQueryParam(String.valueOf(value)))); + } + else if (v != null && v.getClass().isArray()) { + Object[] values = (Object[]) v; + for (Object value : values) { + queryParams.add(key, encodeQueryParam(String.valueOf(value))); + } + } + else { + queryParams.set(key, encodeQueryParam(String.valueOf(v))); + } + }); UriBuilder uriBuilder = this.uriBuilderFactory.uriString(this.authorizationUri).queryParams(queryParams); return this.authorizationRequestUriFunction.apply(uriBuilder).toString(); } diff --git a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/OAuth2AuthorizationRequestTests.java b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/OAuth2AuthorizationRequestTests.java index 1a912d58a8..1c4365560d 100644 --- a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/OAuth2AuthorizationRequestTests.java +++ b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/OAuth2AuthorizationRequestTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -19,6 +19,7 @@ package org.springframework.security.oauth2.core.endpoint; import java.net.URI; import java.util.Arrays; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; @@ -319,4 +320,49 @@ public class OAuth2AuthorizationRequestTests { + "item%20amount=19.95%E2%82%AC&%C3%A2ge=4%C2%BD&item%20name=H%C3%85M%C3%96"); } + @Test + public void buildWhenAdditionalParametersContainsArrayThenProperlyEncoded() { + Map additionalParameters = new LinkedHashMap<>(); + additionalParameters.put("item1", new String[] { "1", "2" }); + additionalParameters.put("item2", "value2"); + OAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request() + .additionalParameters(additionalParameters) + .build(); + assertThat(authorizationRequest.getAuthorizationRequestUri()).isNotNull(); + assertThat(authorizationRequest.getAuthorizationRequestUri()) + .isEqualTo("https://example.com/login/oauth/authorize?response_type=code&client_id=client-id&state=state&" + + "redirect_uri=https://example.com/authorize/oauth2/code/registration-id&" + + "item1=1&item1=2&item2=value2"); + } + + @Test + public void buildWhenAdditionalParametersContainsIterableThenProperlyEncoded() { + Map additionalParameters = new LinkedHashMap<>(); + additionalParameters.put("item1", Arrays.asList("1", "2")); + additionalParameters.put("item2", "value2"); + OAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request() + .additionalParameters(additionalParameters) + .build(); + assertThat(authorizationRequest.getAuthorizationRequestUri()).isNotNull(); + assertThat(authorizationRequest.getAuthorizationRequestUri()) + .isEqualTo("https://example.com/login/oauth/authorize?response_type=code&client_id=client-id&state=state&" + + "redirect_uri=https://example.com/authorize/oauth2/code/registration-id&" + + "item1=1&item1=2&item2=value2"); + } + + @Test + public void buildWhenAdditionalParametersContainsNullThenAuthorizationRequestUriContainsNull() { + Map additionalParameters = new LinkedHashMap<>(); + additionalParameters.put("item1", null); + additionalParameters.put("item2", "value2"); + OAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request() + .additionalParameters(additionalParameters) + .build(); + assertThat(authorizationRequest.getAuthorizationRequestUri()).isNotNull(); + assertThat(authorizationRequest.getAuthorizationRequestUri()) + .isEqualTo("https://example.com/login/oauth/authorize?response_type=code&client_id=client-id&state=state&" + + "redirect_uri=https://example.com/authorize/oauth2/code/registration-id&" + + "item1=null&item2=value2"); + } + }