소스 검색

Change Type Validation Default

NimbusJwtDecoder and NimbusReactiveJwtDecoder now use
Spring Security's JwtTypeValidator by default instead
of Nimbus's type validator.

Closes gh-17181
Josh Cummings 2 달 전
부모
커밋
6d3b54df21

+ 5 - 11
config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcBackChannelLogoutAuthenticationProvider.java

@@ -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;

+ 5 - 10
config/src/main/java/org/springframework/security/config/web/server/OidcBackChannelLogoutReactiveAuthenticationManager.java

@@ -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()));

+ 79 - 0
docs/modules/ROOT/pages/migration/reactive.adoc

@@ -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

+ 6 - 2
oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtValidators.java

@@ -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);
 	}
 

+ 3 - 3
oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoder.java

@@ -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;
 

+ 4 - 4
oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoder.java

@@ -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;
 

+ 2 - 1
oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtValidatorsTests.java

@@ -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

+ 4 - 6
oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoderTests.java

@@ -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());

+ 3 - 0
oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoderTests.java

@@ -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());