mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-23 20:42:14 +00:00
Add Type Validation
Closes gh-16672
This commit is contained in:
parent
0c7b05a0e3
commit
81e2fd2fe8
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2025 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.oauth2.jwt;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||||
|
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||||
|
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
|
||||||
|
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A validator for the {@code typ} header. Specifically for indicating the header values
|
||||||
|
* that a given {@link JwtDecoder} will support.
|
||||||
|
*
|
||||||
|
* @author Josh Cummings
|
||||||
|
* @since 6.5
|
||||||
|
*/
|
||||||
|
public final class JwtTypeValidator implements OAuth2TokenValidator<Jwt> {
|
||||||
|
|
||||||
|
private Collection<String> validTypes;
|
||||||
|
|
||||||
|
private boolean allowEmpty;
|
||||||
|
|
||||||
|
public JwtTypeValidator(Collection<String> validTypes) {
|
||||||
|
Assert.notEmpty(validTypes, "validTypes cannot be empty");
|
||||||
|
this.validTypes = new ArrayList<>(validTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Require that the {@code typ} header be {@code JWT} or absent
|
||||||
|
*/
|
||||||
|
public static JwtTypeValidator jwt() {
|
||||||
|
JwtTypeValidator validator = new JwtTypeValidator(List.of("JWT"));
|
||||||
|
validator.setAllowEmpty(true);
|
||||||
|
return validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to allow the {@code typ} header to be empty. The default value is
|
||||||
|
* {@code false}
|
||||||
|
*/
|
||||||
|
public void setAllowEmpty(boolean allowEmpty) {
|
||||||
|
this.allowEmpty = allowEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OAuth2TokenValidatorResult validate(Jwt token) {
|
||||||
|
String typ = (String) token.getHeaders().get(JoseHeaderNames.TYP);
|
||||||
|
if (this.allowEmpty && !StringUtils.hasText(typ)) {
|
||||||
|
return OAuth2TokenValidatorResult.success();
|
||||||
|
}
|
||||||
|
if (this.validTypes.contains(typ)) {
|
||||||
|
return OAuth2TokenValidatorResult.success();
|
||||||
|
}
|
||||||
|
return OAuth2TokenValidatorResult.failure(new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN,
|
||||||
|
"the given typ value needs to be one of " + this.validTypes,
|
||||||
|
"https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.9"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -33,6 +33,7 @@ import java.util.function.Function;
|
|||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
|
|
||||||
import com.nimbusds.jose.JOSEException;
|
import com.nimbusds.jose.JOSEException;
|
||||||
|
import com.nimbusds.jose.JOSEObjectType;
|
||||||
import com.nimbusds.jose.JWSAlgorithm;
|
import com.nimbusds.jose.JWSAlgorithm;
|
||||||
import com.nimbusds.jose.KeySourceException;
|
import com.nimbusds.jose.KeySourceException;
|
||||||
import com.nimbusds.jose.RemoteKeySourceException;
|
import com.nimbusds.jose.RemoteKeySourceException;
|
||||||
@ -41,6 +42,8 @@ import com.nimbusds.jose.jwk.source.JWKSetCacheRefreshEvaluator;
|
|||||||
import com.nimbusds.jose.jwk.source.JWKSetSource;
|
import com.nimbusds.jose.jwk.source.JWKSetSource;
|
||||||
import com.nimbusds.jose.jwk.source.JWKSource;
|
import com.nimbusds.jose.jwk.source.JWKSource;
|
||||||
import com.nimbusds.jose.jwk.source.JWKSourceBuilder;
|
import com.nimbusds.jose.jwk.source.JWKSourceBuilder;
|
||||||
|
import com.nimbusds.jose.proc.DefaultJOSEObjectTypeVerifier;
|
||||||
|
import com.nimbusds.jose.proc.JOSEObjectTypeVerifier;
|
||||||
import com.nimbusds.jose.proc.JWSKeySelector;
|
import com.nimbusds.jose.proc.JWSKeySelector;
|
||||||
import com.nimbusds.jose.proc.JWSVerificationKeySelector;
|
import com.nimbusds.jose.proc.JWSVerificationKeySelector;
|
||||||
import com.nimbusds.jose.proc.SecurityContext;
|
import com.nimbusds.jose.proc.SecurityContext;
|
||||||
@ -265,11 +268,20 @@ public final class NimbusJwtDecoder implements JwtDecoder {
|
|||||||
*/
|
*/
|
||||||
public static final class JwkSetUriJwtDecoderBuilder {
|
public static final class JwkSetUriJwtDecoderBuilder {
|
||||||
|
|
||||||
|
private static final JOSEObjectTypeVerifier<SecurityContext> JWT_TYPE_VERIFIER = new DefaultJOSEObjectTypeVerifier<>(
|
||||||
|
JOSEObjectType.JWT, null);
|
||||||
|
|
||||||
|
private static final JOSEObjectTypeVerifier<SecurityContext> NO_TYPE_VERIFIER = (header, context) -> {
|
||||||
|
};
|
||||||
|
|
||||||
private Function<RestOperations, String> jwkSetUri;
|
private Function<RestOperations, String> jwkSetUri;
|
||||||
|
|
||||||
private Function<JWKSource<SecurityContext>, Set<JWSAlgorithm>> defaultAlgorithms = (source) -> Set
|
private Function<JWKSource<SecurityContext>, Set<JWSAlgorithm>> defaultAlgorithms = (source) -> Set
|
||||||
.of(JWSAlgorithm.RS256);
|
.of(JWSAlgorithm.RS256);
|
||||||
|
|
||||||
|
private JOSEObjectTypeVerifier<SecurityContext> typeVerifier = new DefaultJOSEObjectTypeVerifier<>(
|
||||||
|
JOSEObjectType.JWT, null);
|
||||||
|
|
||||||
private Set<SignatureAlgorithm> signatureAlgorithms = new HashSet<>();
|
private Set<SignatureAlgorithm> signatureAlgorithms = new HashSet<>();
|
||||||
|
|
||||||
private RestOperations restOperations = new RestTemplate();
|
private RestOperations restOperations = new RestTemplate();
|
||||||
@ -295,6 +307,54 @@ public final class NimbusJwtDecoder implements JwtDecoder {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to use Nimbus's typ header verification. This is {@code true} by
|
||||||
|
* default, however it may change to {@code false} in a future major release.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* By turning off this feature, {@link NimbusJwtDecoder} expects applications to
|
||||||
|
* check the {@code typ} header themselves in order to determine what kind of
|
||||||
|
* validation is needed
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This is done for you when you use {@link JwtValidators} to construct a
|
||||||
|
* validator.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* That means that this: <code>
|
||||||
|
* NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build();
|
||||||
|
* jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(issuer);
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Is equivalent to this: <code>
|
||||||
|
* NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer)
|
||||||
|
* .validateType(false)
|
||||||
|
* .build();
|
||||||
|
* jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(issuer);
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The difference is that by setting this to {@code false}, it allows you to
|
||||||
|
* provide validation by type, like for {@code at+jwt}:
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
* NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer)
|
||||||
|
* .validateType(false)
|
||||||
|
* .build();
|
||||||
|
* jwtDecoder.setJwtValidator(new MyAtJwtValidator());
|
||||||
|
* </code>
|
||||||
|
* @param shouldValidateTypHeader whether Nimbus should validate the typ header or
|
||||||
|
* not
|
||||||
|
* @return a {@link JwkSetUriJwtDecoderBuilder} for further configurations
|
||||||
|
* @since 6.5
|
||||||
|
*/
|
||||||
|
public JwkSetUriJwtDecoderBuilder validateType(boolean shouldValidateTypHeader) {
|
||||||
|
this.typeVerifier = shouldValidateTypHeader ? JWT_TYPE_VERIFIER : NO_TYPE_VERIFIER;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Append the given signing
|
* Append the given signing
|
||||||
* <a href="https://tools.ietf.org/html/rfc7515#section-4.1.1" target=
|
* <a href="https://tools.ietf.org/html/rfc7515#section-4.1.1" target=
|
||||||
@ -389,6 +449,7 @@ public final class NimbusJwtDecoder implements JwtDecoder {
|
|||||||
JWTProcessor<SecurityContext> processor() {
|
JWTProcessor<SecurityContext> processor() {
|
||||||
JWKSource<SecurityContext> jwkSource = jwkSource();
|
JWKSource<SecurityContext> jwkSource = jwkSource();
|
||||||
ConfigurableJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();
|
ConfigurableJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();
|
||||||
|
jwtProcessor.setJWSTypeVerifier(this.typeVerifier);
|
||||||
jwtProcessor.setJWSKeySelector(jwsKeySelector(jwkSource));
|
jwtProcessor.setJWSKeySelector(jwsKeySelector(jwkSource));
|
||||||
// Spring Security validates the claim set independent from Nimbus
|
// Spring Security validates the claim set independent from Nimbus
|
||||||
jwtProcessor.setJWTClaimsSetVerifier((claims, context) -> {
|
jwtProcessor.setJWTClaimsSetVerifier((claims, context) -> {
|
||||||
@ -481,8 +542,17 @@ public final class NimbusJwtDecoder implements JwtDecoder {
|
|||||||
*/
|
*/
|
||||||
public static final class PublicKeyJwtDecoderBuilder {
|
public static final class PublicKeyJwtDecoderBuilder {
|
||||||
|
|
||||||
|
private static final JOSEObjectTypeVerifier<SecurityContext> JWT_TYPE_VERIFIER = new DefaultJOSEObjectTypeVerifier<>(
|
||||||
|
JOSEObjectType.JWT, null);
|
||||||
|
|
||||||
|
private static final JOSEObjectTypeVerifier<SecurityContext> NO_TYPE_VERIFIER = (header, context) -> {
|
||||||
|
};
|
||||||
|
|
||||||
private JWSAlgorithm jwsAlgorithm;
|
private JWSAlgorithm jwsAlgorithm;
|
||||||
|
|
||||||
|
private JOSEObjectTypeVerifier<SecurityContext> typeVerifier = new DefaultJOSEObjectTypeVerifier<>(
|
||||||
|
JOSEObjectType.JWT, null);
|
||||||
|
|
||||||
private RSAPublicKey key;
|
private RSAPublicKey key;
|
||||||
|
|
||||||
private Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer;
|
private Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer;
|
||||||
@ -495,6 +565,54 @@ public final class NimbusJwtDecoder implements JwtDecoder {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to use Nimbus's typ header verification. This is {@code true} by
|
||||||
|
* default, however it may change to {@code false} in a future major release.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* By turning off this feature, {@link NimbusJwtDecoder} expects applications to
|
||||||
|
* check the {@code typ} header themselves in order to determine what kind of
|
||||||
|
* validation is needed
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This is done for you when you use {@link JwtValidators} to construct a
|
||||||
|
* validator.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* That means that this: <code>
|
||||||
|
* NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build();
|
||||||
|
* jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(issuer);
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Is equivalent to this: <code>
|
||||||
|
* NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer)
|
||||||
|
* .validateType(false)
|
||||||
|
* .build();
|
||||||
|
* jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(issuer);
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The difference is that by setting this to {@code false}, it allows you to
|
||||||
|
* provide validation by type, like for {@code at+jwt}:
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
* NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer)
|
||||||
|
* .validateType(false)
|
||||||
|
* .build();
|
||||||
|
* jwtDecoder.setJwtValidator(new MyAtJwtValidator());
|
||||||
|
* </code>
|
||||||
|
* @param shouldValidateTypHeader whether Nimbus should validate the typ header or
|
||||||
|
* not
|
||||||
|
* @return a {@link JwkSetUriJwtDecoderBuilder} for further configurations
|
||||||
|
* @since 6.5
|
||||||
|
*/
|
||||||
|
public PublicKeyJwtDecoderBuilder validateType(boolean shouldValidateTypHeader) {
|
||||||
|
this.typeVerifier = shouldValidateTypHeader ? JWT_TYPE_VERIFIER : NO_TYPE_VERIFIER;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use the given signing
|
* Use the given signing
|
||||||
* <a href="https://tools.ietf.org/html/rfc7515#section-4.1.1" target=
|
* <a href="https://tools.ietf.org/html/rfc7515#section-4.1.1" target=
|
||||||
@ -533,6 +651,7 @@ public final class NimbusJwtDecoder implements JwtDecoder {
|
|||||||
+ this.jwsAlgorithm + ". Please indicate one of RS256, RS384, or RS512.");
|
+ this.jwsAlgorithm + ". Please indicate one of RS256, RS384, or RS512.");
|
||||||
JWSKeySelector<SecurityContext> jwsKeySelector = new SingleKeyJWSKeySelector<>(this.jwsAlgorithm, this.key);
|
JWSKeySelector<SecurityContext> jwsKeySelector = new SingleKeyJWSKeySelector<>(this.jwsAlgorithm, this.key);
|
||||||
DefaultJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();
|
DefaultJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();
|
||||||
|
jwtProcessor.setJWSTypeVerifier(this.typeVerifier);
|
||||||
jwtProcessor.setJWSKeySelector(jwsKeySelector);
|
jwtProcessor.setJWSKeySelector(jwsKeySelector);
|
||||||
// Spring Security validates the claim set independent from Nimbus
|
// Spring Security validates the claim set independent from Nimbus
|
||||||
jwtProcessor.setJWTClaimsSetVerifier((claims, context) -> {
|
jwtProcessor.setJWTClaimsSetVerifier((claims, context) -> {
|
||||||
@ -557,10 +676,19 @@ public final class NimbusJwtDecoder implements JwtDecoder {
|
|||||||
*/
|
*/
|
||||||
public static final class SecretKeyJwtDecoderBuilder {
|
public static final class SecretKeyJwtDecoderBuilder {
|
||||||
|
|
||||||
|
private static final JOSEObjectTypeVerifier<SecurityContext> JWT_TYPE_VERIFIER = new DefaultJOSEObjectTypeVerifier<>(
|
||||||
|
JOSEObjectType.JWT, null);
|
||||||
|
|
||||||
|
private static final JOSEObjectTypeVerifier<SecurityContext> NO_TYPE_VERIFIER = (header, context) -> {
|
||||||
|
};
|
||||||
|
|
||||||
private final SecretKey secretKey;
|
private final SecretKey secretKey;
|
||||||
|
|
||||||
private JWSAlgorithm jwsAlgorithm = JWSAlgorithm.HS256;
|
private JWSAlgorithm jwsAlgorithm = JWSAlgorithm.HS256;
|
||||||
|
|
||||||
|
private JOSEObjectTypeVerifier<SecurityContext> typeVerifier = new DefaultJOSEObjectTypeVerifier<>(
|
||||||
|
JOSEObjectType.JWT, null);
|
||||||
|
|
||||||
private Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer;
|
private Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer;
|
||||||
|
|
||||||
private SecretKeyJwtDecoderBuilder(SecretKey secretKey) {
|
private SecretKeyJwtDecoderBuilder(SecretKey secretKey) {
|
||||||
@ -570,6 +698,54 @@ public final class NimbusJwtDecoder implements JwtDecoder {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to use Nimbus's typ header verification. This is {@code true} by
|
||||||
|
* default, however it may change to {@code false} in a future major release.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* By turning off this feature, {@link NimbusJwtDecoder} expects applications to
|
||||||
|
* check the {@code typ} header themselves in order to determine what kind of
|
||||||
|
* validation is needed
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This is done for you when you use {@link JwtValidators} to construct a
|
||||||
|
* validator.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* That means that this: <code>
|
||||||
|
* NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build();
|
||||||
|
* jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(issuer);
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Is equivalent to this: <code>
|
||||||
|
* NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer)
|
||||||
|
* .validateType(false)
|
||||||
|
* .build();
|
||||||
|
* jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(issuer);
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The difference is that by setting this to {@code false}, it allows you to
|
||||||
|
* provide validation by type, like for {@code at+jwt}:
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
* NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer)
|
||||||
|
* .validateType(false)
|
||||||
|
* .build();
|
||||||
|
* jwtDecoder.setJwtValidator(new MyAtJwtValidator());
|
||||||
|
* </code>
|
||||||
|
* @param shouldValidateTypHeader whether Nimbus should validate the typ header or
|
||||||
|
* not
|
||||||
|
* @return a {@link JwkSetUriJwtDecoderBuilder} for further configurations
|
||||||
|
* @since 6.5
|
||||||
|
*/
|
||||||
|
public SecretKeyJwtDecoderBuilder validateType(boolean shouldValidateTypHeader) {
|
||||||
|
this.typeVerifier = shouldValidateTypHeader ? JWT_TYPE_VERIFIER : NO_TYPE_VERIFIER;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use the given
|
* Use the given
|
||||||
* <a href="https://tools.ietf.org/html/rfc7515#section-4.1.1" target=
|
* <a href="https://tools.ietf.org/html/rfc7515#section-4.1.1" target=
|
||||||
@ -615,6 +791,7 @@ public final class NimbusJwtDecoder implements JwtDecoder {
|
|||||||
this.secretKey);
|
this.secretKey);
|
||||||
DefaultJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();
|
DefaultJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();
|
||||||
jwtProcessor.setJWSKeySelector(jwsKeySelector);
|
jwtProcessor.setJWSKeySelector(jwsKeySelector);
|
||||||
|
jwtProcessor.setJWSTypeVerifier(this.typeVerifier);
|
||||||
// Spring Security validates the claim set independent from Nimbus
|
// Spring Security validates the claim set independent from Nimbus
|
||||||
jwtProcessor.setJWTClaimsSetVerifier((claims, context) -> {
|
jwtProcessor.setJWTClaimsSetVerifier((claims, context) -> {
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2025 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.oauth2.jwt;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class JwtTypeValidatorTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void constructorWhenJwtThenRequiresJwtOrEmpty() {
|
||||||
|
Jwt.Builder jwt = TestJwts.jwt();
|
||||||
|
JwtTypeValidator validator = JwtTypeValidator.jwt();
|
||||||
|
assertThat(validator.validate(jwt.build()).hasErrors()).isFalse();
|
||||||
|
jwt.header(JoseHeaderNames.TYP, "JWT");
|
||||||
|
assertThat(validator.validate(jwt.build()).hasErrors()).isFalse();
|
||||||
|
jwt.header(JoseHeaderNames.TYP, "at+jwt");
|
||||||
|
assertThat(validator.validate(jwt.build()).hasErrors()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void constructorWhenCustomThenEnforces() {
|
||||||
|
Jwt.Builder jwt = TestJwts.jwt();
|
||||||
|
JwtTypeValidator validator = new JwtTypeValidator("JOSE");
|
||||||
|
assertThat(validator.validate(jwt.build()).hasErrors()).isTrue();
|
||||||
|
jwt.header(JoseHeaderNames.TYP, "JWT");
|
||||||
|
assertThat(validator.validate(jwt.build()).hasErrors()).isTrue();
|
||||||
|
jwt.header(JoseHeaderNames.TYP, "JOSE");
|
||||||
|
assertThat(validator.validate(jwt.build()).hasErrors()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -832,6 +832,28 @@ public class NimbusJwtDecoderTests {
|
|||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void decodeWhenPublicKeyValidateTypeFalseThenSkipsNimbusTypeValidation() throws Exception {
|
||||||
|
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withPublicKey(TestKeys.DEFAULT_PUBLIC_KEY)
|
||||||
|
.validateType(false)
|
||||||
|
.build();
|
||||||
|
RSAPrivateKey privateKey = TestKeys.DEFAULT_PRIVATE_KEY;
|
||||||
|
SignedJWT jwt = signedJwt(privateKey,
|
||||||
|
new JWSHeader.Builder(JWSAlgorithm.RS256).type(JOSEObjectType.JOSE).build(),
|
||||||
|
new JWTClaimsSet.Builder().subject("subject").build());
|
||||||
|
jwtDecoder.decode(jwt.serialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void decodeWhenSecretKeyValidateTypeFalseThenSkipsNimbusTypeValidation() throws Exception {
|
||||||
|
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withSecretKey(TestKeys.DEFAULT_SECRET_KEY)
|
||||||
|
.validateType(false)
|
||||||
|
.build();
|
||||||
|
SignedJWT jwt = signedJwt(TestKeys.DEFAULT_SECRET_KEY, MacAlgorithm.HS256,
|
||||||
|
new JWTClaimsSet.Builder().subject("subject").build());
|
||||||
|
jwtDecoder.decode(jwt.serialize());
|
||||||
|
}
|
||||||
|
|
||||||
private RSAPublicKey key() throws InvalidKeySpecException {
|
private RSAPublicKey key() throws InvalidKeySpecException {
|
||||||
byte[] decoded = Base64.getDecoder().decode(VERIFY_KEY.getBytes());
|
byte[] decoded = Base64.getDecoder().decode(VERIFY_KEY.getBytes());
|
||||||
EncodedKeySpec spec = new X509EncodedKeySpec(decoded);
|
EncodedKeySpec spec = new X509EncodedKeySpec(decoded);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user