From d716b8680447549302e3844968312deaa9a02aa5 Mon Sep 17 00:00:00 2001 From: Bodo Graumann <mail@bodograumann.de> Date: Wed, 25 Sep 2024 16:57:44 +0200 Subject: [PATCH 1/3] Avoid UriComponentsBuilder.fromUri Closes gh-15852 --- .../JwtDecoderProviderConfigurationUtils.java | 18 ++++++++++-------- ...veJwtDecoderProviderConfigurationUtils.java | 15 ++++++++------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtils.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtils.java index e69286c2d67..cfe0d0e538b 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtils.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtils.java @@ -45,6 +45,7 @@ import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; /** @@ -82,11 +83,12 @@ private JwtDecoderProviderConfigurationUtils() { } static Map<String, Object> getConfigurationForOidcIssuerLocation(String oidcIssuerLocation) { - return getConfiguration(oidcIssuerLocation, rest, oidc(URI.create(oidcIssuerLocation))); + UriComponents uri = UriComponentsBuilder.fromUriString(oidcIssuerLocation).build(); + return getConfiguration(oidcIssuerLocation, rest, oidc(uri)); } static Map<String, Object> getConfigurationForIssuerLocation(String issuer, RestOperations rest) { - URI uri = URI.create(issuer); + UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build(); return getConfiguration(issuer, rest, oidc(uri), oidcRfc8414(uri), oauth(uri)); } @@ -183,25 +185,25 @@ private static Map<String, Object> getConfiguration(String issuer, RestOperation throw new IllegalArgumentException(errorMessage); } - private static URI oidc(URI issuer) { + private static URI oidc(UriComponents issuer) { // @formatter:off - return UriComponentsBuilder.fromUri(issuer) + return UriComponentsBuilder.newInstance().uriComponents(issuer) .replacePath(issuer.getPath() + OIDC_METADATA_PATH) .build(Collections.emptyMap()); // @formatter:on } - private static URI oidcRfc8414(URI issuer) { + private static URI oidcRfc8414(UriComponents issuer) { // @formatter:off - return UriComponentsBuilder.fromUri(issuer) + return UriComponentsBuilder.newInstance().uriComponents(issuer) .replacePath(OIDC_METADATA_PATH + issuer.getPath()) .build(Collections.emptyMap()); // @formatter:on } - private static URI oauth(URI issuer) { + private static URI oauth(UriComponents issuer) { // @formatter:off - return UriComponentsBuilder.fromUri(issuer) + return UriComponentsBuilder.newInstance().uriComponents(issuer) .replacePath(OAUTH_METADATA_PATH + issuer.getPath()) .build(Collections.emptyMap()); // @formatter:on diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoderProviderConfigurationUtils.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoderProviderConfigurationUtils.java index 7b0b98e6eed..bda44358a34 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoderProviderConfigurationUtils.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoderProviderConfigurationUtils.java @@ -41,6 +41,7 @@ import org.springframework.util.Assert; import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.function.client.WebClientResponseException; +import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; final class ReactiveJwtDecoderProviderConfigurationUtils { @@ -93,29 +94,29 @@ else if (jwk.getKeyType() == KeyType.EC) { } static Mono<Map<String, Object>> getConfigurationForIssuerLocation(String issuer, WebClient web) { - URI uri = URI.create(issuer); + UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build(); return getConfiguration(issuer, web, oidc(uri), oidcRfc8414(uri), oauth(uri)); } - private static URI oidc(URI issuer) { + private static URI oidc(UriComponents issuer) { // @formatter:off - return UriComponentsBuilder.fromUri(issuer) + return UriComponentsBuilder.newInstance().uriComponents(issuer) .replacePath(issuer.getPath() + OIDC_METADATA_PATH) .build(Collections.emptyMap()); // @formatter:on } - private static URI oidcRfc8414(URI issuer) { + private static URI oidcRfc8414(UriComponents issuer) { // @formatter:off - return UriComponentsBuilder.fromUri(issuer) + return UriComponentsBuilder.newInstance().uriComponents(issuer) .replacePath(OIDC_METADATA_PATH + issuer.getPath()) .build(Collections.emptyMap()); // @formatter:on } - private static URI oauth(URI issuer) { + private static URI oauth(UriComponents issuer) { // @formatter:off - return UriComponentsBuilder.fromUri(issuer) + return UriComponentsBuilder.newInstance().uriComponents(issuer) .replacePath(OAUTH_METADATA_PATH + issuer.getPath()) .build(Collections.emptyMap()); // @formatter:on From 571135d2ab06b24d30c91249ca8c7d0b0285a346 Mon Sep 17 00:00:00 2001 From: Josh Cummings <3627351+jzheaux@users.noreply.github.com> Date: Thu, 20 Feb 2025 14:53:58 -0700 Subject: [PATCH 2/3] JwtDecoders Supports Hostnames with Underscores In the process of verifying gh-15852, another issue with URI was discovered. This commit adds tests to the uri-computing methods and changes them to use UriComponents instead of URI. Issue gh-15852 --- .../registration/ClientRegistrations.java | 14 +++++-- .../JwtDecoderProviderConfigurationUtils.java | 41 +++++++++---------- ...eJwtDecoderProviderConfigurationUtils.java | 36 ++++++++-------- ...ecoderProviderConfigurationUtilsTests.java | 13 ++++++ ...ecoderProviderConfigurationUtilsTests.java | 13 ++++++ 5 files changed, 74 insertions(+), 43 deletions(-) diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrations.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrations.java index 91517d640a5..d9ae09f3298 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrations.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrations.java @@ -37,6 +37,7 @@ import org.springframework.util.Assert; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; /** @@ -211,13 +212,18 @@ private static Supplier<ClientRegistration.Builder> oidc(URI issuer) { }; } - private static Supplier<ClientRegistration.Builder> oidcRfc8414(URI issuer) { + private static Supplier<ClientRegistration.Builder> oidcRfc8414(String issuer) { + URI uri = oidcRfc8414Uri(issuer); + return getRfc8414Builder(issuer, uri); + } + + static URI oidcRfc8414Uri(String issuer) { + UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build(); // @formatter:off - URI uri = UriComponentsBuilder.fromUri(issuer) - .replacePath(OIDC_METADATA_PATH + issuer.getPath()) + return UriComponentsBuilder.newInstance().uriComponents(uri) + .replacePath(OIDC_METADATA_PATH + uri.getPath()) .build(Collections.emptyMap()); // @formatter:on - return getRfc8414Builder(issuer, uri); } private static Supplier<ClientRegistration.Builder> oauth(URI issuer) { diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtils.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtils.java index cfe0d0e538b..d901743e371 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtils.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtils.java @@ -16,8 +16,6 @@ package org.springframework.security.oauth2.jwt; -import java.net.URI; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -83,13 +81,11 @@ private JwtDecoderProviderConfigurationUtils() { } static Map<String, Object> getConfigurationForOidcIssuerLocation(String oidcIssuerLocation) { - UriComponents uri = UriComponentsBuilder.fromUriString(oidcIssuerLocation).build(); - return getConfiguration(oidcIssuerLocation, rest, oidc(uri)); + return getConfiguration(oidcIssuerLocation, rest, oidc(oidcIssuerLocation)); } static Map<String, Object> getConfigurationForIssuerLocation(String issuer, RestOperations rest) { - UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build(); - return getConfiguration(issuer, rest, oidc(uri), oidcRfc8414(uri), oauth(uri)); + return getConfiguration(issuer, rest, oidc(issuer), oidcRfc8414(issuer), oauth(issuer)); } static Map<String, Object> getConfigurationForIssuerLocation(String issuer) { @@ -161,11 +157,11 @@ private static String getMetadataIssuer(Map<String, Object> configuration) { return "(unavailable)"; } - private static Map<String, Object> getConfiguration(String issuer, RestOperations rest, URI... uris) { + private static Map<String, Object> getConfiguration(String issuer, RestOperations rest, UriComponents... uris) { String errorMessage = "Unable to resolve the Configuration with the provided Issuer of " + "\"" + issuer + "\""; - for (URI uri : uris) { + for (UriComponents uri : uris) { try { - RequestEntity<Void> request = RequestEntity.get(uri).build(); + RequestEntity<Void> request = RequestEntity.get(uri.toUriString()).build(); ResponseEntity<Map<String, Object>> response = rest.exchange(request, STRING_OBJECT_MAP); Map<String, Object> configuration = response.getBody(); Assert.isTrue(configuration.get("jwks_uri") != null, "The public JWK set URI must not be null"); @@ -185,27 +181,30 @@ private static Map<String, Object> getConfiguration(String issuer, RestOperation throw new IllegalArgumentException(errorMessage); } - private static URI oidc(UriComponents issuer) { + static UriComponents oidc(String issuer) { + UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build(); // @formatter:off - return UriComponentsBuilder.newInstance().uriComponents(issuer) - .replacePath(issuer.getPath() + OIDC_METADATA_PATH) - .build(Collections.emptyMap()); + return UriComponentsBuilder.newInstance().uriComponents(uri) + .replacePath(uri.getPath() + OIDC_METADATA_PATH) + .build(); // @formatter:on } - private static URI oidcRfc8414(UriComponents issuer) { + static UriComponents oidcRfc8414(String issuer) { + UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build(); // @formatter:off - return UriComponentsBuilder.newInstance().uriComponents(issuer) - .replacePath(OIDC_METADATA_PATH + issuer.getPath()) - .build(Collections.emptyMap()); + return UriComponentsBuilder.newInstance().uriComponents(uri) + .replacePath(OIDC_METADATA_PATH + uri.getPath()) + .build(); // @formatter:on } - private static URI oauth(UriComponents issuer) { + static UriComponents oauth(String issuer) { + UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build(); // @formatter:off - return UriComponentsBuilder.newInstance().uriComponents(issuer) - .replacePath(OAUTH_METADATA_PATH + issuer.getPath()) - .build(Collections.emptyMap()); + return UriComponentsBuilder.newInstance().uriComponents(uri) + .replacePath(OAUTH_METADATA_PATH + uri.getPath()) + .build(); // @formatter:on } diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoderProviderConfigurationUtils.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoderProviderConfigurationUtils.java index bda44358a34..d9506d900df 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoderProviderConfigurationUtils.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoderProviderConfigurationUtils.java @@ -16,8 +16,6 @@ package org.springframework.security.oauth2.jwt; -import java.net.URI; -import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -94,38 +92,40 @@ else if (jwk.getKeyType() == KeyType.EC) { } static Mono<Map<String, Object>> getConfigurationForIssuerLocation(String issuer, WebClient web) { - UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build(); - return getConfiguration(issuer, web, oidc(uri), oidcRfc8414(uri), oauth(uri)); + return getConfiguration(issuer, web, oidc(issuer), oidcRfc8414(issuer), oauth(issuer)); } - private static URI oidc(UriComponents issuer) { + static UriComponents oidc(String issuer) { + UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build(); // @formatter:off - return UriComponentsBuilder.newInstance().uriComponents(issuer) - .replacePath(issuer.getPath() + OIDC_METADATA_PATH) - .build(Collections.emptyMap()); + return UriComponentsBuilder.newInstance().uriComponents(uri) + .replacePath(uri.getPath() + OIDC_METADATA_PATH) + .build(); // @formatter:on } - private static URI oidcRfc8414(UriComponents issuer) { + static UriComponents oidcRfc8414(String issuer) { + UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build(); // @formatter:off - return UriComponentsBuilder.newInstance().uriComponents(issuer) - .replacePath(OIDC_METADATA_PATH + issuer.getPath()) - .build(Collections.emptyMap()); + return UriComponentsBuilder.newInstance().uriComponents(uri) + .replacePath(OIDC_METADATA_PATH + uri.getPath()) + .build(); // @formatter:on } - private static URI oauth(UriComponents issuer) { + static UriComponents oauth(String issuer) { + UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build(); // @formatter:off - return UriComponentsBuilder.newInstance().uriComponents(issuer) - .replacePath(OAUTH_METADATA_PATH + issuer.getPath()) - .build(Collections.emptyMap()); + return UriComponentsBuilder.newInstance().uriComponents(uri) + .replacePath(OAUTH_METADATA_PATH + uri.getPath()) + .build(); // @formatter:on } - private static Mono<Map<String, Object>> getConfiguration(String issuer, WebClient web, URI... uris) { + private static Mono<Map<String, Object>> getConfiguration(String issuer, WebClient web, UriComponents... uris) { String errorMessage = "Unable to resolve the Configuration with the provided Issuer of " + "\"" + issuer + "\""; return Flux.just(uris) - .concatMap((uri) -> web.get().uri(uri).retrieve().bodyToMono(STRING_OBJECT_MAP)) + .concatMap((uri) -> web.get().uri(uri.toUriString()).retrieve().bodyToMono(STRING_OBJECT_MAP)) .flatMap((configuration) -> { if (configuration.get("jwks_uri") == null) { return Mono.error(() -> new IllegalArgumentException("The public JWK set URI must not be null")); diff --git a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtilsTests.java b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtilsTests.java index 1b88d6f3f35..ec87e7a1de1 100644 --- a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtilsTests.java +++ b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtilsTests.java @@ -35,6 +35,7 @@ import org.springframework.security.oauth2.jose.TestKeys; import org.springframework.security.oauth2.jose.jws.JwsAlgorithms; import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm; +import org.springframework.web.util.UriComponents; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; @@ -90,4 +91,16 @@ public void getSignatureAlgorithmsWhenAlgorithmThenParses() throws Exception { assertThat(algorithms).containsOnly(SignatureAlgorithm.RS256); } + // gh-15852 + @Test + public void oidcWhenHostContainsUnderscoreThenRetains() { + UriComponents oidc = JwtDecoderProviderConfigurationUtils.oidc("https://elated_sutherland:8080/path"); + assertThat(oidc.getHost()).isEqualTo("elated_sutherland"); + UriComponents oauth = JwtDecoderProviderConfigurationUtils.oauth("https://elated_sutherland:8080/path"); + assertThat(oauth.getHost()).isEqualTo("elated_sutherland"); + UriComponents oidcRfc8414 = JwtDecoderProviderConfigurationUtils + .oidcRfc8414("https://elated_sutherland:8080/path"); + assertThat(oidcRfc8414.getHost()).isEqualTo("elated_sutherland"); + } + } diff --git a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoderProviderConfigurationUtilsTests.java b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoderProviderConfigurationUtilsTests.java index 30a0affd144..12ccd7c46fb 100644 --- a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoderProviderConfigurationUtilsTests.java +++ b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoderProviderConfigurationUtilsTests.java @@ -37,6 +37,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import static org.assertj.core.api.Assertions.assertThat; @@ -227,6 +228,18 @@ public void issuerWhenOidcFallbackRequestedIssuerIsUnresponsiveThenThrowsIllegal // @formatter:on } + // gh-15852 + @Test + public void oidcWhenHostContainsUnderscoreThenRetains() { + UriComponents oidc = ReactiveJwtDecoderProviderConfigurationUtils.oidc("https://elated_sutherland:8080/path"); + assertThat(oidc.getHost()).isEqualTo("elated_sutherland"); + UriComponents oauth = ReactiveJwtDecoderProviderConfigurationUtils.oauth("https://elated_sutherland:8080/path"); + assertThat(oauth.getHost()).isEqualTo("elated_sutherland"); + UriComponents oidcRfc8414 = ReactiveJwtDecoderProviderConfigurationUtils + .oidcRfc8414("https://elated_sutherland:8080/path"); + assertThat(oidcRfc8414.getHost()).isEqualTo("elated_sutherland"); + } + private void prepareConfigurationResponse() { String body = String.format(DEFAULT_RESPONSE_TEMPLATE, this.issuer, this.issuer); prepareConfigurationResponse(body); From 49e5d3796e019d8ba03c68481e7c1a4327cad751 Mon Sep 17 00:00:00 2001 From: Josh Cummings <3627351+jzheaux@users.noreply.github.com> Date: Thu, 20 Feb 2025 15:08:37 -0700 Subject: [PATCH 3/3] ClientRegistrations supports hostnames with underscores Issue gh-15852 --- .../registration/ClientRegistrations.java | 56 +++++++++++-------- .../ClientRegistrationsTests.java | 12 ++++ 2 files changed, 44 insertions(+), 24 deletions(-) diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrations.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrations.java index d9ae09f3298..54efef5107d 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrations.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrations.java @@ -17,7 +17,6 @@ package org.springframework.security.oauth2.client.registration; import java.net.URI; -import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -146,7 +145,7 @@ public static ClientRegistration.Builder fromOidcConfiguration(Map<String, Objec */ public static ClientRegistration.Builder fromOidcIssuerLocation(String issuer) { Assert.hasText(issuer, "issuer cannot be empty"); - return getBuilder(issuer, oidc(URI.create(issuer))); + return getBuilder(issuer, oidc(issuer)); } /** @@ -189,21 +188,17 @@ public static ClientRegistration.Builder fromOidcIssuerLocation(String issuer) { */ public static ClientRegistration.Builder fromIssuerLocation(String issuer) { Assert.hasText(issuer, "issuer cannot be empty"); - URI uri = URI.create(issuer); - return getBuilder(issuer, oidc(uri), oidcRfc8414(uri), oauth(uri)); + return getBuilder(issuer, oidc(issuer), oidcRfc8414(issuer), oauth(issuer)); } - private static Supplier<ClientRegistration.Builder> oidc(URI issuer) { - // @formatter:off - URI uri = UriComponentsBuilder.fromUri(issuer) - .replacePath(issuer.getPath() + OIDC_METADATA_PATH) - .build(Collections.emptyMap()); + static Supplier<ClientRegistration.Builder> oidc(String issuer) { + UriComponents uri = oidcUri(issuer); // @formatter:on return () -> { - RequestEntity<Void> request = RequestEntity.get(uri).build(); + RequestEntity<Void> request = RequestEntity.get(uri.toUriString()).build(); Map<String, Object> configuration = rest.exchange(request, typeReference).getBody(); OIDCProviderMetadata metadata = parse(configuration, OIDCProviderMetadata::parse); - ClientRegistration.Builder builder = withProviderConfiguration(metadata, issuer.toASCIIString()) + ClientRegistration.Builder builder = withProviderConfiguration(metadata, issuer) .jwkSetUri(metadata.getJWKSetURI().toASCIIString()); if (metadata.getUserInfoEndpointURI() != null) { builder.userInfoUri(metadata.getUserInfoEndpointURI().toASCIIString()); @@ -212,35 +207,48 @@ private static Supplier<ClientRegistration.Builder> oidc(URI issuer) { }; } - private static Supplier<ClientRegistration.Builder> oidcRfc8414(String issuer) { - URI uri = oidcRfc8414Uri(issuer); + static UriComponents oidcUri(String issuer) { + UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build(); + // @formatter:off + return UriComponentsBuilder.newInstance().uriComponents(uri) + .replacePath(uri.getPath() + OIDC_METADATA_PATH) + .build(); + } + + static Supplier<ClientRegistration.Builder> oidcRfc8414(String issuer) { + UriComponents uri = oidcRfc8414Uri(issuer); + // @formatter:on return getRfc8414Builder(issuer, uri); } - static URI oidcRfc8414Uri(String issuer) { + static UriComponents oidcRfc8414Uri(String issuer) { UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build(); // @formatter:off return UriComponentsBuilder.newInstance().uriComponents(uri) .replacePath(OIDC_METADATA_PATH + uri.getPath()) - .build(Collections.emptyMap()); - // @formatter:on + .build(); + } + + static Supplier<ClientRegistration.Builder> oauth(String issuer) { + UriComponents uri = oauthUri(issuer); + return getRfc8414Builder(issuer, uri); } - private static Supplier<ClientRegistration.Builder> oauth(URI issuer) { + static UriComponents oauthUri(String issuer) { + UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build(); // @formatter:off - URI uri = UriComponentsBuilder.fromUri(issuer) - .replacePath(OAUTH_METADATA_PATH + issuer.getPath()) - .build(Collections.emptyMap()); + return UriComponentsBuilder.newInstance().uriComponents(uri) + .replacePath(OAUTH_METADATA_PATH + uri.getPath()) + .build(); // @formatter:on - return getRfc8414Builder(issuer, uri); } - private static Supplier<ClientRegistration.Builder> getRfc8414Builder(URI issuer, URI uri) { + private static Supplier<ClientRegistration.Builder> getRfc8414Builder(String issuer, UriComponents uri) { return () -> { - RequestEntity<Void> request = RequestEntity.get(uri).build(); + RequestEntity<Void> request = RequestEntity.get(uri.toUriString()).build(); Map<String, Object> configuration = rest.exchange(request, typeReference).getBody(); AuthorizationServerMetadata metadata = parse(configuration, AuthorizationServerMetadata::parse); - ClientRegistration.Builder builder = withProviderConfiguration(metadata, issuer.toASCIIString()); + ClientRegistration.Builder builder = withProviderConfiguration(metadata, issuer); URI jwkSetUri = metadata.getJWKSetURI(); if (jwkSetUri != null) { builder.jwkSetUri(jwkSetUri.toASCIIString()); diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationsTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationsTests.java index 59c0fb05288..f66fe394548 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationsTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationsTests.java @@ -34,6 +34,7 @@ import org.springframework.http.MediaType; import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; +import org.springframework.web.util.UriComponents; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; @@ -569,6 +570,17 @@ public void issuerWhenOidcConfigurationTlsClientAuthMethodThenSuccess() throws E .isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); } + // gh-15852 + @Test + public void oidcWhenHostContainsUnderscoreThenRetains() { + UriComponents oidc = ClientRegistrations.oidcUri("https://elated_sutherland:8080/path"); + assertThat(oidc.getHost()).isEqualTo("elated_sutherland"); + UriComponents oauth = ClientRegistrations.oauthUri("https://elated_sutherland:8080/path"); + assertThat(oauth.getHost()).isEqualTo("elated_sutherland"); + UriComponents oidcRfc8414 = ClientRegistrations.oidcRfc8414Uri("https://elated_sutherland:8080/path"); + assertThat(oidcRfc8414.getHost()).isEqualTo("elated_sutherland"); + } + private ClientRegistration.Builder registration(String path) throws Exception { this.issuer = createIssuerFromServer(path); this.response.put("issuer", this.issuer);