ClientRegistrations supports hostnames with underscores

Issue gh-15852
This commit is contained in:
Josh Cummings 2025-02-20 15:08:37 -07:00
parent 3d15be1b06
commit b6c813c5a2
2 changed files with 45 additions and 25 deletions

View File

@ -17,7 +17,6 @@
package org.springframework.security.oauth2.client.registration; package org.springframework.security.oauth2.client.registration;
import java.net.URI; import java.net.URI;
import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -146,7 +145,7 @@ public final class ClientRegistrations {
*/ */
public static ClientRegistration.Builder fromOidcIssuerLocation(String issuer) { public static ClientRegistration.Builder fromOidcIssuerLocation(String issuer) {
Assert.hasText(issuer, "issuer cannot be empty"); Assert.hasText(issuer, "issuer cannot be empty");
return getBuilder(issuer, oidc(URI.create(issuer))); return getBuilder(issuer, oidc(issuer));
} }
/** /**
@ -189,21 +188,17 @@ public final class ClientRegistrations {
*/ */
public static ClientRegistration.Builder fromIssuerLocation(String issuer) { public static ClientRegistration.Builder fromIssuerLocation(String issuer) {
Assert.hasText(issuer, "issuer cannot be empty"); Assert.hasText(issuer, "issuer cannot be empty");
URI uri = URI.create(issuer); return getBuilder(issuer, oidc(issuer), oidcRfc8414(issuer), oauth(issuer));
return getBuilder(issuer, oidc(uri), oidcRfc8414(uri), oauth(uri));
} }
private static Supplier<ClientRegistration.Builder> oidc(URI issuer) { static Supplier<ClientRegistration.Builder> oidc(String issuer) {
// @formatter:off UriComponents uri = oidcUri(issuer);
URI uri = UriComponentsBuilder.fromUri(issuer)
.replacePath(issuer.getPath() + OIDC_METADATA_PATH)
.build(Collections.emptyMap());
// @formatter:on // @formatter:on
return () -> { 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(); Map<String, Object> configuration = rest.exchange(request, typeReference).getBody();
OIDCProviderMetadata metadata = parse(configuration, OIDCProviderMetadata::parse); OIDCProviderMetadata metadata = parse(configuration, OIDCProviderMetadata::parse);
ClientRegistration.Builder builder = withProviderConfiguration(metadata, issuer.toASCIIString()) ClientRegistration.Builder builder = withProviderConfiguration(metadata, issuer)
.jwkSetUri(metadata.getJWKSetURI().toASCIIString()); .jwkSetUri(metadata.getJWKSetURI().toASCIIString());
if (metadata.getUserInfoEndpointURI() != null) { if (metadata.getUserInfoEndpointURI() != null) {
builder.userInfoUri(metadata.getUserInfoEndpointURI().toASCIIString()); builder.userInfoUri(metadata.getUserInfoEndpointURI().toASCIIString());
@ -212,35 +207,48 @@ public final class ClientRegistrations {
}; };
} }
private static Supplier<ClientRegistration.Builder> oidcRfc8414(String issuer) { static UriComponents oidcUri(String issuer) {
URI uri = oidcRfc8414Uri(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); return getRfc8414Builder(issuer, uri);
} }
static URI oidcRfc8414Uri(String issuer) { static UriComponents oidcRfc8414Uri(String issuer) {
UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build(); UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build();
// @formatter:off // @formatter:off
return UriComponentsBuilder.newInstance().uriComponents(uri) return UriComponentsBuilder.newInstance().uriComponents(uri)
.replacePath(OIDC_METADATA_PATH + uri.getPath()) .replacePath(OIDC_METADATA_PATH + uri.getPath())
.build(Collections.emptyMap()); .build();
// @formatter:on
} }
private static Supplier<ClientRegistration.Builder> oauth(URI issuer) { static Supplier<ClientRegistration.Builder> oauth(String issuer) {
// @formatter:off UriComponents uri = oauthUri(issuer);
URI uri = UriComponentsBuilder.fromUri(issuer)
.replacePath(OAUTH_METADATA_PATH + issuer.getPath())
.build(Collections.emptyMap());
// @formatter:on
return getRfc8414Builder(issuer, uri); return getRfc8414Builder(issuer, uri);
} }
private static Supplier<ClientRegistration.Builder> 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<ClientRegistration.Builder> getRfc8414Builder(String issuer, UriComponents uri) {
return () -> { 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(); Map<String, Object> configuration = rest.exchange(request, typeReference).getBody();
AuthorizationServerMetadata metadata = parse(configuration, AuthorizationServerMetadata::parse); AuthorizationServerMetadata metadata = parse(configuration, AuthorizationServerMetadata::parse);
ClientRegistration.Builder builder = withProviderConfiguration(metadata, issuer.toASCIIString()); ClientRegistration.Builder builder = withProviderConfiguration(metadata, issuer);
URI jwkSetUri = metadata.getJWKSetURI(); URI jwkSetUri = metadata.getJWKSetURI();
if (jwkSetUri != null) { if (jwkSetUri != null) {
builder.jwkSetUri(jwkSetUri.toASCIIString()); builder.jwkSetUri(jwkSetUri.toASCIIString());

View File

@ -34,6 +34,7 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod; 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.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
@ -569,6 +570,17 @@ public class ClientRegistrationsTests {
.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); .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 { private ClientRegistration.Builder registration(String path) throws Exception {
this.issuer = createIssuerFromServer(path); this.issuer = createIssuerFromServer(path);
this.response.put("issuer", this.issuer); this.response.put("issuer", this.issuer);