Add Post-Processor for JWTProcessor Configuration

Extends all existing builders in NimbusJwtDecoder and NimbusReactiveJwtDecoder with a
post-processor hook to apply changes on the JWTProcessor used for token verification.
Test cases added show how this is used to configure the JWTProcessor to allow additional
JWT typ headers.

Closes gh-8730
This commit is contained in:
Jan Oopkaup 2020-06-22 13:58:56 +02:00 committed by Josh Cummings
parent 3c2a97ed29
commit d31fff11b3
No known key found for this signature in database
GPG Key ID: 49EF60DD7FF83443
4 changed files with 280 additions and 0 deletions

View File

@ -219,10 +219,12 @@ public final class NimbusJwtDecoder implements JwtDecoder {
private Set<SignatureAlgorithm> signatureAlgorithms = new HashSet<>();
private RestOperations restOperations = new RestTemplate();
private Cache cache;
private Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer;
private JwkSetUriJwtDecoderBuilder(String jwkSetUri) {
Assert.hasText(jwkSetUri, "jwkSetUri cannot be empty");
this.jwkSetUri = jwkSetUri;
this.jwtProcessorCustomizer = (processor) -> {};
}
/**
@ -282,6 +284,20 @@ public final class NimbusJwtDecoder implements JwtDecoder {
return this;
}
/**
* Use the given {@link Consumer} to customize the {@link JWTProcessor ConfigurableJWTProcessor} before
* passing it to the build {@link NimbusJwtDecoder}.
*
* @param jwtProcessorCustomizer the callback used to alter the processor
* @return a {@link JwkSetUriJwtDecoderBuilder} for further configurations
* @since 5.4
*/
public JwkSetUriJwtDecoderBuilder jwtProcessorCustomizer(Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer) {
Assert.notNull(jwtProcessorCustomizer, "jwtProcessorCustomizer cannot be null");
this.jwtProcessorCustomizer = jwtProcessorCustomizer;
return this;
}
JWSKeySelector<SecurityContext> jwsKeySelector(JWKSource<SecurityContext> jwkSource) {
if (this.signatureAlgorithms.isEmpty()) {
return new JWSVerificationKeySelector<>(JWSAlgorithm.RS256, jwkSource);
@ -312,6 +328,8 @@ public final class NimbusJwtDecoder implements JwtDecoder {
// Spring Security validates the claim set independent from Nimbus
jwtProcessor.setJWTClaimsSetVerifier((claims, context) -> { });
this.jwtProcessorCustomizer.accept(jwtProcessor);
return jwtProcessor;
}
@ -414,11 +432,13 @@ public final class NimbusJwtDecoder implements JwtDecoder {
public static final class PublicKeyJwtDecoderBuilder {
private JWSAlgorithm jwsAlgorithm;
private RSAPublicKey key;
private Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer;
private PublicKeyJwtDecoderBuilder(RSAPublicKey key) {
Assert.notNull(key, "key cannot be null");
this.jwsAlgorithm = JWSAlgorithm.RS256;
this.key = key;
this.jwtProcessorCustomizer = (processor) -> {};
}
/**
@ -437,6 +457,20 @@ public final class NimbusJwtDecoder implements JwtDecoder {
return this;
}
/**
* Use the given {@link Consumer} to customize the {@link JWTProcessor ConfigurableJWTProcessor} before
* passing it to the build {@link NimbusJwtDecoder}.
*
* @param jwtProcessorCustomizer the callback used to alter the processor
* @return a {@link PublicKeyJwtDecoderBuilder} for further configurations
* @since 5.4
*/
public PublicKeyJwtDecoderBuilder jwtProcessorCustomizer(Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer) {
Assert.notNull(jwtProcessorCustomizer, "jwtProcessorCustomizer cannot be null");
this.jwtProcessorCustomizer = jwtProcessorCustomizer;
return this;
}
JWTProcessor<SecurityContext> processor() {
if (!JWSAlgorithm.Family.RSA.contains(this.jwsAlgorithm)) {
throw new IllegalStateException("The provided key is of type RSA; " +
@ -452,6 +486,8 @@ public final class NimbusJwtDecoder implements JwtDecoder {
// Spring Security validates the claim set independent from Nimbus
jwtProcessor.setJWTClaimsSetVerifier((claims, context) -> { });
this.jwtProcessorCustomizer.accept(jwtProcessor);
return jwtProcessor;
}
@ -471,10 +507,12 @@ public final class NimbusJwtDecoder implements JwtDecoder {
public static final class SecretKeyJwtDecoderBuilder {
private final SecretKey secretKey;
private JWSAlgorithm jwsAlgorithm = JWSAlgorithm.HS256;
private Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer;
private SecretKeyJwtDecoderBuilder(SecretKey secretKey) {
Assert.notNull(secretKey, "secretKey cannot be null");
this.secretKey = secretKey;
this.jwtProcessorCustomizer = (processor) -> {};
}
/**
@ -494,6 +532,20 @@ public final class NimbusJwtDecoder implements JwtDecoder {
return this;
}
/**
* Use the given {@link Consumer} to customize the {@link JWTProcessor ConfigurableJWTProcessor} before
* passing it to the build {@link NimbusJwtDecoder}.
*
* @param jwtProcessorCustomizer the callback used to alter the processor
* @return a {@link SecretKeyJwtDecoderBuilder} for further configurations
* @since 5.4
*/
public SecretKeyJwtDecoderBuilder jwtProcessorCustomizer(Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer) {
Assert.notNull(jwtProcessorCustomizer, "jwtProcessorCustomizer cannot be null");
this.jwtProcessorCustomizer = jwtProcessorCustomizer;
return this;
}
/**
* Build the configured {@link NimbusJwtDecoder}.
*
@ -512,6 +564,8 @@ public final class NimbusJwtDecoder implements JwtDecoder {
// Spring Security validates the claim set independent from Nimbus
jwtProcessor.setJWTClaimsSetVerifier((claims, context) -> { });
this.jwtProcessorCustomizer.accept(jwtProcessor);
return jwtProcessor;
}
}

View File

@ -44,6 +44,7 @@ import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.JWTParser;
import com.nimbusds.jwt.PlainJWT;
import com.nimbusds.jwt.SignedJWT;
import com.nimbusds.jwt.proc.ConfigurableJWTProcessor;
import com.nimbusds.jwt.proc.DefaultJWTProcessor;
import com.nimbusds.jwt.proc.JWTProcessor;
import reactor.core.publisher.Flux;
@ -245,10 +246,12 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
private final String jwkSetUri;
private Set<SignatureAlgorithm> signatureAlgorithms = new HashSet<>();
private WebClient webClient = WebClient.create();
private Consumer<ConfigurableJWTProcessor<JWKSecurityContext>> jwtProcessorCustomizer;
private JwkSetUriReactiveJwtDecoderBuilder(String jwkSetUri) {
Assert.hasText(jwkSetUri, "jwkSetUri cannot be empty");
this.jwkSetUri = jwkSetUri;
this.jwtProcessorCustomizer = (processor) -> {};
}
/**
@ -294,6 +297,20 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
return this;
}
/**
* Use the given {@link Consumer} to customize the {@link JWTProcessor ConfigurableJWTProcessor} before
* passing it to the build {@link NimbusReactiveJwtDecoder}.
*
* @param jwtProcessorCustomizer the callback used to alter the processor
* @return a {@link JwkSetUriReactiveJwtDecoderBuilder} for further configurations
* @since 5.4
*/
public JwkSetUriReactiveJwtDecoderBuilder jwtProcessorCustomizer(Consumer<ConfigurableJWTProcessor<JWKSecurityContext>> jwtProcessorCustomizer) {
Assert.notNull(jwtProcessorCustomizer, "jwtProcessorCustomizer cannot be null");
this.jwtProcessorCustomizer = jwtProcessorCustomizer;
return this;
}
/**
* Build the configured {@link NimbusReactiveJwtDecoder}.
*
@ -323,6 +340,8 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
jwtProcessor.setJWSKeySelector(jwsKeySelector);
jwtProcessor.setJWTClaimsSetVerifier((claims, context) -> {});
this.jwtProcessorCustomizer.accept(jwtProcessor);
ReactiveRemoteJWKSource source = new ReactiveRemoteJWKSource(this.jwkSetUri);
source.setWebClient(this.webClient);
@ -360,11 +379,13 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
public static final class PublicKeyReactiveJwtDecoderBuilder {
private final RSAPublicKey key;
private JWSAlgorithm jwsAlgorithm;
private Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer;
private PublicKeyReactiveJwtDecoderBuilder(RSAPublicKey key) {
Assert.notNull(key, "key cannot be null");
this.key = key;
this.jwsAlgorithm = JWSAlgorithm.RS256;
this.jwtProcessorCustomizer = (processor) -> {};
}
/**
@ -382,6 +403,20 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
return this;
}
/**
* Use the given {@link Consumer} to customize the {@link JWTProcessor ConfigurableJWTProcessor} before
* passing it to the build {@link NimbusReactiveJwtDecoder}.
*
* @param jwtProcessorCustomizer the callback used to alter the processor
* @return a {@link PublicKeyReactiveJwtDecoderBuilder} for further configurations
* @since 5.4
*/
public PublicKeyReactiveJwtDecoderBuilder jwtProcessorCustomizer(Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer) {
Assert.notNull(jwtProcessorCustomizer, "jwtProcessorCustomizer cannot be null");
this.jwtProcessorCustomizer = jwtProcessorCustomizer;
return this;
}
/**
* Build the configured {@link NimbusReactiveJwtDecoder}.
*
@ -406,6 +441,8 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
// Spring Security validates the claim set independent from Nimbus
jwtProcessor.setJWTClaimsSetVerifier((claims, context) -> { });
this.jwtProcessorCustomizer.accept(jwtProcessor);
return jwt -> Mono.just(createClaimsSet(jwtProcessor, jwt, null));
}
}
@ -418,10 +455,12 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
public static final class SecretKeyReactiveJwtDecoderBuilder {
private final SecretKey secretKey;
private JWSAlgorithm jwsAlgorithm = JWSAlgorithm.HS256;
private Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer;
private SecretKeyReactiveJwtDecoderBuilder(SecretKey secretKey) {
Assert.notNull(secretKey, "secretKey cannot be null");
this.secretKey = secretKey;
this.jwtProcessorCustomizer = (processor) -> {};
}
/**
@ -441,6 +480,20 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
return this;
}
/**
* Use the given {@link Consumer} to customize the {@link JWTProcessor ConfigurableJWTProcessor} before
* passing it to the build {@link NimbusReactiveJwtDecoder}.
*
* @param jwtProcessorCustomizer the callback used to alter the processor
* @return a {@link SecretKeyReactiveJwtDecoderBuilder} for further configurations
* @since 5.4
*/
public SecretKeyReactiveJwtDecoderBuilder jwtProcessorCustomizer(Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer) {
Assert.notNull(jwtProcessorCustomizer, "jwtProcessorCustomizer cannot be null");
this.jwtProcessorCustomizer = jwtProcessorCustomizer;
return this;
}
/**
* Build the configured {@link NimbusReactiveJwtDecoder}.
*
@ -459,6 +512,8 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
// Spring Security validates the claim set independent from Nimbus
jwtProcessor.setJWTClaimsSetVerifier((claims, context) -> { });
this.jwtProcessorCustomizer.accept(jwtProcessor);
return jwt -> Mono.just(createClaimsSet(jwtProcessor, jwt, null));
}
}
@ -471,10 +526,12 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
public static final class JwkSourceReactiveJwtDecoderBuilder {
private final Function<SignedJWT, Flux<JWK>> jwkSource;
private JWSAlgorithm jwsAlgorithm = JWSAlgorithm.RS256;
private Consumer<ConfigurableJWTProcessor<JWKSecurityContext>> jwtProcessorCustomizer;
private JwkSourceReactiveJwtDecoderBuilder(Function<SignedJWT, Flux<JWK>> jwkSource) {
Assert.notNull(jwkSource, "jwkSource cannot be null");
this.jwkSource = jwkSource;
this.jwtProcessorCustomizer = (processor) -> {};
}
/**
@ -490,6 +547,20 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
return this;
}
/**
* Use the given {@link Consumer} to customize the {@link JWTProcessor ConfigurableJWTProcessor} before
* passing it to the build {@link NimbusReactiveJwtDecoder}.
*
* @param jwtProcessorCustomizer the callback used to alter the processor
* @return a {@link JwkSourceReactiveJwtDecoderBuilder} for further configurations
* @since 5.4
*/
public JwkSourceReactiveJwtDecoderBuilder jwtProcessorCustomizer(Consumer<ConfigurableJWTProcessor<JWKSecurityContext>> jwtProcessorCustomizer) {
Assert.notNull(jwtProcessorCustomizer, "jwtProcessorCustomizer cannot be null");
this.jwtProcessorCustomizer = jwtProcessorCustomizer;
return this;
}
/**
* Build the configured {@link NimbusReactiveJwtDecoder}.
*
@ -507,6 +578,8 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
jwtProcessor.setJWSKeySelector(jwsKeySelector);
jwtProcessor.setJWTClaimsSetVerifier((claims, context) -> {});
this.jwtProcessorCustomizer.accept(jwtProcessor);
return jwt -> {
if (jwt instanceof SignedJWT) {
return this.jwkSource.apply((SignedJWT) jwt)

View File

@ -35,6 +35,7 @@ import java.util.Map;
import java.util.concurrent.Callable;
import javax.crypto.SecretKey;
import com.nimbusds.jose.JOSEObjectType;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSSigner;
@ -42,6 +43,7 @@ import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.BadJOSEException;
import com.nimbusds.jose.proc.DefaultJOSEObjectTypeVerifier;
import com.nimbusds.jose.proc.JWSKeySelector;
import com.nimbusds.jose.proc.JWSVerificationKeySelector;
import com.nimbusds.jose.proc.SecurityContext;
@ -346,6 +348,30 @@ public class NimbusJwtDecoderTests {
.isInstanceOf(BadJwtException.class);
}
// gh-8730
@Test
public void withPublicKeyWhenUsingCustomTypeHeaderThenSuccessfullyDecodes() throws Exception {
RSAPublicKey publicKey = TestKeys.DEFAULT_PUBLIC_KEY;
RSAPrivateKey privateKey = TestKeys.DEFAULT_PRIVATE_KEY;
JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS256).type(new JOSEObjectType("JWS")).build();
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
.expirationTime(Date.from(Instant.now().plusSeconds(60)))
.build();
SignedJWT signedJwt = signedJwt(privateKey, header, claimsSet);
NimbusJwtDecoder decoder = withPublicKey(publicKey)
.signatureAlgorithm(SignatureAlgorithm.RS256)
.jwtProcessorCustomizer(p -> p.setJWSTypeVerifier(new DefaultJOSEObjectTypeVerifier<>(new JOSEObjectType("JWS"))))
.build();
assertThat(decoder.decode(signedJwt.serialize()).containsClaim(JwtClaimNames.EXP)).isNotNull();
}
@Test
public void withPublicKeyWhenJwtProcessorCustomizerNullThenThrowsIllegalArgumentException() {
assertThatThrownBy(() -> withPublicKey(key()).jwtProcessorCustomizer(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("jwtProcessorCustomizer cannot be null");
}
@Test
public void withSecretKeyWhenNullThenThrowsIllegalArgumentException() {
assertThatThrownBy(() -> withSecretKey(null))
@ -407,6 +433,30 @@ public class NimbusJwtDecoderTests {
.isEqualTo("test-subject");
}
// gh-8730
@Test
public void withSecretKeyWhenUsingCustomTypeHeaderThenSuccessfullyDecodes() throws Exception {
SecretKey secretKey = TestKeys.DEFAULT_SECRET_KEY;
JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.HS256).type(new JOSEObjectType("JWS")).build();
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
.expirationTime(Date.from(Instant.now().plusSeconds(60)))
.build();
SignedJWT signedJwt = signedJwt(secretKey, header, claimsSet);
NimbusJwtDecoder decoder = withSecretKey(secretKey)
.macAlgorithm(MacAlgorithm.HS256)
.jwtProcessorCustomizer(p -> p.setJWSTypeVerifier(new DefaultJOSEObjectTypeVerifier<>(new JOSEObjectType("JWS"))))
.build();
assertThat(decoder.decode(signedJwt.serialize()).containsClaim(JwtClaimNames.EXP)).isNotNull();
}
@Test
public void withSecretKeyWhenJwtProcessorCustomizerNullThenThrowsIllegalArgumentException() {
SecretKey secretKey = TestKeys.DEFAULT_SECRET_KEY;
assertThatThrownBy(() -> withSecretKey(secretKey).jwtProcessorCustomizer(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("jwtProcessorCustomizer cannot be null");
}
@Test
public void jwsKeySelectorWhenNoAlgorithmThenReturnsRS256Selector() {
JWKSource<SecurityContext> jwkSource = mock(JWKSource.class);
@ -524,6 +574,28 @@ public class NimbusJwtDecoderTests {
.hasMessageContaining("An error occurred while attempting to decode the Jwt");
}
// gh-8730
@Test
public void withJwkSetUriWhenUsingCustomTypeHeaderThenRefuseOmittedType() throws Exception {
RestOperations restOperations = mock(RestOperations.class);
when(restOperations.exchange(any(RequestEntity.class), eq(String.class)))
.thenReturn(new ResponseEntity<>(JWK_SET, HttpStatus.OK));
NimbusJwtDecoder jwtDecoder = withJwkSetUri(JWK_SET_URI)
.restOperations(restOperations)
.jwtProcessorCustomizer(p -> p.setJWSTypeVerifier(new DefaultJOSEObjectTypeVerifier<>(new JOSEObjectType("JWS"))))
.build();
assertThatCode(() -> jwtDecoder.decode(SIGNED_JWT))
.isInstanceOf(BadJwtException.class)
.hasMessageContaining("An error occurred while attempting to decode the Jwt: Required JOSE header \"typ\" (type) parameter is missing");
}
@Test
public void withJwkSetUriWhenJwtProcessorCustomizerNullThenThrowsIllegalArgumentException() {
assertThatThrownBy(() -> withJwkSetUri(JWK_SET_URI).jwtProcessorCustomizer(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("jwtProcessorCustomizer cannot be null");
}
private RSAPublicKey key() throws InvalidKeySpecException {
byte[] decoded = Base64.getDecoder().decode(VERIFY_KEY.getBytes());
EncodedKeySpec spec = new X509EncodedKeySpec(decoded);

View File

@ -31,12 +31,14 @@ import java.util.Date;
import java.util.Map;
import javax.crypto.SecretKey;
import com.nimbusds.jose.JOSEObjectType;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.DefaultJOSEObjectTypeVerifier;
import com.nimbusds.jose.proc.JWKSecurityContext;
import com.nimbusds.jose.proc.JWSKeySelector;
import com.nimbusds.jose.proc.JWSVerificationKeySelector;
@ -44,6 +46,7 @@ import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import org.assertj.core.api.AssertionsForClassTypes;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
@ -269,6 +272,13 @@ public class NimbusReactiveJwtDecoderTests {
assertThatCode(() -> builder.jwsAlgorithm(null)).isInstanceOf(IllegalArgumentException.class);
}
@Test
public void withJwkSetUriWhenJwtProcessorCustomizerNullThenThrowsIllegalArgumentException() {
assertThatCode(() -> withJwkSetUri(jwkSetUri).jwtProcessorCustomizer(null).build())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("jwtProcessorCustomizer cannot be null");
}
@Test
public void restOperationsWhenNullThenThrowsException() {
NimbusReactiveJwtDecoder.JwkSetUriReactiveJwtDecoderBuilder builder = withJwkSetUri(this.jwkSetUri);
@ -286,6 +296,19 @@ public class NimbusReactiveJwtDecoderTests {
verify(webClient).get();
}
// gh-8730
@Test
public void withJwkSetUriWhenUsingCustomTypeHeaderThenRefuseOmittedType() {
WebClient webClient = mockJwkSetResponse(this.jwkSet);
NimbusReactiveJwtDecoder decoder = withJwkSetUri(this.jwkSetUri)
.webClient(webClient)
.jwtProcessorCustomizer(p -> p.setJWSTypeVerifier(new DefaultJOSEObjectTypeVerifier<>(new JOSEObjectType("JWS"))))
.build();
assertThatCode(() -> decoder.decode(messageReadToken).block())
.isInstanceOf(BadJwtException.class)
.hasRootCauseMessage("Required JOSE header \"typ\" (type) parameter is missing");
}
@Test
public void withPublicKeyWhenNullThenThrowsException() {
assertThatThrownBy(() -> withPublicKey(null))
@ -300,6 +323,13 @@ public class NimbusReactiveJwtDecoderTests {
.isInstanceOf(IllegalStateException.class);
}
@Test
public void buildWhenJwtProcessorCustomizerNullThenThrowsIllegalArgumentException() {
assertThatCode(() -> withPublicKey(key()).jwtProcessorCustomizer(null).build())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("jwtProcessorCustomizer cannot be null");
}
@Test
public void decodeWhenUsingPublicKeyThenSuccessfullyDecodes() throws Exception {
NimbusReactiveJwtDecoder decoder = withPublicKey(key()).build();
@ -325,12 +355,31 @@ public class NimbusReactiveJwtDecoderTests {
.isInstanceOf(BadJwtException.class);
}
// gh-8730
@Test
public void withPublicKeyWhenUsingCustomTypeHeaderThenRefuseOmittedType() throws Exception {
NimbusReactiveJwtDecoder decoder = withPublicKey(key())
.jwtProcessorCustomizer(p -> p.setJWSTypeVerifier(new DefaultJOSEObjectTypeVerifier<>(new JOSEObjectType("JWS"))))
.build();
AssertionsForClassTypes.assertThatCode(() -> decoder.decode(this.rsa256).block())
.isInstanceOf(BadJwtException.class)
.hasRootCauseMessage("Required JOSE header \"typ\" (type) parameter is missing");
}
@Test
public void withJwkSourceWhenNullThenThrowsException() {
assertThatCode(() -> withJwkSource(null))
.isInstanceOf(IllegalArgumentException.class);
}
@Test
public void withJwkSourceWhenJwtProcessorCustomizerNullThenThrowsIllegalArgumentException() {
assertThatCode(() -> withJwkSource(jwt -> Flux.empty()).jwtProcessorCustomizer(null).build())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("jwtProcessorCustomizer cannot be null");
}
@Test
public void decodeWhenCustomJwkSourceResolutionThenDecodes() {
NimbusReactiveJwtDecoder decoder =
@ -342,6 +391,18 @@ public class NimbusReactiveJwtDecoderTests {
.isNotNull();
}
// gh-8730
@Test
public void withJwkSourceWhenUsingCustomTypeHeaderThenRefuseOmittedType() {
NimbusReactiveJwtDecoder decoder = withJwkSource(jwt -> Flux.empty())
.jwtProcessorCustomizer(p -> p.setJWSTypeVerifier(new DefaultJOSEObjectTypeVerifier<>(new JOSEObjectType("JWS"))))
.build();
assertThatCode(() -> decoder.decode(this.messageReadToken).block())
.isInstanceOf(BadJwtException.class)
.hasRootCauseMessage("Required JOSE header \"typ\" (type) parameter is missing");
}
@Test
public void withSecretKeyWhenSecretKeyNullThenThrowsIllegalArgumentException() {
assertThatThrownBy(() -> withSecretKey(null))
@ -349,6 +410,14 @@ public class NimbusReactiveJwtDecoderTests {
.hasMessage("secretKey cannot be null");
}
@Test
public void withSecretKeyWhenJwtProcessorCustomizerNullThenThrowsIllegalArgumentException() {
SecretKey secretKey = TestKeys.DEFAULT_SECRET_KEY;
assertThatThrownBy(() -> withSecretKey(secretKey).jwtProcessorCustomizer(null).build())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("jwtProcessorCustomizer cannot be null");
}
@Test
public void withSecretKeyWhenMacAlgorithmNullThenThrowsIllegalArgumentException() {
SecretKey secretKey = TestKeys.DEFAULT_SECRET_KEY;
@ -372,6 +441,18 @@ public class NimbusReactiveJwtDecoderTests {
assertThat(jwt.getSubject()).isEqualTo("test-subject");
}
// gh-8730
@Test
public void withSecretKeyWhenUsingCustomTypeHeaderThenRefuseOmittedType() {
SecretKey secretKey = TestKeys.DEFAULT_SECRET_KEY;
NimbusReactiveJwtDecoder decoder = withSecretKey(secretKey)
.jwtProcessorCustomizer(p -> p.setJWSTypeVerifier(new DefaultJOSEObjectTypeVerifier<>(new JOSEObjectType("JWS"))))
.build();
assertThatCode(() -> decoder.decode(messageReadToken).block())
.isInstanceOf(BadJwtException.class)
.hasRootCauseMessage("Required JOSE header \"typ\" (type) parameter is missing");
}
@Test
public void decodeWhenSecretKeyAndAlgorithmMismatchThenThrowsJwtException() throws Exception {
SecretKey secretKey = TestKeys.DEFAULT_SECRET_KEY;