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..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; @@ -37,6 +36,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; /** @@ -145,7 +145,7 @@ public static ClientRegistration.Builder fromOidcConfiguration(Map oidc(URI issuer) { - // @formatter:off - URI uri = UriComponentsBuilder.fromUri(issuer) - .replacePath(issuer.getPath() + OIDC_METADATA_PATH) - .build(Collections.emptyMap()); + static Supplier oidc(String issuer) { + UriComponents uri = oidcUri(issuer); // @formatter:on return () -> { - RequestEntity request = RequestEntity.get(uri).build(); + RequestEntity request = RequestEntity.get(uri.toUriString()).build(); Map 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()); @@ -211,30 +207,48 @@ private static Supplier oidc(URI issuer) { }; } - private static Supplier oidcRfc8414(URI issuer) { + static UriComponents oidcUri(String issuer) { + UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build(); // @formatter:off - URI uri = UriComponentsBuilder.fromUri(issuer) - .replacePath(OIDC_METADATA_PATH + issuer.getPath()) - .build(Collections.emptyMap()); + return UriComponentsBuilder.newInstance().uriComponents(uri) + .replacePath(uri.getPath() + OIDC_METADATA_PATH) + .build(); + } + + static Supplier oidcRfc8414(String issuer) { + UriComponents uri = oidcRfc8414Uri(issuer); // @formatter:on return getRfc8414Builder(issuer, uri); } - private static Supplier oauth(URI issuer) { + static UriComponents oidcRfc8414Uri(String issuer) { + UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build(); // @formatter:off - URI uri = UriComponentsBuilder.fromUri(issuer) - .replacePath(OAUTH_METADATA_PATH + issuer.getPath()) - .build(Collections.emptyMap()); - // @formatter:on + return UriComponentsBuilder.newInstance().uriComponents(uri) + .replacePath(OIDC_METADATA_PATH + uri.getPath()) + .build(); + } + + static Supplier oauth(String issuer) { + UriComponents uri = oauthUri(issuer); return getRfc8414Builder(issuer, uri); } - private static Supplier getRfc8414Builder(URI issuer, URI uri) { + static UriComponents oauthUri(String issuer) { + UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build(); + // @formatter:off + return UriComponentsBuilder.newInstance().uriComponents(uri) + .replacePath(OAUTH_METADATA_PATH + uri.getPath()) + .build(); + // @formatter:on + } + + private static Supplier getRfc8414Builder(String issuer, UriComponents uri) { return () -> { - RequestEntity request = RequestEntity.get(uri).build(); + RequestEntity request = RequestEntity.get(uri.toUriString()).build(); Map 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); 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..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; @@ -45,6 +43,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,12 +81,11 @@ private JwtDecoderProviderConfigurationUtils() { } static Map getConfigurationForOidcIssuerLocation(String oidcIssuerLocation) { - return getConfiguration(oidcIssuerLocation, rest, oidc(URI.create(oidcIssuerLocation))); + return getConfiguration(oidcIssuerLocation, rest, oidc(oidcIssuerLocation)); } static Map getConfigurationForIssuerLocation(String issuer, RestOperations rest) { - URI uri = URI.create(issuer); - return getConfiguration(issuer, rest, oidc(uri), oidcRfc8414(uri), oauth(uri)); + return getConfiguration(issuer, rest, oidc(issuer), oidcRfc8414(issuer), oauth(issuer)); } static Map getConfigurationForIssuerLocation(String issuer) { @@ -159,11 +157,11 @@ private static String getMetadataIssuer(Map configuration) { return "(unavailable)"; } - private static Map getConfiguration(String issuer, RestOperations rest, URI... uris) { + private static Map 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 request = RequestEntity.get(uri).build(); + RequestEntity request = RequestEntity.get(uri.toUriString()).build(); ResponseEntity> response = rest.exchange(request, STRING_OBJECT_MAP); Map configuration = response.getBody(); Assert.isTrue(configuration.get("jwks_uri") != null, "The public JWK set URI must not be null"); @@ -183,27 +181,30 @@ private static Map getConfiguration(String issuer, RestOperation throw new IllegalArgumentException(errorMessage); } - private static URI oidc(URI issuer) { + static UriComponents oidc(String issuer) { + UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build(); // @formatter:off - return UriComponentsBuilder.fromUri(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(URI issuer) { + static UriComponents oidcRfc8414(String issuer) { + UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build(); // @formatter:off - return UriComponentsBuilder.fromUri(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(URI issuer) { + static UriComponents oauth(String issuer) { + UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build(); // @formatter:off - return 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 } 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..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; @@ -41,6 +39,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,38 +92,40 @@ else if (jwk.getKeyType() == KeyType.EC) { } static Mono> getConfigurationForIssuerLocation(String issuer, WebClient web) { - URI uri = URI.create(issuer); - return getConfiguration(issuer, web, oidc(uri), oidcRfc8414(uri), oauth(uri)); + return getConfiguration(issuer, web, oidc(issuer), oidcRfc8414(issuer), oauth(issuer)); } - private static URI oidc(URI issuer) { + static UriComponents oidc(String issuer) { + UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build(); // @formatter:off - return UriComponentsBuilder.fromUri(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(URI issuer) { + static UriComponents oidcRfc8414(String issuer) { + UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build(); // @formatter:off - return UriComponentsBuilder.fromUri(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(URI issuer) { + static UriComponents oauth(String issuer) { + UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build(); // @formatter:off - return 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 } - private static Mono> getConfiguration(String issuer, WebClient web, URI... uris) { + private static Mono> 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);