Skip to content

Commit 3d15be1

Browse files
committed
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
1 parent 0cd6a19 commit 3d15be1

File tree

5 files changed

+74
-43
lines changed

5 files changed

+74
-43
lines changed

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrations.java

+10-4
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.springframework.util.Assert;
3838
import org.springframework.web.client.HttpClientErrorException;
3939
import org.springframework.web.client.RestTemplate;
40+
import org.springframework.web.util.UriComponents;
4041
import org.springframework.web.util.UriComponentsBuilder;
4142

4243
/**
@@ -211,13 +212,18 @@ private static Supplier<ClientRegistration.Builder> oidc(URI issuer) {
211212
};
212213
}
213214

214-
private static Supplier<ClientRegistration.Builder> oidcRfc8414(URI issuer) {
215+
private static Supplier<ClientRegistration.Builder> oidcRfc8414(String issuer) {
216+
URI uri = oidcRfc8414Uri(issuer);
217+
return getRfc8414Builder(issuer, uri);
218+
}
219+
220+
static URI oidcRfc8414Uri(String issuer) {
221+
UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build();
215222
// @formatter:off
216-
URI uri = UriComponentsBuilder.fromUri(issuer)
217-
.replacePath(OIDC_METADATA_PATH + issuer.getPath())
223+
return UriComponentsBuilder.newInstance().uriComponents(uri)
224+
.replacePath(OIDC_METADATA_PATH + uri.getPath())
218225
.build(Collections.emptyMap());
219226
// @formatter:on
220-
return getRfc8414Builder(issuer, uri);
221227
}
222228

223229
private static Supplier<ClientRegistration.Builder> oauth(URI issuer) {

oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtils.java

+20-21
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
package org.springframework.security.oauth2.jwt;
1818

19-
import java.net.URI;
20-
import java.util.Collections;
2119
import java.util.HashSet;
2220
import java.util.List;
2321
import java.util.Map;
@@ -83,13 +81,11 @@ private JwtDecoderProviderConfigurationUtils() {
8381
}
8482

8583
static Map<String, Object> getConfigurationForOidcIssuerLocation(String oidcIssuerLocation) {
86-
UriComponents uri = UriComponentsBuilder.fromUriString(oidcIssuerLocation).build();
87-
return getConfiguration(oidcIssuerLocation, rest, oidc(uri));
84+
return getConfiguration(oidcIssuerLocation, rest, oidc(oidcIssuerLocation));
8885
}
8986

9087
static Map<String, Object> getConfigurationForIssuerLocation(String issuer, RestOperations rest) {
91-
UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build();
92-
return getConfiguration(issuer, rest, oidc(uri), oidcRfc8414(uri), oauth(uri));
88+
return getConfiguration(issuer, rest, oidc(issuer), oidcRfc8414(issuer), oauth(issuer));
9389
}
9490

9591
static Map<String, Object> getConfigurationForIssuerLocation(String issuer) {
@@ -161,11 +157,11 @@ private static String getMetadataIssuer(Map<String, Object> configuration) {
161157
return "(unavailable)";
162158
}
163159

164-
private static Map<String, Object> getConfiguration(String issuer, RestOperations rest, URI... uris) {
160+
private static Map<String, Object> getConfiguration(String issuer, RestOperations rest, UriComponents... uris) {
165161
String errorMessage = "Unable to resolve the Configuration with the provided Issuer of " + "\"" + issuer + "\"";
166-
for (URI uri : uris) {
162+
for (UriComponents uri : uris) {
167163
try {
168-
RequestEntity<Void> request = RequestEntity.get(uri).build();
164+
RequestEntity<Void> request = RequestEntity.get(uri.toUriString()).build();
169165
ResponseEntity<Map<String, Object>> response = rest.exchange(request, STRING_OBJECT_MAP);
170166
Map<String, Object> configuration = response.getBody();
171167
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
185181
throw new IllegalArgumentException(errorMessage);
186182
}
187183

188-
private static URI oidc(UriComponents issuer) {
184+
static UriComponents oidc(String issuer) {
185+
UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build();
189186
// @formatter:off
190-
return UriComponentsBuilder.newInstance().uriComponents(issuer)
191-
.replacePath(issuer.getPath() + OIDC_METADATA_PATH)
192-
.build(Collections.emptyMap());
187+
return UriComponentsBuilder.newInstance().uriComponents(uri)
188+
.replacePath(uri.getPath() + OIDC_METADATA_PATH)
189+
.build();
193190
// @formatter:on
194191
}
195192

196-
private static URI oidcRfc8414(UriComponents issuer) {
193+
static UriComponents oidcRfc8414(String issuer) {
194+
UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build();
197195
// @formatter:off
198-
return UriComponentsBuilder.newInstance().uriComponents(issuer)
199-
.replacePath(OIDC_METADATA_PATH + issuer.getPath())
200-
.build(Collections.emptyMap());
196+
return UriComponentsBuilder.newInstance().uriComponents(uri)
197+
.replacePath(OIDC_METADATA_PATH + uri.getPath())
198+
.build();
201199
// @formatter:on
202200
}
203201

204-
private static URI oauth(UriComponents issuer) {
202+
static UriComponents oauth(String issuer) {
203+
UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build();
205204
// @formatter:off
206-
return UriComponentsBuilder.newInstance().uriComponents(issuer)
207-
.replacePath(OAUTH_METADATA_PATH + issuer.getPath())
208-
.build(Collections.emptyMap());
205+
return UriComponentsBuilder.newInstance().uriComponents(uri)
206+
.replacePath(OAUTH_METADATA_PATH + uri.getPath())
207+
.build();
209208
// @formatter:on
210209
}
211210

oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoderProviderConfigurationUtils.java

+18-18
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
package org.springframework.security.oauth2.jwt;
1818

19-
import java.net.URI;
20-
import java.util.Collections;
2119
import java.util.HashSet;
2220
import java.util.Map;
2321
import java.util.Set;
@@ -94,38 +92,40 @@ else if (jwk.getKeyType() == KeyType.EC) {
9492
}
9593

9694
static Mono<Map<String, Object>> getConfigurationForIssuerLocation(String issuer, WebClient web) {
97-
UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build();
98-
return getConfiguration(issuer, web, oidc(uri), oidcRfc8414(uri), oauth(uri));
95+
return getConfiguration(issuer, web, oidc(issuer), oidcRfc8414(issuer), oauth(issuer));
9996
}
10097

101-
private static URI oidc(UriComponents issuer) {
98+
static UriComponents oidc(String issuer) {
99+
UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build();
102100
// @formatter:off
103-
return UriComponentsBuilder.newInstance().uriComponents(issuer)
104-
.replacePath(issuer.getPath() + OIDC_METADATA_PATH)
105-
.build(Collections.emptyMap());
101+
return UriComponentsBuilder.newInstance().uriComponents(uri)
102+
.replacePath(uri.getPath() + OIDC_METADATA_PATH)
103+
.build();
106104
// @formatter:on
107105
}
108106

109-
private static URI oidcRfc8414(UriComponents issuer) {
107+
static UriComponents oidcRfc8414(String issuer) {
108+
UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build();
110109
// @formatter:off
111-
return UriComponentsBuilder.newInstance().uriComponents(issuer)
112-
.replacePath(OIDC_METADATA_PATH + issuer.getPath())
113-
.build(Collections.emptyMap());
110+
return UriComponentsBuilder.newInstance().uriComponents(uri)
111+
.replacePath(OIDC_METADATA_PATH + uri.getPath())
112+
.build();
114113
// @formatter:on
115114
}
116115

117-
private static URI oauth(UriComponents issuer) {
116+
static UriComponents oauth(String issuer) {
117+
UriComponents uri = UriComponentsBuilder.fromUriString(issuer).build();
118118
// @formatter:off
119-
return UriComponentsBuilder.newInstance().uriComponents(issuer)
120-
.replacePath(OAUTH_METADATA_PATH + issuer.getPath())
121-
.build(Collections.emptyMap());
119+
return UriComponentsBuilder.newInstance().uriComponents(uri)
120+
.replacePath(OAUTH_METADATA_PATH + uri.getPath())
121+
.build();
122122
// @formatter:on
123123
}
124124

125-
private static Mono<Map<String, Object>> getConfiguration(String issuer, WebClient web, URI... uris) {
125+
private static Mono<Map<String, Object>> getConfiguration(String issuer, WebClient web, UriComponents... uris) {
126126
String errorMessage = "Unable to resolve the Configuration with the provided Issuer of " + "\"" + issuer + "\"";
127127
return Flux.just(uris)
128-
.concatMap((uri) -> web.get().uri(uri).retrieve().bodyToMono(STRING_OBJECT_MAP))
128+
.concatMap((uri) -> web.get().uri(uri.toUriString()).retrieve().bodyToMono(STRING_OBJECT_MAP))
129129
.flatMap((configuration) -> {
130130
if (configuration.get("jwks_uri") == null) {
131131
return Mono.error(() -> new IllegalArgumentException("The public JWK set URI must not be null"));

oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtilsTests.java

+13
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.springframework.security.oauth2.jose.TestKeys;
3636
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
3737
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
38+
import org.springframework.web.util.UriComponents;
3839

3940
import static org.assertj.core.api.Assertions.assertThat;
4041
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
@@ -90,4 +91,16 @@ public void getSignatureAlgorithmsWhenAlgorithmThenParses() throws Exception {
9091
assertThat(algorithms).containsOnly(SignatureAlgorithm.RS256);
9192
}
9293

94+
// gh-15852
95+
@Test
96+
public void oidcWhenHostContainsUnderscoreThenRetains() {
97+
UriComponents oidc = JwtDecoderProviderConfigurationUtils.oidc("https://elated_sutherland:8080/path");
98+
assertThat(oidc.getHost()).isEqualTo("elated_sutherland");
99+
UriComponents oauth = JwtDecoderProviderConfigurationUtils.oauth("https://elated_sutherland:8080/path");
100+
assertThat(oauth.getHost()).isEqualTo("elated_sutherland");
101+
UriComponents oidcRfc8414 = JwtDecoderProviderConfigurationUtils
102+
.oidcRfc8414("https://elated_sutherland:8080/path");
103+
assertThat(oidcRfc8414.getHost()).isEqualTo("elated_sutherland");
104+
}
105+
93106
}

oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoderProviderConfigurationUtilsTests.java

+13
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.springframework.http.HttpHeaders;
3838
import org.springframework.http.MediaType;
3939
import org.springframework.web.reactive.function.client.WebClient;
40+
import org.springframework.web.util.UriComponents;
4041
import org.springframework.web.util.UriComponentsBuilder;
4142

4243
import static org.assertj.core.api.Assertions.assertThat;
@@ -227,6 +228,18 @@ public void issuerWhenOidcFallbackRequestedIssuerIsUnresponsiveThenThrowsIllegal
227228
// @formatter:on
228229
}
229230

231+
// gh-15852
232+
@Test
233+
public void oidcWhenHostContainsUnderscoreThenRetains() {
234+
UriComponents oidc = ReactiveJwtDecoderProviderConfigurationUtils.oidc("https://elated_sutherland:8080/path");
235+
assertThat(oidc.getHost()).isEqualTo("elated_sutherland");
236+
UriComponents oauth = ReactiveJwtDecoderProviderConfigurationUtils.oauth("https://elated_sutherland:8080/path");
237+
assertThat(oauth.getHost()).isEqualTo("elated_sutherland");
238+
UriComponents oidcRfc8414 = ReactiveJwtDecoderProviderConfigurationUtils
239+
.oidcRfc8414("https://elated_sutherland:8080/path");
240+
assertThat(oidcRfc8414.getHost()).isEqualTo("elated_sutherland");
241+
}
242+
230243
private void prepareConfigurationResponse() {
231244
String body = String.format(DEFAULT_RESPONSE_TEMPLATE, this.issuer, this.issuer);
232245
prepareConfigurationResponse(body);

0 commit comments

Comments
 (0)