mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-05-30 16:52:13 +00:00
Change Type Validation Default
NimbusJwtDecoder and NimbusReactiveJwtDecoder now use Spring Security's JwtTypeValidator by default instead of Nimbus's type validator. Closes gh-17181
This commit is contained in:
parent
37a814bc29
commit
6d3b54df21
@ -18,11 +18,6 @@ package org.springframework.security.config.annotation.web.configurers.oauth2.cl
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.nimbusds.jose.JOSEObjectType;
|
||||
import com.nimbusds.jose.proc.DefaultJOSEObjectTypeVerifier;
|
||||
import com.nimbusds.jose.proc.JOSEObjectTypeVerifier;
|
||||
import com.nimbusds.jose.proc.SecurityContext;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
@ -38,6 +33,7 @@ import org.springframework.security.oauth2.jwt.BadJwtException;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
|
||||
import org.springframework.security.oauth2.jwt.JwtTypeValidator;
|
||||
import org.springframework.security.oauth2.jwt.JwtValidators;
|
||||
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
||||
import org.springframework.util.Assert;
|
||||
@ -67,8 +63,10 @@ final class OidcBackChannelLogoutAuthenticationProvider implements Authenticatio
|
||||
* Construct an {@link OidcBackChannelLogoutAuthenticationProvider}
|
||||
*/
|
||||
OidcBackChannelLogoutAuthenticationProvider() {
|
||||
JwtTypeValidator type = new JwtTypeValidator("JWT", "logout+jwt");
|
||||
type.setAllowEmpty(true);
|
||||
Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidator = (clientRegistration) -> JwtValidators
|
||||
.createDefaultWithValidators(new OidcBackChannelLogoutTokenValidator(clientRegistration));
|
||||
.createDefaultWithValidators(type, new OidcBackChannelLogoutTokenValidator(clientRegistration));
|
||||
this.logoutTokenDecoderFactory = (clientRegistration) -> {
|
||||
String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();
|
||||
if (!StringUtils.hasText(jwkSetUri)) {
|
||||
@ -79,11 +77,7 @@ final class OidcBackChannelLogoutAuthenticationProvider implements Authenticatio
|
||||
null);
|
||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
||||
}
|
||||
JOSEObjectTypeVerifier<SecurityContext> typeVerifier = new DefaultJOSEObjectTypeVerifier<>(null,
|
||||
JOSEObjectType.JWT, new JOSEObjectType("logout+jwt"));
|
||||
NimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri)
|
||||
.jwtProcessorCustomizer((processor) -> processor.setJWSTypeVerifier(typeVerifier))
|
||||
.build();
|
||||
NimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build();
|
||||
decoder.setJwtValidator(jwtValidator.apply(clientRegistration));
|
||||
decoder.setClaimSetConverter(OidcIdTokenDecoderFactory.createDefaultClaimTypeConverter());
|
||||
return decoder;
|
||||
|
@ -18,10 +18,6 @@ package org.springframework.security.config.web.server;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.nimbusds.jose.JOSEObjectType;
|
||||
import com.nimbusds.jose.proc.DefaultJOSEObjectTypeVerifier;
|
||||
import com.nimbusds.jose.proc.JOSEObjectTypeVerifier;
|
||||
import com.nimbusds.jose.proc.JWKSecurityContext;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
@ -41,6 +37,7 @@ import org.springframework.security.oauth2.jwt.BadJwtException;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
|
||||
import org.springframework.security.oauth2.jwt.JwtTypeValidator;
|
||||
import org.springframework.security.oauth2.jwt.JwtValidators;
|
||||
import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;
|
||||
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
|
||||
@ -72,8 +69,10 @@ final class OidcBackChannelLogoutReactiveAuthenticationManager implements Reacti
|
||||
* Construct an {@link OidcBackChannelLogoutReactiveAuthenticationManager}
|
||||
*/
|
||||
OidcBackChannelLogoutReactiveAuthenticationManager() {
|
||||
JwtTypeValidator type = new JwtTypeValidator("JWT", "logout+jwt");
|
||||
type.setAllowEmpty(true);
|
||||
Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidator = (clientRegistration) -> JwtValidators
|
||||
.createDefaultWithValidators(new OidcBackChannelLogoutTokenValidator(clientRegistration));
|
||||
.createDefaultWithValidators(type, new OidcBackChannelLogoutTokenValidator(clientRegistration));
|
||||
this.logoutTokenDecoderFactory = (clientRegistration) -> {
|
||||
String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();
|
||||
if (!StringUtils.hasText(jwkSetUri)) {
|
||||
@ -84,11 +83,7 @@ final class OidcBackChannelLogoutReactiveAuthenticationManager implements Reacti
|
||||
null);
|
||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
||||
}
|
||||
JOSEObjectTypeVerifier<JWKSecurityContext> typeVerifier = new DefaultJOSEObjectTypeVerifier<>(null,
|
||||
JOSEObjectType.JWT, new JOSEObjectType("logout+jwt"));
|
||||
NimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri)
|
||||
.jwtProcessorCustomizer((processor) -> processor.setJWSTypeVerifier(typeVerifier))
|
||||
.build();
|
||||
NimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri).build();
|
||||
decoder.setJwtValidator(jwtValidator.apply(clientRegistration));
|
||||
decoder.setClaimSetConverter(
|
||||
new ClaimTypeConverter(OidcIdTokenDecoderFactory.createDefaultClaimTypeConverters()));
|
||||
|
@ -1,3 +1,82 @@
|
||||
= Reactive
|
||||
|
||||
If you have already performed the xref:migration/index.adoc[initial migration steps] for your Reactive application, you're now ready to perform steps specific to Reactive applications.
|
||||
|
||||
== Validate `typ` Header with `JwtTypeValidator`
|
||||
|
||||
If when following the 6.5 preparatory steps you set `validateTypes` to `false`, you can now remove it.
|
||||
You can also remove explicitly adding `JwtTypeValidator` to the list of defaults.
|
||||
|
||||
For example, change this:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
JwtDecoder jwtDecoder() {
|
||||
NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withIssuerLocation(location)
|
||||
.validateTypes(false) <1>
|
||||
// ... your remaining configuration
|
||||
.build();
|
||||
jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators(
|
||||
new JwtIssuerValidator(location), JwtTypeValidator.jwt())); <2>
|
||||
return jwtDecoder;
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Bean
|
||||
fun jwtDecoder(): JwtDecoder {
|
||||
val jwtDecoder = NimbusReactiveJwtDecoder.withIssuerLocation(location)
|
||||
.validateTypes(false) <1>
|
||||
// ... your remaining configuration
|
||||
.build()
|
||||
jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators(
|
||||
JwtIssuerValidator(location), JwtTypeValidator.jwt())) <2>
|
||||
return jwtDecoder
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> - Switch off Nimbus verifying the `typ`
|
||||
<2> - Add the default `typ` validator
|
||||
|
||||
to this:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
NimbusReactiveJwtDecoder jwtDecoder() {
|
||||
NimbusJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withIssuerLocation(location)
|
||||
// ... your remaining configuration <1>
|
||||
.build();
|
||||
jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(location)); <2>
|
||||
return jwtDecoder;
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Bean
|
||||
fun jwtDecoder(): NimbusReactiveJwtDecoder {
|
||||
val jwtDecoder = NimbusReactiveJwtDecoder.withIssuerLocation(location)
|
||||
// ... your remaining configuration
|
||||
.build()
|
||||
jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(location)) <2>
|
||||
return jwtDecoder
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> - `validateTypes` now defaults to `false`
|
||||
<2> - `JwtTypeValidator#jwt` is added by all `createDefaultXXX` methods
|
||||
|
@ -75,8 +75,8 @@ public final class JwtValidators {
|
||||
* supplied
|
||||
*/
|
||||
public static OAuth2TokenValidator<Jwt> createDefault() {
|
||||
return new DelegatingOAuth2TokenValidator<>(
|
||||
Arrays.asList(new JwtTimestampValidator(), new X509CertificateThumbprintValidator(
|
||||
return new DelegatingOAuth2TokenValidator<>(Arrays.asList(JwtTypeValidator.jwt(), new JwtTimestampValidator(),
|
||||
new X509CertificateThumbprintValidator(
|
||||
X509CertificateThumbprintValidator.DEFAULT_X509_CERTIFICATE_SUPPLIER)));
|
||||
}
|
||||
|
||||
@ -104,6 +104,10 @@ public final class JwtValidators {
|
||||
if (jwtTimestampValidator == null) {
|
||||
tokenValidators.add(0, new JwtTimestampValidator());
|
||||
}
|
||||
JwtTypeValidator typeValidator = CollectionUtils.findValueOfType(tokenValidators, JwtTypeValidator.class);
|
||||
if (typeValidator == null) {
|
||||
tokenValidators.add(0, JwtTypeValidator.jwt());
|
||||
}
|
||||
return new DelegatingOAuth2TokenValidator<>(tokenValidators);
|
||||
}
|
||||
|
||||
|
@ -279,7 +279,7 @@ public final class NimbusJwtDecoder implements JwtDecoder {
|
||||
private Function<JWKSource<SecurityContext>, Set<JWSAlgorithm>> defaultAlgorithms = (source) -> Set
|
||||
.of(JWSAlgorithm.RS256);
|
||||
|
||||
private JOSEObjectTypeVerifier<SecurityContext> typeVerifier = JWT_TYPE_VERIFIER;
|
||||
private JOSEObjectTypeVerifier<SecurityContext> typeVerifier = NO_TYPE_VERIFIER;
|
||||
|
||||
private final Set<SignatureAlgorithm> signatureAlgorithms = new HashSet<>();
|
||||
|
||||
@ -548,7 +548,7 @@ public final class NimbusJwtDecoder implements JwtDecoder {
|
||||
|
||||
private JWSAlgorithm jwsAlgorithm;
|
||||
|
||||
private JOSEObjectTypeVerifier<SecurityContext> typeVerifier = JWT_TYPE_VERIFIER;
|
||||
private JOSEObjectTypeVerifier<SecurityContext> typeVerifier = NO_TYPE_VERIFIER;
|
||||
|
||||
private final RSAPublicKey key;
|
||||
|
||||
@ -680,7 +680,7 @@ public final class NimbusJwtDecoder implements JwtDecoder {
|
||||
|
||||
private JWSAlgorithm jwsAlgorithm = JWSAlgorithm.HS256;
|
||||
|
||||
private JOSEObjectTypeVerifier<SecurityContext> typeVerifier = JWT_TYPE_VERIFIER;
|
||||
private JOSEObjectTypeVerifier<SecurityContext> typeVerifier = NO_TYPE_VERIFIER;
|
||||
|
||||
private Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer;
|
||||
|
||||
|
@ -324,7 +324,7 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
|
||||
private Function<ReactiveRemoteJWKSource, Mono<Set<JWSAlgorithm>>> defaultAlgorithms = (source) -> Mono
|
||||
.just(Set.of(JWSAlgorithm.RS256));
|
||||
|
||||
private JOSEObjectTypeVerifier<JWKSecurityContext> typeVerifier = JWT_TYPE_VERIFIER;
|
||||
private JOSEObjectTypeVerifier<JWKSecurityContext> typeVerifier = NO_TYPE_VERIFIER;
|
||||
|
||||
private Set<SignatureAlgorithm> signatureAlgorithms = new HashSet<>();
|
||||
|
||||
@ -547,7 +547,7 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
|
||||
|
||||
private JWSAlgorithm jwsAlgorithm;
|
||||
|
||||
private JOSEObjectTypeVerifier<SecurityContext> typeVerifier = JWT_TYPE_VERIFIER;
|
||||
private JOSEObjectTypeVerifier<SecurityContext> typeVerifier = NO_TYPE_VERIFIER;
|
||||
|
||||
private Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer;
|
||||
|
||||
@ -682,7 +682,7 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
|
||||
|
||||
private JWSAlgorithm jwsAlgorithm = JWSAlgorithm.HS256;
|
||||
|
||||
private JOSEObjectTypeVerifier<SecurityContext> typeVerifier = JWT_TYPE_VERIFIER;
|
||||
private JOSEObjectTypeVerifier<SecurityContext> typeVerifier = NO_TYPE_VERIFIER;
|
||||
|
||||
private Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer;
|
||||
|
||||
@ -814,7 +814,7 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
|
||||
|
||||
private JWSAlgorithm jwsAlgorithm = JWSAlgorithm.RS256;
|
||||
|
||||
private JOSEObjectTypeVerifier<JWKSecurityContext> typeVerifier = JWT_TYPE_VERIFIER;
|
||||
private JOSEObjectTypeVerifier<JWKSecurityContext> typeVerifier = NO_TYPE_VERIFIER;
|
||||
|
||||
private Consumer<ConfigurableJWTProcessor<JWKSecurityContext>> jwtProcessorCustomizer;
|
||||
|
||||
|
@ -62,7 +62,8 @@ public class JwtValidatorsTests {
|
||||
|
||||
assertThat(containsByType(validator, JwtTimestampValidator.class)).isTrue();
|
||||
assertThat(containsByType(validator, X509CertificateThumbprintValidator.class)).isTrue();
|
||||
assertThat(Objects.requireNonNull(tokenValidators).size()).isEqualTo(2);
|
||||
assertThat(containsByType(validator, JwtTypeValidator.class)).isTrue();
|
||||
assertThat(Objects.requireNonNull(tokenValidators).size()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -458,10 +458,8 @@ public class NimbusJwtDecoderTests {
|
||||
// @formatter:off
|
||||
NimbusJwtDecoder decoder = NimbusJwtDecoder.withPublicKey(publicKey)
|
||||
.signatureAlgorithm(SignatureAlgorithm.RS256)
|
||||
.jwtProcessorCustomizer((p) -> p
|
||||
.setJWSTypeVerifier(new DefaultJOSEObjectTypeVerifier<>(new JOSEObjectType("JWS")))
|
||||
)
|
||||
.build();
|
||||
decoder.setJwtValidator(JwtValidators.createDefaultWithValidators(new JwtTypeValidator("JWS")));
|
||||
// @formatter:on
|
||||
assertThat(decoder.decode(signedJwt.serialize()).hasClaim(JwtClaimNames.EXP)).isNotNull();
|
||||
}
|
||||
@ -575,10 +573,8 @@ public class NimbusJwtDecoderTests {
|
||||
// @formatter:off
|
||||
NimbusJwtDecoder decoder = NimbusJwtDecoder.withSecretKey(secretKey)
|
||||
.macAlgorithm(MacAlgorithm.HS256)
|
||||
.jwtProcessorCustomizer((p) -> p
|
||||
.setJWSTypeVerifier(new DefaultJOSEObjectTypeVerifier<>(new JOSEObjectType("JWS")))
|
||||
)
|
||||
.build();
|
||||
decoder.setJwtValidator(JwtValidators.createDefaultWithValidators(new JwtTypeValidator("JWS")));
|
||||
// @formatter:on
|
||||
assertThat(decoder.decode(signedJwt.serialize()).hasClaim(JwtClaimNames.EXP)).isNotNull();
|
||||
}
|
||||
@ -837,6 +833,7 @@ public class NimbusJwtDecoderTests {
|
||||
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withPublicKey(TestKeys.DEFAULT_PUBLIC_KEY)
|
||||
.validateType(false)
|
||||
.build();
|
||||
jwtDecoder.setJwtValidator((jwt) -> OAuth2TokenValidatorResult.success());
|
||||
RSAPrivateKey privateKey = TestKeys.DEFAULT_PRIVATE_KEY;
|
||||
SignedJWT jwt = signedJwt(privateKey,
|
||||
new JWSHeader.Builder(JWSAlgorithm.RS256).type(JOSEObjectType.JOSE).build(),
|
||||
@ -849,6 +846,7 @@ public class NimbusJwtDecoderTests {
|
||||
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withSecretKey(TestKeys.DEFAULT_SECRET_KEY)
|
||||
.validateType(false)
|
||||
.build();
|
||||
jwtDecoder.setJwtValidator((jwt) -> OAuth2TokenValidatorResult.success());
|
||||
SignedJWT jwt = signedJwt(TestKeys.DEFAULT_SECRET_KEY,
|
||||
new JWSHeader.Builder(JWSAlgorithm.HS256).type(JOSEObjectType.JOSE).build(),
|
||||
new JWTClaimsSet.Builder().subject("subject").build());
|
||||
|
@ -667,6 +667,7 @@ public class NimbusReactiveJwtDecoderTests {
|
||||
NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withPublicKey(TestKeys.DEFAULT_PUBLIC_KEY)
|
||||
.validateType(false)
|
||||
.build();
|
||||
jwtDecoder.setJwtValidator((jwt) -> OAuth2TokenValidatorResult.success());
|
||||
RSAPrivateKey privateKey = TestKeys.DEFAULT_PRIVATE_KEY;
|
||||
SignedJWT jwt = signedJwt(privateKey,
|
||||
new JWSHeader.Builder(JWSAlgorithm.RS256).type(JOSEObjectType.JOSE).build(),
|
||||
@ -679,6 +680,7 @@ public class NimbusReactiveJwtDecoderTests {
|
||||
NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withSecretKey(TestKeys.DEFAULT_SECRET_KEY)
|
||||
.validateType(false)
|
||||
.build();
|
||||
jwtDecoder.setJwtValidator((jwt) -> OAuth2TokenValidatorResult.success());
|
||||
SignedJWT jwt = signedJwt(TestKeys.DEFAULT_SECRET_KEY,
|
||||
new JWSHeader.Builder(JWSAlgorithm.HS256).type(JOSEObjectType.JOSE).build(),
|
||||
new JWTClaimsSet.Builder().subject("subject").build());
|
||||
@ -693,6 +695,7 @@ public class NimbusReactiveJwtDecoderTests {
|
||||
NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withJwkSource((jwt) -> Flux.just(jwk))
|
||||
.validateType(false)
|
||||
.build();
|
||||
jwtDecoder.setJwtValidator((jwt) -> OAuth2TokenValidatorResult.success());
|
||||
SignedJWT jwt = signedJwt(TestKeys.DEFAULT_PRIVATE_KEY,
|
||||
new JWSHeader.Builder(JWSAlgorithm.RS256).type(JOSEObjectType.JOSE).build(),
|
||||
new JWTClaimsSet.Builder().subject("subject").build());
|
||||
|
Loading…
x
Reference in New Issue
Block a user