4 changed files with 325 additions and 0 deletions
@ -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")); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -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(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue