From 20aa8bef2577fe3ad3dcdb7103be9e2edff8b20e Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 31 Jul 2020 22:16:50 -0700 Subject: [PATCH] Polish spring-security-oauth2-jose main code Manually polish `spring-security-oauth-jose` following the formatting and checkstyle fixes. Issue gh-8945 --- .../oauth2/jose/jws/MacAlgorithm.java | 18 ++-- .../oauth2/jose/jws/SignatureAlgorithm.java | 18 ++-- .../oauth2/jwt/JwtClaimValidator.java | 9 +- .../JwtDecoderProviderConfigurationUtils.java | 25 +++--- .../security/oauth2/jwt/JwtDecoders.java | 7 +- .../oauth2/jwt/JwtIssuerValidator.java | 3 - .../oauth2/jwt/JwtTimestampValidator.java | 11 +-- .../oauth2/jwt/JwtValidationException.java | 1 - .../security/oauth2/jwt/JwtValidators.java | 6 +- .../jwt/MappedJwtClaimSetConverter.java | 14 +--- .../security/oauth2/jwt/NimbusJwtDecoder.java | 83 +++++++------------ .../jwt/NimbusJwtDecoderJwkSupport.java | 1 - .../oauth2/jwt/NimbusReactiveJwtDecoder.java | 59 +++++-------- .../oauth2/jwt/ReactiveJwtDecoders.java | 7 +- .../oauth2/jwt/ReactiveRemoteJWKSource.java | 9 -- 15 files changed, 96 insertions(+), 175 deletions(-) diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jose/jws/MacAlgorithm.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jose/jws/MacAlgorithm.java index ad75edfa5c..110d21896f 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jose/jws/MacAlgorithm.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jose/jws/MacAlgorithm.java @@ -55,6 +55,15 @@ public enum MacAlgorithm implements JwsAlgorithm { this.name = name; } + /** + * Returns the algorithm name. + * @return the algorithm name + */ + @Override + public String getName() { + return this.name; + } + /** * Attempt to resolve the provided algorithm name to a {@code MacAlgorithm}. * @param name the algorithm name @@ -69,13 +78,4 @@ public enum MacAlgorithm implements JwsAlgorithm { return null; } - /** - * Returns the algorithm name. - * @return the algorithm name - */ - @Override - public String getName() { - return this.name; - } - } diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jose/jws/SignatureAlgorithm.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jose/jws/SignatureAlgorithm.java index 1e6e85c7f7..f27ea53f16 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jose/jws/SignatureAlgorithm.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jose/jws/SignatureAlgorithm.java @@ -85,6 +85,15 @@ public enum SignatureAlgorithm implements JwsAlgorithm { this.name = name; } + /** + * Returns the algorithm name. + * @return the algorithm name + */ + @Override + public String getName() { + return this.name; + } + /** * Attempt to resolve the provided algorithm name to a {@code SignatureAlgorithm}. * @param name the algorithm name @@ -99,13 +108,4 @@ public enum SignatureAlgorithm implements JwsAlgorithm { return null; } - /** - * Returns the algorithm name. - * @return the algorithm name - */ - @Override - public String getName() { - return this.name; - } - } diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtClaimValidator.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtClaimValidator.java index 0e39cf0abd..73c13c7dc2 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtClaimValidator.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtClaimValidator.java @@ -58,9 +58,6 @@ public final class JwtClaimValidator implements OAuth2TokenValidator { "https://tools.ietf.org/html/rfc6750#section-3.1"); } - /** - * {@inheritDoc} - */ @Override public OAuth2TokenValidatorResult validate(Jwt token) { Assert.notNull(token, "token cannot be null"); @@ -68,10 +65,8 @@ public final class JwtClaimValidator implements OAuth2TokenValidator { if (this.test.test(claimValue)) { return OAuth2TokenValidatorResult.success(); } - else { - this.logger.debug(this.error.getDescription()); - return OAuth2TokenValidatorResult.failure(this.error); - } + this.logger.debug(this.error.getDescription()); + return OAuth2TokenValidatorResult.failure(this.error); } } diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtils.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtils.java index e895ad2950..3aba32e867 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtils.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtils.java @@ -23,6 +23,7 @@ import java.util.Map; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; +import org.springframework.util.Assert; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; @@ -46,7 +47,7 @@ final class JwtDecoderProviderConfigurationUtils { private static final RestTemplate rest = new RestTemplate(); - private static final ParameterizedTypeReference> typeReference = new ParameterizedTypeReference>() { + private static final ParameterizedTypeReference> STRING_OBJECT_MAP = new ParameterizedTypeReference>() { }; private JwtDecoderProviderConfigurationUtils() { @@ -62,14 +63,16 @@ final class JwtDecoderProviderConfigurationUtils { } static void validateIssuer(Map configuration, String issuer) { - String metadataIssuer = "(unavailable)"; + String metadataIssuer = getMetadataIssuer(configuration); + Assert.state(issuer.equals(metadataIssuer), () -> "The Issuer \"" + metadataIssuer + + "\" provided in the configuration did not " + "match the requested issuer \"" + issuer + "\""); + } + + private static String getMetadataIssuer(Map configuration) { if (configuration.containsKey("issuer")) { - metadataIssuer = configuration.get("issuer").toString(); - } - if (!issuer.equals(metadataIssuer)) { - throw new IllegalStateException("The Issuer \"" + metadataIssuer - + "\" provided in the configuration did not " + "match the requested issuer \"" + issuer + "\""); + return configuration.get("issuer").toString(); } + return "(unavailable)"; } private static Map getConfiguration(String issuer, URI... uris) { @@ -77,13 +80,9 @@ final class JwtDecoderProviderConfigurationUtils { for (URI uri : uris) { try { RequestEntity request = RequestEntity.get(uri).build(); - ResponseEntity> response = rest.exchange(request, typeReference); + ResponseEntity> response = rest.exchange(request, STRING_OBJECT_MAP); Map configuration = response.getBody(); - - if (configuration.get("jwks_uri") == null) { - throw new IllegalArgumentException("The public JWK set URI must not be null"); - } - + Assert.isTrue(configuration.get("jwks_uri") != null, "The public JWK set URI must not be null"); return configuration; } catch (IllegalArgumentException ex) { diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoders.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoders.java index ad6c9274f1..d109cc0d2d 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoders.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoders.java @@ -34,6 +34,9 @@ import org.springframework.util.Assert; */ public final class JwtDecoders { + private JwtDecoders() { + } + /** * Creates a {@link JwtDecoder} using the provided Issuer @@ -105,11 +108,7 @@ public final class JwtDecoders { OAuth2TokenValidator jwtValidator = JwtValidators.createDefaultWithIssuer(issuer); NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(configuration.get("jwks_uri").toString()).build(); jwtDecoder.setJwtValidator(jwtValidator); - return jwtDecoder; } - private JwtDecoders() { - } - } diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtIssuerValidator.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtIssuerValidator.java index 00b894d86e..61c5d11882 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtIssuerValidator.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtIssuerValidator.java @@ -39,9 +39,6 @@ public final class JwtIssuerValidator implements OAuth2TokenValidator { this.validator = new JwtClaimValidator(JwtClaimNames.ISS, issuer::equals); } - /** - * {@inheritDoc} - */ @Override public OAuth2TokenValidatorResult validate(Jwt token) { Assert.notNull(token, "token cannot be null"); diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtTimestampValidator.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtTimestampValidator.java index 5685e30793..0fb72aca00 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtTimestampValidator.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtTimestampValidator.java @@ -68,31 +68,23 @@ public final class JwtTimestampValidator implements OAuth2TokenValidator { this.clockSkew = clockSkew; } - /** - * {@inheritDoc} - */ @Override public OAuth2TokenValidatorResult validate(Jwt jwt) { Assert.notNull(jwt, "jwt cannot be null"); - Instant expiry = jwt.getExpiresAt(); - if (expiry != null) { if (Instant.now(this.clock).minus(this.clockSkew).isAfter(expiry)) { OAuth2Error oAuth2Error = createOAuth2Error(String.format("Jwt expired at %s", jwt.getExpiresAt())); return OAuth2TokenValidatorResult.failure(oAuth2Error); } } - Instant notBefore = jwt.getNotBefore(); - if (notBefore != null) { if (Instant.now(this.clock).plus(this.clockSkew).isBefore(notBefore)) { OAuth2Error oAuth2Error = createOAuth2Error(String.format("Jwt used before %s", jwt.getNotBefore())); return OAuth2TokenValidatorResult.failure(oAuth2Error); } } - return OAuth2TokenValidatorResult.success(); } @@ -103,8 +95,7 @@ public final class JwtTimestampValidator implements OAuth2TokenValidator { } /** - * ' Use this {@link Clock} with {@link Instant#now()} for assessing timestamp - * validity + * Use this {@link Clock} with {@link Instant#now()} for assessing timestamp validity * @param clock */ public void setClock(Clock clock) { diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtValidationException.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtValidationException.java index d0d2dde0b3..94568d2dc6 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtValidationException.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtValidationException.java @@ -54,7 +54,6 @@ public class JwtValidationException extends BadJwtException { */ public JwtValidationException(String message, Collection errors) { super(message); - Assert.notEmpty(errors, "errors cannot be empty"); this.errors = new ArrayList<>(errors); } diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtValidators.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtValidators.java index ca49e7bae9..4d13ce52ab 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtValidators.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtValidators.java @@ -32,6 +32,9 @@ import org.springframework.security.oauth2.core.OAuth2TokenValidator; */ public final class JwtValidators { + private JwtValidators() { + } + /** *

* Create a {@link Jwt} Validator that contains all standard validators when an issuer @@ -69,7 +72,4 @@ public final class JwtValidators { return new DelegatingOAuth2TokenValidator<>(Arrays.asList(new JwtTimestampValidator())); } - private JwtValidators() { - } - } diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/MappedJwtClaimSetConverter.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/MappedJwtClaimSetConverter.java index d5c6c23670..221c6d730c 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/MappedJwtClaimSetConverter.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/MappedJwtClaimSetConverter.java @@ -93,11 +93,9 @@ public final class MappedJwtClaimSetConverter implements Converter> claimTypeConverters) { Assert.notNull(claimTypeConverters, "claimTypeConverters cannot be null"); - Converter stringConverter = getConverter(STRING_TYPE_DESCRIPTOR); Converter collectionStringConverter = getConverter( TypeDescriptor.collection(Collection.class, STRING_TYPE_DESCRIPTOR)); - Map> claimNameToConverter = new HashMap<>(); claimNameToConverter.put(JwtClaimNames.AUD, collectionStringConverter); claimNameToConverter.put(JwtClaimNames.EXP, MappedJwtClaimSetConverter::convertInstant); @@ -107,7 +105,6 @@ public final class MappedJwtClaimSetConverter implements Converter "Could not coerce " + source + " into an Instant"); return result; } @@ -145,24 +140,17 @@ public final class MappedJwtClaimSetConverter implements Converter convert(Map claims) { Assert.notNull(claims, "claims cannot be null"); - Map mappedClaims = this.delegate.convert(claims); - mappedClaims = removeClaims(mappedClaims); mappedClaims = addClaims(mappedClaims); - Instant issuedAt = (Instant) mappedClaims.get(JwtClaimNames.IAT); Instant expiresAt = (Instant) mappedClaims.get(JwtClaimNames.EXP); if (issuedAt == null && expiresAt != null) { mappedClaims.put(JwtClaimNames.IAT, expiresAt.minusSeconds(1)); } - return mappedClaims; } diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoder.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoder.java index aa76a21151..632d755cc7 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoder.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoder.java @@ -145,19 +145,15 @@ public final class NimbusJwtDecoder implements JwtDecoder { try { // Verify the signature JWTClaimsSet jwtClaimsSet = this.jwtProcessor.process(parsedJwt, null); - Map headers = new LinkedHashMap<>(parsedJwt.getHeader().toJSONObject()); Map claims = this.claimSetConverter.convert(jwtClaimsSet.getClaims()); - return Jwt.withTokenValue(token).headers((h) -> h.putAll(headers)).claims((c) -> c.putAll(claims)).build(); } catch (RemoteKeySourceException ex) { if (ex.getCause() instanceof ParseException) { throw new JwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, "Malformed Jwk set")); } - else { - throw new JwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, ex.getMessage()), ex); - } + throw new JwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, ex.getMessage()), ex); } catch (JOSEException ex) { throw new JwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, ex.getMessage()), ex); @@ -166,9 +162,7 @@ public final class NimbusJwtDecoder implements JwtDecoder { if (ex.getCause() instanceof ParseException) { throw new BadJwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, "Malformed payload")); } - else { - throw new BadJwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, ex.getMessage()), ex); - } + throw new BadJwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, ex.getMessage()), ex); } } @@ -176,20 +170,21 @@ public final class NimbusJwtDecoder implements JwtDecoder { OAuth2TokenValidatorResult result = this.jwtValidator.validate(jwt); if (result.hasErrors()) { Collection errors = result.getErrors(); - String validationErrorString = "Unable to validate Jwt"; - for (OAuth2Error oAuth2Error : errors) { - if (!StringUtils.isEmpty(oAuth2Error.getDescription())) { - validationErrorString = String.format(DECODING_ERROR_MESSAGE_TEMPLATE, - oAuth2Error.getDescription()); - break; - } - } - throw new JwtValidationException(validationErrorString, result.getErrors()); + String validationErrorString = getJwtValidationExceptionMessage(errors); + throw new JwtValidationException(validationErrorString, errors); } - return jwt; } + private String getJwtValidationExceptionMessage(Collection errors) { + for (OAuth2Error oAuth2Error : errors) { + if (!StringUtils.isEmpty(oAuth2Error.getDescription())) { + return String.format(DECODING_ERROR_MESSAGE_TEMPLATE, oAuth2Error.getDescription()); + } + } + return "Unable to validate Jwt"; + } + /** * Use the given JWK Set * uri. @@ -316,14 +311,12 @@ public final class NimbusJwtDecoder implements JwtDecoder { if (this.signatureAlgorithms.isEmpty()) { return new JWSVerificationKeySelector<>(JWSAlgorithm.RS256, jwkSource); } - else { - Set jwsAlgorithms = new HashSet<>(); - for (SignatureAlgorithm signatureAlgorithm : this.signatureAlgorithms) { - JWSAlgorithm jwsAlgorithm = JWSAlgorithm.parse(signatureAlgorithm.getName()); - jwsAlgorithms.add(jwsAlgorithm); - } - return new JWSVerificationKeySelector<>(jwsAlgorithms, jwkSource); + Set jwsAlgorithms = new HashSet<>(); + for (SignatureAlgorithm signatureAlgorithm : this.signatureAlgorithms) { + JWSAlgorithm jwsAlgorithm = JWSAlgorithm.parse(signatureAlgorithm.getName()); + jwsAlgorithms.add(jwsAlgorithm); } + return new JWSVerificationKeySelector<>(jwsAlgorithms, jwkSource); } JWKSource jwkSource(ResourceRetriever jwkSetRetriever) { @@ -339,13 +332,10 @@ public final class NimbusJwtDecoder implements JwtDecoder { JWKSource jwkSource = jwkSource(jwkSetRetriever); ConfigurableJWTProcessor jwtProcessor = new DefaultJWTProcessor<>(); jwtProcessor.setJWSKeySelector(jwsKeySelector(jwkSource)); - // Spring Security validates the claim set independent from Nimbus jwtProcessor.setJWTClaimsSetVerifier((claims, context) -> { }); - this.jwtProcessorCustomizer.accept(jwtProcessor); - return jwtProcessor; } @@ -397,10 +387,10 @@ public final class NimbusJwtDecoder implements JwtDecoder { @Override public Resource retrieveResource(URL url) throws IOException { - String jwkSet; try { - jwkSet = this.cache.get(url.toString(), + String jwkSet = this.cache.get(url.toString(), () -> this.resourceRetriever.retrieveResource(url).getContent()); + return new Resource(jwkSet, "UTF-8"); } catch (Cache.ValueRetrievalException ex) { Throwable thrownByValueLoader = ex.getCause(); @@ -412,8 +402,6 @@ public final class NimbusJwtDecoder implements JwtDecoder { catch (Exception ex) { throw new IOException(ex); } - - return new Resource(jwkSet, "UTF-8"); } } @@ -433,21 +421,21 @@ public final class NimbusJwtDecoder implements JwtDecoder { public Resource retrieveResource(URL url) throws IOException { HttpHeaders headers = new HttpHeaders(); headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON, APPLICATION_JWK_SET_JSON)); + ResponseEntity response = getResponse(url, headers); + if (response.getStatusCodeValue() != 200) { + throw new IOException(response.toString()); + } + return new Resource(response.getBody(), "UTF-8"); + } - ResponseEntity response; + private ResponseEntity getResponse(URL url, HttpHeaders headers) throws IOException { try { RequestEntity request = new RequestEntity<>(headers, HttpMethod.GET, url.toURI()); - response = this.restOperations.exchange(request, String.class); + return this.restOperations.exchange(request, String.class); } catch (Exception ex) { throw new IOException(ex); } - - if (response.getStatusCodeValue() != 200) { - throw new IOException(response.toString()); - } - - return new Resource(response.getBody(), "UTF-8"); } } @@ -506,22 +494,16 @@ public final class NimbusJwtDecoder implements JwtDecoder { } JWTProcessor processor() { - if (!JWSAlgorithm.Family.RSA.contains(this.jwsAlgorithm)) { - throw new IllegalStateException( - "The provided key is of type RSA; " + "however the signature algorithm is of some other type: " - + this.jwsAlgorithm + ". Please indicate one of RS256, RS384, or RS512."); - } - + Assert.state(JWSAlgorithm.Family.RSA.contains(this.jwsAlgorithm), + () -> "The provided key is of type RSA; however the signature algorithm is of some other type: " + + this.jwsAlgorithm + ". Please indicate one of RS256, RS384, or RS512."); JWSKeySelector jwsKeySelector = new SingleKeyJWSKeySelector<>(this.jwsAlgorithm, this.key); DefaultJWTProcessor jwtProcessor = new DefaultJWTProcessor<>(); jwtProcessor.setJWSKeySelector(jwsKeySelector); - // Spring Security validates the claim set independent from Nimbus jwtProcessor.setJWTClaimsSetVerifier((claims, context) -> { }); - this.jwtProcessorCustomizer.accept(jwtProcessor); - return jwtProcessor; } @@ -599,13 +581,10 @@ public final class NimbusJwtDecoder implements JwtDecoder { this.secretKey); DefaultJWTProcessor jwtProcessor = new DefaultJWTProcessor<>(); jwtProcessor.setJWSKeySelector(jwsKeySelector); - // Spring Security validates the claim set independent from Nimbus jwtProcessor.setJWTClaimsSetVerifier((claims, context) -> { }); - this.jwtProcessorCustomizer.accept(jwtProcessor); - return jwtProcessor; } diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoderJwkSupport.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoderJwkSupport.java index 5fa2de4f65..cb3e8a7b6f 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoderJwkSupport.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoderJwkSupport.java @@ -79,7 +79,6 @@ public final class NimbusJwtDecoderJwkSupport implements JwtDecoder { public NimbusJwtDecoderJwkSupport(String jwkSetUrl, String jwsAlgorithm) { Assert.hasText(jwkSetUrl, "jwkSetUrl cannot be empty"); Assert.hasText(jwsAlgorithm, "jwsAlgorithm cannot be empty"); - this.jwtDecoderBuilder = NimbusJwtDecoder.withJwkSetUri(jwkSetUrl) .jwsAlgorithm(SignatureAlgorithm.from(jwsAlgorithm)); this.delegate = makeDelegate(); diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoder.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoder.java index fa457aa1e2..95909265ac 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoder.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoder.java @@ -161,8 +161,8 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder { try { return this.jwtProcessor.convert(parsedToken).map((set) -> createJwt(parsedToken, set)) .map(this::validateJwt) - .onErrorMap((e) -> !(e instanceof IllegalStateException) && !(e instanceof JwtException), - (e) -> new JwtException("An error occurred while attempting to decode the Jwt: ", e)); + .onErrorMap((ex) -> !(ex instanceof IllegalStateException) && !(ex instanceof JwtException), + (ex) -> new JwtException("An error occurred while attempting to decode the Jwt: ", ex)); } catch (JwtException ex) { throw ex; @@ -176,7 +176,6 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder { try { Map headers = new LinkedHashMap<>(parsedJwt.getHeader().toJSONObject()); Map claims = this.claimSetConverter.convert(jwtClaimsSet.getClaims()); - return Jwt.withTokenValue(parsedJwt.getParsedString()).headers((h) -> h.putAll(headers)) .claims((c) -> c.putAll(claims)).build(); } @@ -189,19 +188,21 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder { OAuth2TokenValidatorResult result = this.jwtValidator.validate(jwt); if (result.hasErrors()) { Collection errors = result.getErrors(); - String validationErrorString = "Unable to validate Jwt"; - for (OAuth2Error oAuth2Error : errors) { - if (!StringUtils.isEmpty(oAuth2Error.getDescription())) { - validationErrorString = oAuth2Error.getDescription(); - break; - } - } + String validationErrorString = getJwtValidationExceptionMessage(errors); throw new JwtValidationException(validationErrorString, errors); } - return jwt; } + private String getJwtValidationExceptionMessage(Collection errors) { + for (OAuth2Error oAuth2Error : errors) { + if (!StringUtils.isEmpty(oAuth2Error.getDescription())) { + return oAuth2Error.getDescription(); + } + } + return "Unable to validate Jwt"; + } + /** * Use the given JWK Set * uri to validate JWTs. @@ -353,14 +354,12 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder { if (this.signatureAlgorithms.isEmpty()) { return new JWSVerificationKeySelector<>(JWSAlgorithm.RS256, jwkSource); } - else { - Set jwsAlgorithms = new HashSet<>(); - for (SignatureAlgorithm signatureAlgorithm : this.signatureAlgorithms) { - JWSAlgorithm jwsAlgorithm = JWSAlgorithm.parse(signatureAlgorithm.getName()); - jwsAlgorithms.add(jwsAlgorithm); - } - return new JWSVerificationKeySelector<>(jwsAlgorithms, jwkSource); + Set jwsAlgorithms = new HashSet<>(); + for (SignatureAlgorithm signatureAlgorithm : this.signatureAlgorithms) { + JWSAlgorithm jwsAlgorithm = JWSAlgorithm.parse(signatureAlgorithm.getName()); + jwsAlgorithms.add(jwsAlgorithm); } + return new JWSVerificationKeySelector<>(jwsAlgorithms, jwkSource); } Converter> processor() { @@ -370,16 +369,14 @@ 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); - Function expectedJwsAlgorithms = getExpectedJwsAlgorithms(jwsKeySelector); return (jwt) -> { JWKSelector selector = createSelector(expectedJwsAlgorithms, jwt.getHeader()); - return source.get(selector).onErrorMap((e) -> new IllegalStateException("Could not obtain the keys", e)) + return source.get(selector) + .onErrorMap((ex) -> new IllegalStateException("Could not obtain the keys", ex)) .map((jwkList) -> createClaimsSet(jwtProcessor, jwt, new JWKSecurityContext(jwkList))); }; } @@ -396,7 +393,6 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder { if (!expectedJwsAlgorithms.apply(jwsHeader.getAlgorithm())) { throw new BadJwtException("Unsupported algorithm of " + header.getAlgorithm()); } - return new JWKSelector(JWKMatcher.forJWSHeader(jwsHeader)); } @@ -463,22 +459,16 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder { } Converter> processor() { - if (!JWSAlgorithm.Family.RSA.contains(this.jwsAlgorithm)) { - throw new IllegalStateException( - "The provided key is of type RSA; " + "however the signature algorithm is of some other type: " - + this.jwsAlgorithm + ". Please indicate one of RS256, RS384, or RS512."); - } - + Assert.state(JWSAlgorithm.Family.RSA.contains(this.jwsAlgorithm), + () -> "The provided key is of type RSA; however the signature algorithm is of some other type: " + + this.jwsAlgorithm + ". Please indicate one of RS256, RS384, or RS512."); JWSKeySelector jwsKeySelector = new SingleKeyJWSKeySelector<>(this.jwsAlgorithm, this.key); DefaultJWTProcessor jwtProcessor = new DefaultJWTProcessor<>(); jwtProcessor.setJWSKeySelector(jwsKeySelector); - // 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)); } @@ -550,13 +540,10 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder { this.secretKey); DefaultJWTProcessor jwtProcessor = new DefaultJWTProcessor<>(); jwtProcessor.setJWSKeySelector(jwsKeySelector); - // 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)); } @@ -626,9 +613,7 @@ 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) diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoders.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoders.java index ef5102d01c..c279cc5819 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoders.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoders.java @@ -33,6 +33,9 @@ import org.springframework.util.Assert; */ public final class ReactiveJwtDecoders { + private ReactiveJwtDecoders() { + } + /** * Creates a {@link ReactiveJwtDecoder} using the provided Issuer @@ -106,11 +109,7 @@ public final class ReactiveJwtDecoders { NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder .withJwkSetUri(configuration.get("jwks_uri").toString()).build(); jwtDecoder.setJwtValidator(jwtValidator); - return jwtDecoder; } - private ReactiveJwtDecoders() { - } - } diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveRemoteJWKSource.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveRemoteJWKSource.java index 28b4640b68..ec2bc3f901 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveRemoteJWKSource.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveRemoteJWKSource.java @@ -63,29 +63,23 @@ class ReactiveRemoteJWKSource implements ReactiveJWKSource { return Mono.defer(() -> { // Run the selector on the JWK set List matches = jwkSelector.select(jwkSet); - if (!matches.isEmpty()) { // Success return Mono.just(matches); } - // Refresh the JWK set if the sought key ID is not in the cached JWK set - // Looking for JWK with specific ID? String soughtKeyID = getFirstSpecifiedKeyID(jwkSelector.getMatcher()); if (soughtKeyID == null) { // No key ID specified, return no matches return Mono.just(Collections.emptyList()); } - if (jwkSet.getKeyByKeyId(soughtKeyID) != null) { // The key ID exists in the cached JWK set, matching // failed for some other reason, return no matches return Mono.just(Collections.emptyList()); } - return Mono.empty(); - }); } @@ -114,13 +108,10 @@ class ReactiveRemoteJWKSource implements ReactiveJWKSource { * @return The first key ID, {@code null} if none. */ protected static String getFirstSpecifiedKeyID(final JWKMatcher jwkMatcher) { - Set keyIDs = jwkMatcher.getKeyIDs(); - if (keyIDs == null || keyIDs.isEmpty()) { return null; } - for (String id : keyIDs) { if (id != null) { return id;