Skip to content

Commit de17878

Browse files
committed
Merge pull request #29084 from ahmedmq
* pr/29084: Polish "Add support for aud claim in resource server" Add support for aud claim in resource server Closes gh-29084
2 parents 9025d1d + 7a659e4 commit de17878

File tree

5 files changed

+402
-11
lines changed

5 files changed

+402
-11
lines changed

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import java.io.IOException;
2020
import java.io.InputStream;
2121
import java.nio.charset.StandardCharsets;
22+
import java.util.ArrayList;
23+
import java.util.List;
2224

2325
import org.springframework.boot.context.properties.ConfigurationProperties;
2426
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
@@ -31,6 +33,7 @@
3133
*
3234
* @author Madhura Bhave
3335
* @author Artsiom Yudovin
36+
* @author Mushtaq Ahmed
3437
* @since 2.1.0
3538
*/
3639
@ConfigurationProperties(prefix = "spring.security.oauth2.resourceserver")
@@ -71,6 +74,11 @@ public static class Jwt {
7174
*/
7275
private Resource publicKeyLocation;
7376

77+
/**
78+
* Identifies the recipients that the JWT is intended for.
79+
*/
80+
private List<String> audiences = new ArrayList<>();
81+
7482
public String getJwkSetUri() {
7583
return this.jwkSetUri;
7684
}
@@ -103,6 +111,14 @@ public void setPublicKeyLocation(Resource publicKeyLocation) {
103111
this.publicKeyLocation = publicKeyLocation;
104112
}
105113

114+
public List<String> getAudiences() {
115+
return this.audiences;
116+
}
117+
118+
public void setAudiences(List<String> audiences) {
119+
this.audiences = audiences;
120+
}
121+
106122
public String readPublicKey() throws IOException {
107123
String key = "spring.security.oauth2.resourceserver.public-key-location";
108124
Assert.notNull(this.publicKeyLocation, "PublicKeyLocation must not be null");

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

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@
1919
import java.security.KeyFactory;
2020
import java.security.interfaces.RSAPublicKey;
2121
import java.security.spec.X509EncodedKeySpec;
22+
import java.util.ArrayList;
2223
import java.util.Base64;
24+
import java.util.Collections;
25+
import java.util.List;
26+
import java.util.function.Supplier;
2327

2428
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
2529
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -32,13 +36,19 @@
3236
import org.springframework.context.annotation.Configuration;
3337
import org.springframework.security.config.web.server.ServerHttpSecurity;
3438
import org.springframework.security.config.web.server.ServerHttpSecurity.OAuth2ResourceServerSpec;
39+
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
40+
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
3541
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
42+
import org.springframework.security.oauth2.jwt.Jwt;
43+
import org.springframework.security.oauth2.jwt.JwtClaimNames;
44+
import org.springframework.security.oauth2.jwt.JwtClaimValidator;
3645
import org.springframework.security.oauth2.jwt.JwtValidators;
3746
import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;
3847
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
3948
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoders;
4049
import org.springframework.security.oauth2.jwt.SupplierReactiveJwtDecoder;
4150
import org.springframework.security.web.server.SecurityWebFilterChain;
51+
import org.springframework.util.CollectionUtils;
4252

4353
/**
4454
* Configures a {@link ReactiveJwtDecoder} when a JWK Set URI, OpenID Connect Issuer URI
@@ -49,6 +59,7 @@
4959
* @author Artsiom Yudovin
5060
* @author HaiTao Zhang
5161
* @author Anastasiia Losieva
62+
* @author Mushtaq Ahmed
5263
*/
5364
@Configuration(proxyBeanMethods = false)
5465
class ReactiveOAuth2ResourceServerJwkConfiguration {
@@ -70,19 +81,34 @@ ReactiveJwtDecoder jwtDecoder() {
7081
.withJwkSetUri(this.properties.getJwkSetUri())
7182
.jwsAlgorithm(SignatureAlgorithm.from(this.properties.getJwsAlgorithm())).build();
7283
String issuerUri = this.properties.getIssuerUri();
73-
if (issuerUri != null) {
74-
nimbusReactiveJwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(issuerUri));
75-
}
84+
Supplier<OAuth2TokenValidator<Jwt>> defaultValidator = (issuerUri != null)
85+
? () -> JwtValidators.createDefaultWithIssuer(issuerUri) : JwtValidators::createDefault;
86+
nimbusReactiveJwtDecoder.setJwtValidator(getValidators(defaultValidator));
7687
return nimbusReactiveJwtDecoder;
7788
}
7889

90+
private OAuth2TokenValidator<Jwt> getValidators(Supplier<OAuth2TokenValidator<Jwt>> defaultValidator) {
91+
OAuth2TokenValidator<Jwt> defaultValidators = defaultValidator.get();
92+
List<String> audiences = this.properties.getAudiences();
93+
if (CollectionUtils.isEmpty(audiences)) {
94+
return defaultValidators;
95+
}
96+
List<OAuth2TokenValidator<Jwt>> validators = new ArrayList<>();
97+
validators.add(defaultValidators);
98+
validators.add(new JwtClaimValidator<List<String>>(JwtClaimNames.AUD,
99+
(aud) -> aud != null && !Collections.disjoint(aud, audiences)));
100+
return new DelegatingOAuth2TokenValidator<>(validators);
101+
}
102+
79103
@Bean
80104
@Conditional(KeyValueCondition.class)
81105
NimbusReactiveJwtDecoder jwtDecoderByPublicKeyValue() throws Exception {
82106
RSAPublicKey publicKey = (RSAPublicKey) KeyFactory.getInstance("RSA")
83107
.generatePublic(new X509EncodedKeySpec(getKeySpec(this.properties.readPublicKey())));
84-
return NimbusReactiveJwtDecoder.withPublicKey(publicKey)
108+
NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withPublicKey(publicKey)
85109
.signatureAlgorithm(SignatureAlgorithm.from(this.properties.getJwsAlgorithm())).build();
110+
jwtDecoder.setJwtValidator(getValidators(JwtValidators::createDefault));
111+
return jwtDecoder;
86112
}
87113

88114
private byte[] getKeySpec(String keyValue) {
@@ -93,8 +119,13 @@ private byte[] getKeySpec(String keyValue) {
93119
@Bean
94120
@Conditional(IssuerUriCondition.class)
95121
SupplierReactiveJwtDecoder jwtDecoderByIssuerUri() {
96-
return new SupplierReactiveJwtDecoder(
97-
() -> ReactiveJwtDecoders.fromIssuerLocation(this.properties.getIssuerUri()));
122+
return new SupplierReactiveJwtDecoder(() -> {
123+
NimbusReactiveJwtDecoder jwtDecoder = (NimbusReactiveJwtDecoder) ReactiveJwtDecoders
124+
.fromIssuerLocation(this.properties.getIssuerUri());
125+
jwtDecoder.setJwtValidator(
126+
getValidators(() -> JwtValidators.createDefaultWithIssuer(this.properties.getIssuerUri())));
127+
return jwtDecoder;
128+
});
98129
}
99130

100131
}

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

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@
1919
import java.security.KeyFactory;
2020
import java.security.interfaces.RSAPublicKey;
2121
import java.security.spec.X509EncodedKeySpec;
22+
import java.util.ArrayList;
2223
import java.util.Base64;
24+
import java.util.Collections;
25+
import java.util.List;
26+
import java.util.function.Supplier;
2327

2428
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
2529
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -33,13 +37,19 @@
3337
import org.springframework.context.annotation.Configuration;
3438
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
3539
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
40+
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
41+
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
3642
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
43+
import org.springframework.security.oauth2.jwt.Jwt;
44+
import org.springframework.security.oauth2.jwt.JwtClaimNames;
45+
import org.springframework.security.oauth2.jwt.JwtClaimValidator;
3746
import org.springframework.security.oauth2.jwt.JwtDecoder;
3847
import org.springframework.security.oauth2.jwt.JwtDecoders;
3948
import org.springframework.security.oauth2.jwt.JwtValidators;
4049
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
4150
import org.springframework.security.oauth2.jwt.SupplierJwtDecoder;
4251
import org.springframework.security.web.SecurityFilterChain;
52+
import org.springframework.util.CollectionUtils;
4353

4454
/**
4555
* Configures a {@link JwtDecoder} when a JWK Set URI, OpenID Connect Issuer URI or Public
@@ -49,6 +59,7 @@
4959
* @author Madhura Bhave
5060
* @author Artsiom Yudovin
5161
* @author HaiTao Zhang
62+
* @author Mushtaq Ahmed
5263
*/
5364
@Configuration(proxyBeanMethods = false)
5465
class OAuth2ResourceServerJwtConfiguration {
@@ -69,19 +80,34 @@ JwtDecoder jwtDecoderByJwkKeySetUri() {
6980
NimbusJwtDecoder nimbusJwtDecoder = NimbusJwtDecoder.withJwkSetUri(this.properties.getJwkSetUri())
7081
.jwsAlgorithm(SignatureAlgorithm.from(this.properties.getJwsAlgorithm())).build();
7182
String issuerUri = this.properties.getIssuerUri();
72-
if (issuerUri != null) {
73-
nimbusJwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(issuerUri));
74-
}
83+
Supplier<OAuth2TokenValidator<Jwt>> defaultValidator = (issuerUri != null)
84+
? () -> JwtValidators.createDefaultWithIssuer(issuerUri) : JwtValidators::createDefault;
85+
nimbusJwtDecoder.setJwtValidator(getValidators(defaultValidator));
7586
return nimbusJwtDecoder;
7687
}
7788

89+
private OAuth2TokenValidator<Jwt> getValidators(Supplier<OAuth2TokenValidator<Jwt>> defaultValidator) {
90+
OAuth2TokenValidator<Jwt> defaultValidators = defaultValidator.get();
91+
List<String> audiences = this.properties.getAudiences();
92+
if (CollectionUtils.isEmpty(audiences)) {
93+
return defaultValidators;
94+
}
95+
List<OAuth2TokenValidator<Jwt>> validators = new ArrayList<>();
96+
validators.add(defaultValidators);
97+
validators.add(new JwtClaimValidator<List<String>>(JwtClaimNames.AUD,
98+
(aud) -> aud != null && !Collections.disjoint(aud, audiences)));
99+
return new DelegatingOAuth2TokenValidator<>(validators);
100+
}
101+
78102
@Bean
79103
@Conditional(KeyValueCondition.class)
80104
JwtDecoder jwtDecoderByPublicKeyValue() throws Exception {
81105
RSAPublicKey publicKey = (RSAPublicKey) KeyFactory.getInstance("RSA")
82106
.generatePublic(new X509EncodedKeySpec(getKeySpec(this.properties.readPublicKey())));
83-
return NimbusJwtDecoder.withPublicKey(publicKey)
107+
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withPublicKey(publicKey)
84108
.signatureAlgorithm(SignatureAlgorithm.from(this.properties.getJwsAlgorithm())).build();
109+
jwtDecoder.setJwtValidator(getValidators(JwtValidators::createDefault));
110+
return jwtDecoder;
85111
}
86112

87113
private byte[] getKeySpec(String keyValue) {
@@ -92,7 +118,12 @@ private byte[] getKeySpec(String keyValue) {
92118
@Bean
93119
@Conditional(IssuerUriCondition.class)
94120
SupplierJwtDecoder jwtDecoderByIssuerUri() {
95-
return new SupplierJwtDecoder(() -> JwtDecoders.fromIssuerLocation(this.properties.getIssuerUri()));
121+
return new SupplierJwtDecoder(() -> {
122+
String issuerUri = this.properties.getIssuerUri();
123+
NimbusJwtDecoder jwtDecoder = JwtDecoders.fromIssuerLocation(issuerUri);
124+
jwtDecoder.setJwtValidator(getValidators(() -> JwtValidators.createDefaultWithIssuer(issuerUri)));
125+
return jwtDecoder;
126+
});
96127
}
97128

98129
}

0 commit comments

Comments
 (0)