Skip to content

Commit fab53cc

Browse files
committed
Merge pull request #50118 from vinhhieu21
* fix-jws-algorithm-null-add: Polish "Reject unknown JWS algorithms configured on NimbusJwtDecoder" Reject unknown JWS algorithms configured on NimbusJwtDecoder Closes gh-50118
2 parents eceb603 + 73a7b77 commit fab53cc

6 files changed

Lines changed: 73 additions & 22 deletions

File tree

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerJwkConfiguration.java

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.springframework.boot.autoconfigure.security.oauth2.resource.ConditionalOnPublicKeyJwtDecoder;
3535
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties;
3636
import org.springframework.boot.context.properties.PropertyMapper;
37+
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
3738
import org.springframework.context.annotation.Bean;
3839
import org.springframework.context.annotation.Conditional;
3940
import org.springframework.context.annotation.Configuration;
@@ -54,7 +55,9 @@
5455
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverter;
5556
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtGrantedAuthoritiesConverterAdapter;
5657
import org.springframework.security.web.server.SecurityWebFilterChain;
58+
import org.springframework.util.Assert;
5759
import org.springframework.util.CollectionUtils;
60+
import org.springframework.util.StringUtils;
5861

5962
/**
6063
* Configures a {@link ReactiveJwtDecoder} when a JWK Set URI, OpenID Connect Issuer URI
@@ -103,7 +106,12 @@ ReactiveJwtDecoder jwtDecoder(ObjectProvider<JwkSetUriReactiveJwtDecoderBuilderC
103106

104107
private void jwsAlgorithms(Set<SignatureAlgorithm> signatureAlgorithms) {
105108
for (String algorithm : this.properties.getJwsAlgorithms()) {
106-
signatureAlgorithms.add(SignatureAlgorithm.from(algorithm));
109+
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.from(algorithm);
110+
if (signatureAlgorithm == null) {
111+
throw new InvalidConfigurationPropertyValueException(
112+
"spring.security.oauth2.resourceserver.jwt.jws-algorithms", algorithm, "Unknown algorithm");
113+
}
114+
signatureAlgorithms.add(signatureAlgorithm);
107115
}
108116
}
109117

@@ -135,7 +143,7 @@ NimbusReactiveJwtDecoder jwtDecoderByPublicKeyValue() throws Exception {
135143
RSAPublicKey publicKey = (RSAPublicKey) KeyFactory.getInstance("RSA")
136144
.generatePublic(new X509EncodedKeySpec(getKeySpec(this.properties.readPublicKey())));
137145
NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withPublicKey(publicKey)
138-
.signatureAlgorithm(SignatureAlgorithm.from(exactlyOneAlgorithm()))
146+
.signatureAlgorithm(exactlyOneAlgorithm())
139147
.build();
140148
jwtDecoder.setJwtValidator(getValidators(JwtValidators.createDefault()));
141149
return jwtDecoder;
@@ -146,15 +154,18 @@ private byte[] getKeySpec(String keyValue) {
146154
return Base64.getMimeDecoder().decode(keyValue);
147155
}
148156

149-
private String exactlyOneAlgorithm() {
157+
private SignatureAlgorithm exactlyOneAlgorithm() {
150158
List<String> algorithms = this.properties.getJwsAlgorithms();
151-
int count = (algorithms != null) ? algorithms.size() : 0;
152-
if (count != 1) {
153-
throw new IllegalStateException(
154-
"Creating a JWT decoder using a public key requires exactly one JWS algorithm but " + count
155-
+ " were configured");
159+
Assert.state(algorithms != null && algorithms.size() == 1,
160+
() -> "Creating a JWT decoder using a public key requires exactly one JWS algorithm but "
161+
+ algorithms.size() + " were configured");
162+
SignatureAlgorithm algorithm = SignatureAlgorithm.from(algorithms.get(0));
163+
if (algorithm == null) {
164+
throw new InvalidConfigurationPropertyValueException(
165+
"spring.security.oauth2.resourceserver.jwt.jws-algorithms",
166+
StringUtils.collectionToCommaDelimitedString(algorithms), "Unknown algorithm");
156167
}
157-
return algorithms.get(0);
168+
return algorithm;
158169
}
159170

160171
@Bean

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwtConfiguration.java

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.springframework.boot.autoconfigure.security.oauth2.resource.ConditionalOnPublicKeyJwtDecoder;
3636
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties;
3737
import org.springframework.boot.context.properties.PropertyMapper;
38+
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
3839
import org.springframework.context.annotation.Bean;
3940
import org.springframework.context.annotation.Conditional;
4041
import org.springframework.context.annotation.Configuration;
@@ -53,7 +54,9 @@
5354
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
5455
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
5556
import org.springframework.security.web.SecurityFilterChain;
57+
import org.springframework.util.Assert;
5658
import org.springframework.util.CollectionUtils;
59+
import org.springframework.util.StringUtils;
5760

5861
import static org.springframework.security.config.Customizer.withDefaults;
5962

@@ -102,7 +105,12 @@ JwtDecoder jwtDecoderByJwkKeySetUri(ObjectProvider<JwkSetUriJwtDecoderBuilderCus
102105

103106
private void jwsAlgorithms(Set<SignatureAlgorithm> signatureAlgorithms) {
104107
for (String algorithm : this.properties.getJwsAlgorithms()) {
105-
signatureAlgorithms.add(SignatureAlgorithm.from(algorithm));
108+
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.from(algorithm);
109+
if (signatureAlgorithm == null) {
110+
throw new InvalidConfigurationPropertyValueException(
111+
"spring.security.oauth2.resourceserver.jwt.jws-algorithms", algorithm, "Unknown algorithm");
112+
}
113+
signatureAlgorithms.add(signatureAlgorithm);
106114
}
107115
}
108116

@@ -134,7 +142,7 @@ JwtDecoder jwtDecoderByPublicKeyValue() throws Exception {
134142
RSAPublicKey publicKey = (RSAPublicKey) KeyFactory.getInstance("RSA")
135143
.generatePublic(new X509EncodedKeySpec(getKeySpec(this.properties.readPublicKey())));
136144
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withPublicKey(publicKey)
137-
.signatureAlgorithm(SignatureAlgorithm.from(exactlyOneAlgorithm()))
145+
.signatureAlgorithm(exactlyOneAlgorithm())
138146
.build();
139147
jwtDecoder.setJwtValidator(getValidators(JwtValidators.createDefault()));
140148
return jwtDecoder;
@@ -145,15 +153,18 @@ private byte[] getKeySpec(String keyValue) {
145153
return Base64.getMimeDecoder().decode(keyValue);
146154
}
147155

148-
private String exactlyOneAlgorithm() {
156+
private SignatureAlgorithm exactlyOneAlgorithm() {
149157
List<String> algorithms = this.properties.getJwsAlgorithms();
150-
int count = (algorithms != null) ? algorithms.size() : 0;
151-
if (count != 1) {
152-
throw new IllegalStateException(
153-
"Creating a JWT decoder using a public key requires exactly one JWS algorithm but " + count
154-
+ " were configured");
158+
Assert.state(algorithms != null && algorithms.size() == 1,
159+
() -> "Creating a JWT decoder using a public key requires exactly one JWS algorithm but "
160+
+ algorithms.size() + " were configured");
161+
SignatureAlgorithm algorithm = SignatureAlgorithm.from(algorithms.get(0));
162+
if (algorithm == null) {
163+
throw new InvalidConfigurationPropertyValueException(
164+
"spring.security.oauth2.resourceserver.jwt.jws-algorithms",
165+
StringUtils.collectionToCommaDelimitedString(algorithms), "Unknown algorithm");
155166
}
156-
return algorithms.get(0);
167+
return algorithm;
157168
}
158169

159170
@Bean

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/JwtConverterCustomizationsArgumentsProvider.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,10 @@ public Stream<? extends Arguments> provideArguments(ExtensionContext extensionCo
7070
.claim(customPrincipalClaim, customPrincipalValue);
7171
Jwt noAuthoritiesCustomizationsJwt = jwtBuilder.claim("scp", jwtScopes[0] + " " + jwtScopes[1]).build();
7272
Jwt customAuthoritiesDelimiterJwt = jwtBuilder.claim("scp", jwtScopes[0] + "~" + jwtScopes[1]).build();
73-
Jwt customAuthoritiesClaimJwt = jwtBuilder.claim("scp", null)
73+
Jwt customAuthoritiesClaimJwt = jwtBuilder.claim("scp", "value")
7474
.claim(customAuthoritiesClaim, jwtScopes[0] + " " + jwtScopes[1])
7575
.build();
76-
Jwt customAuthoritiesClaimAndDelimiterJwt = jwtBuilder.claim("scp", null)
76+
Jwt customAuthoritiesClaimAndDelimiterJwt = jwtBuilder.claim("scp", "value")
7777
.claim(customAuthoritiesClaim, jwtScopes[0] + "~" + jwtScopes[1])
7878
.build();
7979
String[] customPrefixAuthorities = { customPrefix + jwtScopes[0], customPrefix + jwtScopes[1] };

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,19 @@ void autoConfigurationUsingJwkSetUriShouldConfigureResourceServerUsingMultipleJw
177177
});
178178
}
179179

180+
@Test
181+
void autoConfigurationUsingJwkSetUriShouldFailIfJwsAlgorithmIsUnknown() {
182+
this.contextRunner
183+
.withPropertyValues("spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://jwk-set-uri.com",
184+
"spring.security.oauth2.resourceserver.jwt.jws-algorithms=NOT_VALID")
185+
.run((context) -> {
186+
assertThat(context).hasFailed();
187+
assertThat(context.getStartupFailure())
188+
.hasRootCauseMessage("Property spring.security.oauth2.resourceserver.jwt.jws-algorithms with value "
189+
+ "'NOT_VALID' is invalid: Unknown algorithm");
190+
});
191+
}
192+
180193
@Test
181194
@WithPublicKeyResource
182195
void autoConfigurationUsingPublicKeyValueShouldConfigureResourceServerUsingSingleJwsAlgorithm() {

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerAutoConfigurationTests.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import org.springframework.boot.autoconfigure.AutoConfigurations;
5050
import org.springframework.boot.autoconfigure.security.oauth2.resource.JwtConverterCustomizationsArgumentsProvider;
5151
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
52+
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
5253
import org.springframework.boot.test.context.FilteredClassLoader;
5354
import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
5455
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
@@ -178,6 +179,19 @@ void autoConfigurationShouldConfigureResourceServerWithMultipleJwsAlgorithms() {
178179
});
179180
}
180181

182+
@Test
183+
void autoConfigurationUsingJwkSetUriShouldFailIfJwsAlgorithmIsUnknown() {
184+
this.contextRunner
185+
.withPropertyValues("spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://jwk-set-uri.com",
186+
"spring.security.oauth2.resourceserver.jwt.jws-algorithms=NOT_VALID")
187+
.run((context) -> {
188+
assertThat(context).hasFailed();
189+
assertThat(context.getStartupFailure())
190+
.hasRootCauseMessage("Property spring.security.oauth2.resourceserver.jwt.jws-algorithms with value "
191+
+ "'NOT_VALID' is invalid: Unknown algorithm");
192+
});
193+
}
194+
181195
@Test
182196
@WithPublicKeyResource
183197
void autoConfigurationUsingPublicKeyValueShouldConfigureResourceServerUsingSingleJwsAlgorithm() {
@@ -320,7 +334,8 @@ void autoConfigurationShouldFailIfAlgorithmIsInvalid() {
320334
"spring.security.oauth2.resourceserver.jwt.jws-algorithms=NOT_VALID")
321335
.run((context) -> assertThat(context).hasFailed()
322336
.getFailure()
323-
.hasMessageContaining("signatureAlgorithm cannot be null"));
337+
.hasMessageContaining("Unknown algorithm")
338+
.hasRootCauseExactlyInstanceOf(InvalidConfigurationPropertyValueException.class));
324339
}
325340

326341
@Test

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/server/servlet/OAuth2AuthorizationServerJwtAutoConfigurationTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
3232

3333
import static org.assertj.core.api.Assertions.assertThat;
34+
import static org.mockito.Mockito.mock;
3435

3536
/**
3637
* Tests for {@link OAuth2AuthorizationServerJwtAutoConfiguration}.
@@ -96,7 +97,7 @@ static class TestJwtDecoderConfiguration {
9697

9798
@Bean
9899
JwtDecoder jwtDecoder() {
99-
return (token) -> null;
100+
return mock(JwtDecoder.class);
100101
}
101102

102103
}

0 commit comments

Comments
 (0)