parent
							
								
									fc6b66fdb3
								
							
						
					
					
						commit
						bed3371b80
					
				| 
						 | 
				
			
			@ -15,15 +15,14 @@
 | 
			
		|||
 */
 | 
			
		||||
package org.springframework.security.oauth2.client.oidc.authentication;
 | 
			
		||||
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
 | 
			
		||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
 | 
			
		||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
 | 
			
		||||
import org.springframework.security.oauth2.core.OAuth2Error;
 | 
			
		||||
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
 | 
			
		||||
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.JwsAlgorithm;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
 | 
			
		||||
import org.springframework.security.oauth2.jwt.Jwt;
 | 
			
		||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
 | 
			
		||||
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
 | 
			
		||||
| 
						 | 
				
			
			@ -31,7 +30,15 @@ import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
 | 
			
		|||
import org.springframework.util.Assert;
 | 
			
		||||
import org.springframework.util.StringUtils;
 | 
			
		||||
 | 
			
		||||
import javax.crypto.spec.SecretKeySpec;
 | 
			
		||||
import java.nio.charset.StandardCharsets;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
 | 
			
		||||
import static org.springframework.security.oauth2.jwt.NimbusJwtDecoder.withJwkSetUri;
 | 
			
		||||
import static org.springframework.security.oauth2.jwt.NimbusJwtDecoder.withSecretKey;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A {@link JwtDecoderFactory factory} that provides a {@link JwtDecoder}
 | 
			
		||||
| 
						 | 
				
			
			@ -47,14 +54,45 @@ import static org.springframework.security.oauth2.jwt.NimbusJwtDecoder.withJwkSe
 | 
			
		|||
 */
 | 
			
		||||
public final class OidcIdTokenDecoderFactory implements JwtDecoderFactory<ClientRegistration> {
 | 
			
		||||
	private static final String MISSING_SIGNATURE_VERIFIER_ERROR_CODE = "missing_signature_verifier";
 | 
			
		||||
	private static Map<JwsAlgorithm, String> jcaAlgorithmMappings = new HashMap<JwsAlgorithm, String>() {
 | 
			
		||||
		{
 | 
			
		||||
			put(MacAlgorithm.HS256, "HmacSHA256");
 | 
			
		||||
			put(MacAlgorithm.HS384, "HmacSHA384");
 | 
			
		||||
			put(MacAlgorithm.HS512, "HmacSHA512");
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
	private final Map<String, JwtDecoder> jwtDecoders = new ConcurrentHashMap<>();
 | 
			
		||||
	private Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidatorFactory = OidcIdTokenValidator::new;
 | 
			
		||||
	private Function<ClientRegistration, JwsAlgorithm> jwsAlgorithmResolver = clientRegistration -> SignatureAlgorithm.RS256;
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public JwtDecoder createDecoder(ClientRegistration clientRegistration) {
 | 
			
		||||
		Assert.notNull(clientRegistration, "clientRegistration cannot be null");
 | 
			
		||||
		return this.jwtDecoders.computeIfAbsent(clientRegistration.getRegistrationId(), key -> {
 | 
			
		||||
			if (!StringUtils.hasText(clientRegistration.getProviderDetails().getJwkSetUri())) {
 | 
			
		||||
			NimbusJwtDecoder jwtDecoder = buildDecoder(clientRegistration);
 | 
			
		||||
			OAuth2TokenValidator<Jwt> jwtValidator = this.jwtValidatorFactory.apply(clientRegistration);
 | 
			
		||||
			jwtDecoder.setJwtValidator(jwtValidator);
 | 
			
		||||
			return jwtDecoder;
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private NimbusJwtDecoder buildDecoder(ClientRegistration clientRegistration) {
 | 
			
		||||
		JwsAlgorithm jwsAlgorithm = this.jwsAlgorithmResolver.apply(clientRegistration);
 | 
			
		||||
		if (jwsAlgorithm != null && SignatureAlgorithm.class.isAssignableFrom(jwsAlgorithm.getClass())) {
 | 
			
		||||
			// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
 | 
			
		||||
			//
 | 
			
		||||
			// 6. If the ID Token is received via direct communication between the Client
 | 
			
		||||
			// and the Token Endpoint (which it is in this flow),
 | 
			
		||||
			// the TLS server validation MAY be used to validate the issuer in place of checking the token signature.
 | 
			
		||||
			// The Client MUST validate the signature of all other ID Tokens according to JWS [JWS]
 | 
			
		||||
			// using the algorithm specified in the JWT alg Header Parameter.
 | 
			
		||||
			// The Client MUST use the keys provided by the Issuer.
 | 
			
		||||
			//
 | 
			
		||||
			// 7. The alg value SHOULD be the default of RS256 or the algorithm sent by the Client
 | 
			
		||||
			// in the id_token_signed_response_alg parameter during Registration.
 | 
			
		||||
 | 
			
		||||
			String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();
 | 
			
		||||
			if (!StringUtils.hasText(jwkSetUri)) {
 | 
			
		||||
				OAuth2Error oauth2Error = new OAuth2Error(
 | 
			
		||||
						MISSING_SIGNATURE_VERIFIER_ERROR_CODE,
 | 
			
		||||
						"Failed to find a Signature Verifier for Client Registration: '" +
 | 
			
		||||
| 
						 | 
				
			
			@ -64,12 +102,42 @@ public final class OidcIdTokenDecoderFactory implements JwtDecoderFactory<Client
 | 
			
		|||
				);
 | 
			
		||||
				throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
 | 
			
		||||
			}
 | 
			
		||||
			String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();
 | 
			
		||||
			NimbusJwtDecoder jwtDecoder = withJwkSetUri(jwkSetUri).build();
 | 
			
		||||
			OAuth2TokenValidator<Jwt> jwtValidator = this.jwtValidatorFactory.apply(clientRegistration);
 | 
			
		||||
			jwtDecoder.setJwtValidator(jwtValidator);
 | 
			
		||||
			return jwtDecoder;
 | 
			
		||||
		});
 | 
			
		||||
			return withJwkSetUri(jwkSetUri).jwsAlgorithm(jwsAlgorithm).build();
 | 
			
		||||
		} else if (jwsAlgorithm != null && MacAlgorithm.class.isAssignableFrom(jwsAlgorithm.getClass())) {
 | 
			
		||||
			// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
 | 
			
		||||
			//
 | 
			
		||||
			// 8. If the JWT alg Header Parameter uses a MAC based algorithm such as HS256, HS384, or HS512,
 | 
			
		||||
			// the octets of the UTF-8 representation of the client_secret
 | 
			
		||||
			// corresponding to the client_id contained in the aud (audience) Claim
 | 
			
		||||
			// are used as the key to validate the signature.
 | 
			
		||||
			// For MAC based algorithms, the behavior is unspecified if the aud is multi-valued or
 | 
			
		||||
			// if an azp value is present that is different than the aud value.
 | 
			
		||||
 | 
			
		||||
			String clientSecret = clientRegistration.getClientSecret();
 | 
			
		||||
			if (!StringUtils.hasText(clientSecret)) {
 | 
			
		||||
				OAuth2Error oauth2Error = new OAuth2Error(
 | 
			
		||||
						MISSING_SIGNATURE_VERIFIER_ERROR_CODE,
 | 
			
		||||
						"Failed to find a Signature Verifier for Client Registration: '" +
 | 
			
		||||
								clientRegistration.getRegistrationId() +
 | 
			
		||||
								"'. Check to ensure you have configured the client secret.",
 | 
			
		||||
						null
 | 
			
		||||
				);
 | 
			
		||||
				throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
 | 
			
		||||
			}
 | 
			
		||||
			SecretKeySpec secretKeySpec = new SecretKeySpec(
 | 
			
		||||
					clientSecret.getBytes(StandardCharsets.UTF_8), jcaAlgorithmMappings.get(jwsAlgorithm));
 | 
			
		||||
			return withSecretKey(secretKeySpec).macAlgorithm((MacAlgorithm) jwsAlgorithm).build();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		OAuth2Error oauth2Error = new OAuth2Error(
 | 
			
		||||
				MISSING_SIGNATURE_VERIFIER_ERROR_CODE,
 | 
			
		||||
				"Failed to find a Signature Verifier for Client Registration: '" +
 | 
			
		||||
						clientRegistration.getRegistrationId() +
 | 
			
		||||
						"'. Check to ensure you have configured a valid JWS Algorithm: '" +
 | 
			
		||||
						jwsAlgorithm + "'",
 | 
			
		||||
				null
 | 
			
		||||
		);
 | 
			
		||||
		throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -82,4 +150,17 @@ public final class OidcIdTokenDecoderFactory implements JwtDecoderFactory<Client
 | 
			
		|||
		Assert.notNull(jwtValidatorFactory, "jwtValidatorFactory cannot be null");
 | 
			
		||||
		this.jwtValidatorFactory = jwtValidatorFactory;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Sets the resolver that provides the expected {@link JwsAlgorithm JWS algorithm}
 | 
			
		||||
	 * used for the signature or MAC on the {@link OidcIdToken ID Token}.
 | 
			
		||||
	 * The default resolves to {@link SignatureAlgorithm#RS256 RS256} for all {@link ClientRegistration clients}.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param jwsAlgorithmResolver the resolver that provides the expected {@link JwsAlgorithm JWS algorithm}
 | 
			
		||||
	 *                             for a specific {@link ClientRegistration client}
 | 
			
		||||
	 */
 | 
			
		||||
	public final void setJwsAlgorithmResolver(Function<ClientRegistration, JwsAlgorithm> jwsAlgorithmResolver) {
 | 
			
		||||
		Assert.notNull(jwsAlgorithmResolver, "jwsAlgorithmResolver cannot be null");
 | 
			
		||||
		this.jwsAlgorithmResolver = jwsAlgorithmResolver;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,6 +20,9 @@ import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
 | 
			
		|||
import org.springframework.security.oauth2.core.OAuth2Error;
 | 
			
		||||
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
 | 
			
		||||
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.JwsAlgorithm;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
 | 
			
		||||
import org.springframework.security.oauth2.jwt.Jwt;
 | 
			
		||||
import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;
 | 
			
		||||
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
 | 
			
		||||
| 
						 | 
				
			
			@ -27,10 +30,16 @@ import org.springframework.security.oauth2.jwt.ReactiveJwtDecoderFactory;
 | 
			
		|||
import org.springframework.util.Assert;
 | 
			
		||||
import org.springframework.util.StringUtils;
 | 
			
		||||
 | 
			
		||||
import javax.crypto.spec.SecretKeySpec;
 | 
			
		||||
import java.nio.charset.StandardCharsets;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
 | 
			
		||||
import static org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder.withJwkSetUri;
 | 
			
		||||
import static org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder.withSecretKey;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A {@link ReactiveJwtDecoderFactory factory} that provides a {@link ReactiveJwtDecoder}
 | 
			
		||||
 * used for {@link OidcIdToken} signature verification.
 | 
			
		||||
| 
						 | 
				
			
			@ -45,14 +54,45 @@ import java.util.function.Function;
 | 
			
		|||
 */
 | 
			
		||||
public final class ReactiveOidcIdTokenDecoderFactory implements ReactiveJwtDecoderFactory<ClientRegistration> {
 | 
			
		||||
	private static final String MISSING_SIGNATURE_VERIFIER_ERROR_CODE = "missing_signature_verifier";
 | 
			
		||||
	private static Map<JwsAlgorithm, String> jcaAlgorithmMappings = new HashMap<JwsAlgorithm, String>() {
 | 
			
		||||
		{
 | 
			
		||||
			put(MacAlgorithm.HS256, "HmacSHA256");
 | 
			
		||||
			put(MacAlgorithm.HS384, "HmacSHA384");
 | 
			
		||||
			put(MacAlgorithm.HS512, "HmacSHA512");
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
	private final Map<String, ReactiveJwtDecoder> jwtDecoders = new ConcurrentHashMap<>();
 | 
			
		||||
	private Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidatorFactory = OidcIdTokenValidator::new;
 | 
			
		||||
	private Function<ClientRegistration, JwsAlgorithm> jwsAlgorithmResolver = clientRegistration -> SignatureAlgorithm.RS256;
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public ReactiveJwtDecoder createDecoder(ClientRegistration clientRegistration) {
 | 
			
		||||
		Assert.notNull(clientRegistration, "clientRegistration cannot be null");
 | 
			
		||||
		return this.jwtDecoders.computeIfAbsent(clientRegistration.getRegistrationId(), key -> {
 | 
			
		||||
			if (!StringUtils.hasText(clientRegistration.getProviderDetails().getJwkSetUri())) {
 | 
			
		||||
			NimbusReactiveJwtDecoder jwtDecoder = buildDecoder(clientRegistration);
 | 
			
		||||
			OAuth2TokenValidator<Jwt> jwtValidator = this.jwtValidatorFactory.apply(clientRegistration);
 | 
			
		||||
			jwtDecoder.setJwtValidator(jwtValidator);
 | 
			
		||||
			return jwtDecoder;
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private NimbusReactiveJwtDecoder buildDecoder(ClientRegistration clientRegistration) {
 | 
			
		||||
		JwsAlgorithm jwsAlgorithm = this.jwsAlgorithmResolver.apply(clientRegistration);
 | 
			
		||||
		if (jwsAlgorithm != null && SignatureAlgorithm.class.isAssignableFrom(jwsAlgorithm.getClass())) {
 | 
			
		||||
			// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
 | 
			
		||||
			//
 | 
			
		||||
			// 6. If the ID Token is received via direct communication between the Client
 | 
			
		||||
			// and the Token Endpoint (which it is in this flow),
 | 
			
		||||
			// the TLS server validation MAY be used to validate the issuer in place of checking the token signature.
 | 
			
		||||
			// The Client MUST validate the signature of all other ID Tokens according to JWS [JWS]
 | 
			
		||||
			// using the algorithm specified in the JWT alg Header Parameter.
 | 
			
		||||
			// The Client MUST use the keys provided by the Issuer.
 | 
			
		||||
			//
 | 
			
		||||
			// 7. The alg value SHOULD be the default of RS256 or the algorithm sent by the Client
 | 
			
		||||
			// in the id_token_signed_response_alg parameter during Registration.
 | 
			
		||||
 | 
			
		||||
			String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();
 | 
			
		||||
			if (!StringUtils.hasText(jwkSetUri)) {
 | 
			
		||||
				OAuth2Error oauth2Error = new OAuth2Error(
 | 
			
		||||
						MISSING_SIGNATURE_VERIFIER_ERROR_CODE,
 | 
			
		||||
						"Failed to find a Signature Verifier for Client Registration: '" +
 | 
			
		||||
| 
						 | 
				
			
			@ -62,12 +102,42 @@ public final class ReactiveOidcIdTokenDecoderFactory implements ReactiveJwtDecod
 | 
			
		|||
				);
 | 
			
		||||
				throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
 | 
			
		||||
			}
 | 
			
		||||
			NimbusReactiveJwtDecoder jwtDecoder = new NimbusReactiveJwtDecoder(
 | 
			
		||||
					clientRegistration.getProviderDetails().getJwkSetUri());
 | 
			
		||||
			OAuth2TokenValidator<Jwt> jwtValidator = this.jwtValidatorFactory.apply(clientRegistration);
 | 
			
		||||
			jwtDecoder.setJwtValidator(jwtValidator);
 | 
			
		||||
			return jwtDecoder;
 | 
			
		||||
		});
 | 
			
		||||
			return withJwkSetUri(jwkSetUri).jwsAlgorithm(jwsAlgorithm).build();
 | 
			
		||||
		} else if (jwsAlgorithm != null && MacAlgorithm.class.isAssignableFrom(jwsAlgorithm.getClass())) {
 | 
			
		||||
			// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
 | 
			
		||||
			//
 | 
			
		||||
			// 8. If the JWT alg Header Parameter uses a MAC based algorithm such as HS256, HS384, or HS512,
 | 
			
		||||
			// the octets of the UTF-8 representation of the client_secret
 | 
			
		||||
			// corresponding to the client_id contained in the aud (audience) Claim
 | 
			
		||||
			// are used as the key to validate the signature.
 | 
			
		||||
			// For MAC based algorithms, the behavior is unspecified if the aud is multi-valued or
 | 
			
		||||
			// if an azp value is present that is different than the aud value.
 | 
			
		||||
 | 
			
		||||
			String clientSecret = clientRegistration.getClientSecret();
 | 
			
		||||
			if (!StringUtils.hasText(clientSecret)) {
 | 
			
		||||
				OAuth2Error oauth2Error = new OAuth2Error(
 | 
			
		||||
						MISSING_SIGNATURE_VERIFIER_ERROR_CODE,
 | 
			
		||||
						"Failed to find a Signature Verifier for Client Registration: '" +
 | 
			
		||||
								clientRegistration.getRegistrationId() +
 | 
			
		||||
								"'. Check to ensure you have configured the client secret.",
 | 
			
		||||
						null
 | 
			
		||||
				);
 | 
			
		||||
				throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
 | 
			
		||||
			}
 | 
			
		||||
			SecretKeySpec secretKeySpec = new SecretKeySpec(
 | 
			
		||||
					clientSecret.getBytes(StandardCharsets.UTF_8), jcaAlgorithmMappings.get(jwsAlgorithm));
 | 
			
		||||
			return withSecretKey(secretKeySpec).macAlgorithm((MacAlgorithm) jwsAlgorithm).build();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		OAuth2Error oauth2Error = new OAuth2Error(
 | 
			
		||||
				MISSING_SIGNATURE_VERIFIER_ERROR_CODE,
 | 
			
		||||
				"Failed to find a Signature Verifier for Client Registration: '" +
 | 
			
		||||
						clientRegistration.getRegistrationId() +
 | 
			
		||||
						"'. Check to ensure you have configured a valid JWS Algorithm: '" +
 | 
			
		||||
						jwsAlgorithm + "'",
 | 
			
		||||
				null
 | 
			
		||||
		);
 | 
			
		||||
		throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -80,4 +150,17 @@ public final class ReactiveOidcIdTokenDecoderFactory implements ReactiveJwtDecod
 | 
			
		|||
		Assert.notNull(jwtValidatorFactory, "jwtValidatorFactory cannot be null");
 | 
			
		||||
		this.jwtValidatorFactory = jwtValidatorFactory;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Sets the resolver that provides the expected {@link JwsAlgorithm JWS algorithm}
 | 
			
		||||
	 * used for the signature or MAC on the {@link OidcIdToken ID Token}.
 | 
			
		||||
	 * The default resolves to {@link SignatureAlgorithm#RS256 RS256} for all {@link ClientRegistration clients}.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param jwsAlgorithmResolver the resolver that provides the expected {@link JwsAlgorithm JWS algorithm}
 | 
			
		||||
	 *                             for a specific {@link ClientRegistration client}
 | 
			
		||||
	 */
 | 
			
		||||
	public final void setJwsAlgorithmResolver(Function<ClientRegistration, JwsAlgorithm> jwsAlgorithmResolver) {
 | 
			
		||||
		Assert.notNull(jwsAlgorithmResolver, "jwsAlgorithmResolver cannot be null");
 | 
			
		||||
		this.jwsAlgorithmResolver = jwsAlgorithmResolver;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,13 +21,15 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio
 | 
			
		|||
import org.springframework.security.oauth2.client.registration.TestClientRegistrations;
 | 
			
		||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
 | 
			
		||||
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.JwsAlgorithm;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
 | 
			
		||||
import org.springframework.security.oauth2.jwt.Jwt;
 | 
			
		||||
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
 | 
			
		||||
import static org.mockito.ArgumentMatchers.any;
 | 
			
		||||
import static org.mockito.Mockito.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -42,8 +44,6 @@ public class OidcIdTokenDecoderFactoryTests {
 | 
			
		|||
 | 
			
		||||
	private OidcIdTokenDecoderFactory idTokenDecoderFactory;
 | 
			
		||||
 | 
			
		||||
	private Function<ClientRegistration, OAuth2TokenValidator<Jwt>> defaultJwtValidatorFactory = OidcIdTokenValidator::new;
 | 
			
		||||
 | 
			
		||||
	@Before
 | 
			
		||||
	public void setUp() {
 | 
			
		||||
		this.idTokenDecoderFactory = new OidcIdTokenDecoderFactory();
 | 
			
		||||
| 
						 | 
				
			
			@ -55,6 +55,12 @@ public class OidcIdTokenDecoderFactoryTests {
 | 
			
		|||
				.isInstanceOf(IllegalArgumentException.class);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void setJwsAlgorithmResolverWhenNullThenThrowIllegalArgumentException() {
 | 
			
		||||
		assertThatThrownBy(() -> this.idTokenDecoderFactory.setJwsAlgorithmResolver(null))
 | 
			
		||||
				.isInstanceOf(IllegalArgumentException.class);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void createDecoderWhenClientRegistrationNullThenThrowIllegalArgumentException() {
 | 
			
		||||
		assertThatThrownBy(() -> this.idTokenDecoderFactory.createDecoder(null))
 | 
			
		||||
| 
						 | 
				
			
			@ -62,9 +68,42 @@ public class OidcIdTokenDecoderFactoryTests {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void createDecoderWhenJwkSetUriEmptyThenThrowOAuth2AuthenticationException() {
 | 
			
		||||
	public void createDecoderWhenJwsAlgorithmDefaultAndJwkSetUriEmptyThenThrowOAuth2AuthenticationException() {
 | 
			
		||||
		assertThatThrownBy(() -> this.idTokenDecoderFactory.createDecoder(this.registration.jwkSetUri(null).build()))
 | 
			
		||||
				.isInstanceOf(OAuth2AuthenticationException.class);
 | 
			
		||||
				.isInstanceOf(OAuth2AuthenticationException.class)
 | 
			
		||||
				.hasMessage("[missing_signature_verifier] Failed to find a Signature Verifier " +
 | 
			
		||||
						"for Client Registration: 'registration-id'. " +
 | 
			
		||||
						"Check to ensure you have configured the JwkSet URI.");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void createDecoderWhenJwsAlgorithmEcAndJwkSetUriEmptyThenThrowOAuth2AuthenticationException() {
 | 
			
		||||
		this.idTokenDecoderFactory.setJwsAlgorithmResolver(clientRegistration -> SignatureAlgorithm.ES256);
 | 
			
		||||
		assertThatThrownBy(() -> this.idTokenDecoderFactory.createDecoder(this.registration.jwkSetUri(null).build()))
 | 
			
		||||
				.isInstanceOf(OAuth2AuthenticationException.class)
 | 
			
		||||
				.hasMessage("[missing_signature_verifier] Failed to find a Signature Verifier " +
 | 
			
		||||
						"for Client Registration: 'registration-id'. " +
 | 
			
		||||
						"Check to ensure you have configured the JwkSet URI.");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void createDecoderWhenJwsAlgorithmHmacAndClientSecretNullThenThrowOAuth2AuthenticationException() {
 | 
			
		||||
		this.idTokenDecoderFactory.setJwsAlgorithmResolver(clientRegistration -> MacAlgorithm.HS256);
 | 
			
		||||
		assertThatThrownBy(() -> this.idTokenDecoderFactory.createDecoder(this.registration.clientSecret(null).build()))
 | 
			
		||||
				.isInstanceOf(OAuth2AuthenticationException.class)
 | 
			
		||||
				.hasMessage("[missing_signature_verifier] Failed to find a Signature Verifier " +
 | 
			
		||||
						"for Client Registration: 'registration-id'. " +
 | 
			
		||||
						"Check to ensure you have configured the client secret.");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void createDecoderWhenJwsAlgorithmNullThenThrowOAuth2AuthenticationException() {
 | 
			
		||||
		this.idTokenDecoderFactory.setJwsAlgorithmResolver(clientRegistration -> null);
 | 
			
		||||
		assertThatThrownBy(() -> this.idTokenDecoderFactory.createDecoder(this.registration.build()))
 | 
			
		||||
				.isInstanceOf(OAuth2AuthenticationException.class)
 | 
			
		||||
				.hasMessage("[missing_signature_verifier] Failed to find a Signature Verifier " +
 | 
			
		||||
						"for Client Registration: 'registration-id'. " +
 | 
			
		||||
						"Check to ensure you have configured a valid JWS Algorithm: 'null'");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
| 
						 | 
				
			
			@ -78,11 +117,28 @@ public class OidcIdTokenDecoderFactoryTests {
 | 
			
		|||
		Function<ClientRegistration, OAuth2TokenValidator<Jwt>> customJwtValidatorFactory = mock(Function.class);
 | 
			
		||||
		this.idTokenDecoderFactory.setJwtValidatorFactory(customJwtValidatorFactory);
 | 
			
		||||
 | 
			
		||||
		when(customJwtValidatorFactory.apply(any(ClientRegistration.class)))
 | 
			
		||||
				.thenReturn(this.defaultJwtValidatorFactory.apply(this.registration.build()));
 | 
			
		||||
		ClientRegistration clientRegistration = this.registration.build();
 | 
			
		||||
 | 
			
		||||
		this.idTokenDecoderFactory.createDecoder(this.registration.build());
 | 
			
		||||
		when(customJwtValidatorFactory.apply(same(clientRegistration)))
 | 
			
		||||
				.thenReturn(new OidcIdTokenValidator(clientRegistration));
 | 
			
		||||
 | 
			
		||||
		verify(customJwtValidatorFactory).apply(any(ClientRegistration.class));
 | 
			
		||||
		this.idTokenDecoderFactory.createDecoder(clientRegistration);
 | 
			
		||||
 | 
			
		||||
		verify(customJwtValidatorFactory).apply(same(clientRegistration));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void createDecoderWhenCustomJwsAlgorithmResolverSetThenApplied() {
 | 
			
		||||
		Function<ClientRegistration, JwsAlgorithm> customJwsAlgorithmResolver = mock(Function.class);
 | 
			
		||||
		this.idTokenDecoderFactory.setJwsAlgorithmResolver(customJwsAlgorithmResolver);
 | 
			
		||||
 | 
			
		||||
		ClientRegistration clientRegistration = this.registration.build();
 | 
			
		||||
 | 
			
		||||
		when(customJwsAlgorithmResolver.apply(same(clientRegistration)))
 | 
			
		||||
				.thenReturn(MacAlgorithm.HS256);
 | 
			
		||||
 | 
			
		||||
		this.idTokenDecoderFactory.createDecoder(clientRegistration);
 | 
			
		||||
 | 
			
		||||
		verify(customJwsAlgorithmResolver).apply(same(clientRegistration));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,13 +21,15 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio
 | 
			
		|||
import org.springframework.security.oauth2.client.registration.TestClientRegistrations;
 | 
			
		||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
 | 
			
		||||
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.JwsAlgorithm;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
 | 
			
		||||
import org.springframework.security.oauth2.jwt.Jwt;
 | 
			
		||||
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
 | 
			
		||||
import static org.mockito.ArgumentMatchers.any;
 | 
			
		||||
import static org.mockito.Mockito.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -42,8 +44,6 @@ public class ReactiveOidcIdTokenDecoderFactoryTests {
 | 
			
		|||
 | 
			
		||||
	private ReactiveOidcIdTokenDecoderFactory idTokenDecoderFactory;
 | 
			
		||||
 | 
			
		||||
	private Function<ClientRegistration, OAuth2TokenValidator<Jwt>> defaultJwtValidatorFactory = OidcIdTokenValidator::new;
 | 
			
		||||
 | 
			
		||||
	@Before
 | 
			
		||||
	public void setUp() {
 | 
			
		||||
		this.idTokenDecoderFactory = new ReactiveOidcIdTokenDecoderFactory();
 | 
			
		||||
| 
						 | 
				
			
			@ -55,6 +55,12 @@ public class ReactiveOidcIdTokenDecoderFactoryTests {
 | 
			
		|||
				.isInstanceOf(IllegalArgumentException.class);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void setJwsAlgorithmResolverWhenNullThenThrowIllegalArgumentException() {
 | 
			
		||||
		assertThatThrownBy(() -> this.idTokenDecoderFactory.setJwsAlgorithmResolver(null))
 | 
			
		||||
				.isInstanceOf(IllegalArgumentException.class);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void createDecoderWhenClientRegistrationNullThenThrowIllegalArgumentException() {
 | 
			
		||||
		assertThatThrownBy(() -> this.idTokenDecoderFactory.createDecoder(null))
 | 
			
		||||
| 
						 | 
				
			
			@ -62,9 +68,42 @@ public class ReactiveOidcIdTokenDecoderFactoryTests {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void createDecoderWhenJwkSetUriEmptyThenThrowOAuth2AuthenticationException() {
 | 
			
		||||
	public void createDecoderWhenJwsAlgorithmDefaultAndJwkSetUriEmptyThenThrowOAuth2AuthenticationException() {
 | 
			
		||||
		assertThatThrownBy(() -> this.idTokenDecoderFactory.createDecoder(this.registration.jwkSetUri(null).build()))
 | 
			
		||||
				.isInstanceOf(OAuth2AuthenticationException.class);
 | 
			
		||||
				.isInstanceOf(OAuth2AuthenticationException.class)
 | 
			
		||||
				.hasMessage("[missing_signature_verifier] Failed to find a Signature Verifier " +
 | 
			
		||||
						"for Client Registration: 'registration-id'. " +
 | 
			
		||||
						"Check to ensure you have configured the JwkSet URI.");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void createDecoderWhenJwsAlgorithmEcAndJwkSetUriEmptyThenThrowOAuth2AuthenticationException() {
 | 
			
		||||
		this.idTokenDecoderFactory.setJwsAlgorithmResolver(clientRegistration -> SignatureAlgorithm.ES256);
 | 
			
		||||
		assertThatThrownBy(() -> this.idTokenDecoderFactory.createDecoder(this.registration.jwkSetUri(null).build()))
 | 
			
		||||
				.isInstanceOf(OAuth2AuthenticationException.class)
 | 
			
		||||
				.hasMessage("[missing_signature_verifier] Failed to find a Signature Verifier " +
 | 
			
		||||
						"for Client Registration: 'registration-id'. " +
 | 
			
		||||
						"Check to ensure you have configured the JwkSet URI.");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void createDecoderWhenJwsAlgorithmHmacAndClientSecretNullThenThrowOAuth2AuthenticationException() {
 | 
			
		||||
		this.idTokenDecoderFactory.setJwsAlgorithmResolver(clientRegistration -> MacAlgorithm.HS256);
 | 
			
		||||
		assertThatThrownBy(() -> this.idTokenDecoderFactory.createDecoder(this.registration.clientSecret(null).build()))
 | 
			
		||||
				.isInstanceOf(OAuth2AuthenticationException.class)
 | 
			
		||||
				.hasMessage("[missing_signature_verifier] Failed to find a Signature Verifier " +
 | 
			
		||||
						"for Client Registration: 'registration-id'. " +
 | 
			
		||||
						"Check to ensure you have configured the client secret.");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void createDecoderWhenJwsAlgorithmNullThenThrowOAuth2AuthenticationException() {
 | 
			
		||||
		this.idTokenDecoderFactory.setJwsAlgorithmResolver(clientRegistration -> null);
 | 
			
		||||
		assertThatThrownBy(() -> this.idTokenDecoderFactory.createDecoder(this.registration.build()))
 | 
			
		||||
				.isInstanceOf(OAuth2AuthenticationException.class)
 | 
			
		||||
				.hasMessage("[missing_signature_verifier] Failed to find a Signature Verifier " +
 | 
			
		||||
						"for Client Registration: 'registration-id'. " +
 | 
			
		||||
						"Check to ensure you have configured a valid JWS Algorithm: 'null'");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
| 
						 | 
				
			
			@ -78,11 +117,28 @@ public class ReactiveOidcIdTokenDecoderFactoryTests {
 | 
			
		|||
		Function<ClientRegistration, OAuth2TokenValidator<Jwt>> customJwtValidatorFactory = mock(Function.class);
 | 
			
		||||
		this.idTokenDecoderFactory.setJwtValidatorFactory(customJwtValidatorFactory);
 | 
			
		||||
 | 
			
		||||
		when(customJwtValidatorFactory.apply(any(ClientRegistration.class)))
 | 
			
		||||
				.thenReturn(this.defaultJwtValidatorFactory.apply(this.registration.build()));
 | 
			
		||||
		ClientRegistration clientRegistration = this.registration.build();
 | 
			
		||||
 | 
			
		||||
		this.idTokenDecoderFactory.createDecoder(this.registration.build());
 | 
			
		||||
		when(customJwtValidatorFactory.apply(same(clientRegistration)))
 | 
			
		||||
				.thenReturn(new OidcIdTokenValidator(clientRegistration));
 | 
			
		||||
 | 
			
		||||
		verify(customJwtValidatorFactory).apply(any(ClientRegistration.class));
 | 
			
		||||
		this.idTokenDecoderFactory.createDecoder(clientRegistration);
 | 
			
		||||
 | 
			
		||||
		verify(customJwtValidatorFactory).apply(same(clientRegistration));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void createDecoderWhenCustomJwsAlgorithmResolverSetThenApplied() {
 | 
			
		||||
		Function<ClientRegistration, JwsAlgorithm> customJwsAlgorithmResolver = mock(Function.class);
 | 
			
		||||
		this.idTokenDecoderFactory.setJwsAlgorithmResolver(customJwsAlgorithmResolver);
 | 
			
		||||
 | 
			
		||||
		ClientRegistration clientRegistration = this.registration.build();
 | 
			
		||||
 | 
			
		||||
		when(customJwsAlgorithmResolver.apply(same(clientRegistration)))
 | 
			
		||||
				.thenReturn(MacAlgorithm.HS256);
 | 
			
		||||
 | 
			
		||||
		this.idTokenDecoderFactory.createDecoder(clientRegistration);
 | 
			
		||||
 | 
			
		||||
		verify(customJwsAlgorithmResolver).apply(same(clientRegistration));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2002-2019 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.jose.jws;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Super interface for cryptographic algorithms defined by the JSON Web Algorithms (JWA) specification
 | 
			
		||||
 * and used by JSON Web Signature (JWS) to digitally sign or create a MAC
 | 
			
		||||
 * of the contents of the JWS Protected Header and JWS Payload.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Joe Grandja
 | 
			
		||||
 * @since 5.2
 | 
			
		||||
 * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7518">JSON Web Algorithms (JWA)</a>
 | 
			
		||||
 * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7515">JSON Web Signature (JWS)</a>
 | 
			
		||||
 * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7518#section-3">Cryptographic Algorithms for Digital Signatures and MACs</a>
 | 
			
		||||
 */
 | 
			
		||||
public interface JwsAlgorithm {
 | 
			
		||||
 | 
			
		||||
	String getName();
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,77 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2002-2019 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.jose.jws;
 | 
			
		||||
 | 
			
		||||
import java.util.stream.Stream;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * An enumeration of the cryptographic algorithms defined by the JSON Web Algorithms (JWA) specification
 | 
			
		||||
 * and used by JSON Web Signature (JWS) to create a MAC of the contents of the JWS Protected Header and JWS Payload.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Joe Grandja
 | 
			
		||||
 * @since 5.2
 | 
			
		||||
 * @see JwsAlgorithm
 | 
			
		||||
 * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7518">JSON Web Algorithms (JWA)</a>
 | 
			
		||||
 * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7515">JSON Web Signature (JWS)</a>
 | 
			
		||||
 * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7518#section-3">Cryptographic Algorithms for Digital Signatures and MACs</a>
 | 
			
		||||
 */
 | 
			
		||||
public enum MacAlgorithm implements JwsAlgorithm {
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * HMAC using SHA-256 (Required)
 | 
			
		||||
	 */
 | 
			
		||||
	HS256(JwsAlgorithms.HS256),
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * HMAC using SHA-384 (Optional)
 | 
			
		||||
	 */
 | 
			
		||||
	HS384(JwsAlgorithms.HS384),
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * HMAC using SHA-512 (Optional)
 | 
			
		||||
	 */
 | 
			
		||||
	HS512(JwsAlgorithms.HS512);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	private final String name;
 | 
			
		||||
 | 
			
		||||
	MacAlgorithm(String name) {
 | 
			
		||||
		this.name = name;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Attempt to resolve the provided algorithm name to a {@code MacAlgorithm}.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param name the algorithm name
 | 
			
		||||
	 * @return the resolved {@code MacAlgorithm}, or {@code null} if not found
 | 
			
		||||
	 */
 | 
			
		||||
	public static MacAlgorithm from(String name) {
 | 
			
		||||
		return Stream.of(values())
 | 
			
		||||
				.filter(algorithm -> algorithm.getName().equals(name))
 | 
			
		||||
				.findFirst()
 | 
			
		||||
				.orElse(null);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Returns the algorithm name.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return the algorithm name
 | 
			
		||||
	 */
 | 
			
		||||
	@Override
 | 
			
		||||
	public String getName() {
 | 
			
		||||
		return this.name;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,107 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2002-2019 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.jose.jws;
 | 
			
		||||
 | 
			
		||||
import java.util.stream.Stream;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * An enumeration of the cryptographic algorithms defined by the JSON Web Algorithms (JWA) specification
 | 
			
		||||
 * and used by JSON Web Signature (JWS) to digitally sign the contents of the JWS Protected Header and JWS Payload.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Joe Grandja
 | 
			
		||||
 * @since 5.2
 | 
			
		||||
 * @see JwsAlgorithm
 | 
			
		||||
 * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7518">JSON Web Algorithms (JWA)</a>
 | 
			
		||||
 * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7515">JSON Web Signature (JWS)</a>
 | 
			
		||||
 * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7518#section-3">Cryptographic Algorithms for Digital Signatures and MACs</a>
 | 
			
		||||
 */
 | 
			
		||||
public enum SignatureAlgorithm implements JwsAlgorithm {
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * RSASSA-PKCS1-v1_5 using SHA-256 (Recommended)
 | 
			
		||||
	 */
 | 
			
		||||
	RS256(JwsAlgorithms.RS256),
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * RSASSA-PKCS1-v1_5 using SHA-384 (Optional)
 | 
			
		||||
	 */
 | 
			
		||||
	RS384(JwsAlgorithms.RS384),
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * RSASSA-PKCS1-v1_5 using SHA-512 (Optional)
 | 
			
		||||
	 */
 | 
			
		||||
	RS512(JwsAlgorithms.RS512),
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * ECDSA using P-256 and SHA-256 (Recommended+)
 | 
			
		||||
	 */
 | 
			
		||||
	ES256(JwsAlgorithms.ES256),
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * ECDSA using P-384 and SHA-384 (Optional)
 | 
			
		||||
	 */
 | 
			
		||||
	ES384(JwsAlgorithms.ES384),
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * ECDSA using P-521 and SHA-512 (Optional)
 | 
			
		||||
	 */
 | 
			
		||||
	ES512(JwsAlgorithms.ES512),
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * RSASSA-PSS using SHA-256 and MGF1 with SHA-256 (Optional)
 | 
			
		||||
	 */
 | 
			
		||||
	PS256(JwsAlgorithms.PS256),
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * RSASSA-PSS using SHA-384 and MGF1 with SHA-384 (Optional)
 | 
			
		||||
	 */
 | 
			
		||||
	PS384(JwsAlgorithms.PS384),
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * RSASSA-PSS using SHA-512 and MGF1 with SHA-512 (Optional)
 | 
			
		||||
	 */
 | 
			
		||||
	PS512(JwsAlgorithms.PS512);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	private final String name;
 | 
			
		||||
 | 
			
		||||
	SignatureAlgorithm(String name) {
 | 
			
		||||
		this.name = name;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Attempt to resolve the provided algorithm name to a {@code SignatureAlgorithm}.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param name the algorithm name
 | 
			
		||||
	 * @return the resolved {@code SignatureAlgorithm}, or {@code null} if not found
 | 
			
		||||
	 */
 | 
			
		||||
	public static SignatureAlgorithm from(String name) {
 | 
			
		||||
		return Stream.of(values())
 | 
			
		||||
				.filter(algorithm -> algorithm.getName().equals(name))
 | 
			
		||||
				.findFirst()
 | 
			
		||||
				.orElse(null);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Returns the algorithm name.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return the algorithm name
 | 
			
		||||
	 */
 | 
			
		||||
	@Override
 | 
			
		||||
	public String getName() {
 | 
			
		||||
		return this.name;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -16,21 +16,12 @@
 | 
			
		|||
 | 
			
		||||
package org.springframework.security.oauth2.jwt;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.net.MalformedURLException;
 | 
			
		||||
import java.net.URL;
 | 
			
		||||
import java.security.interfaces.RSAPublicKey;
 | 
			
		||||
import java.text.ParseException;
 | 
			
		||||
import java.time.Instant;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.LinkedHashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import com.nimbusds.jose.JWSAlgorithm;
 | 
			
		||||
import com.nimbusds.jose.RemoteKeySourceException;
 | 
			
		||||
import com.nimbusds.jose.jwk.JWKSet;
 | 
			
		||||
import com.nimbusds.jose.jwk.RSAKey;
 | 
			
		||||
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
 | 
			
		||||
import com.nimbusds.jose.jwk.source.ImmutableSecret;
 | 
			
		||||
import com.nimbusds.jose.jwk.source.JWKSource;
 | 
			
		||||
import com.nimbusds.jose.jwk.source.RemoteJWKSet;
 | 
			
		||||
import com.nimbusds.jose.proc.JWSKeySelector;
 | 
			
		||||
| 
						 | 
				
			
			@ -45,7 +36,6 @@ import com.nimbusds.jwt.SignedJWT;
 | 
			
		|||
import com.nimbusds.jwt.proc.ConfigurableJWTProcessor;
 | 
			
		||||
import com.nimbusds.jwt.proc.DefaultJWTProcessor;
 | 
			
		||||
import com.nimbusds.jwt.proc.JWTProcessor;
 | 
			
		||||
 | 
			
		||||
import org.springframework.core.convert.converter.Converter;
 | 
			
		||||
import org.springframework.http.HttpHeaders;
 | 
			
		||||
import org.springframework.http.HttpMethod;
 | 
			
		||||
| 
						 | 
				
			
			@ -54,15 +44,29 @@ import org.springframework.http.RequestEntity;
 | 
			
		|||
import org.springframework.http.ResponseEntity;
 | 
			
		||||
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
 | 
			
		||||
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.JwsAlgorithm;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
 | 
			
		||||
import org.springframework.util.Assert;
 | 
			
		||||
import org.springframework.web.client.RestOperations;
 | 
			
		||||
import org.springframework.web.client.RestTemplate;
 | 
			
		||||
 | 
			
		||||
import javax.crypto.SecretKey;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.net.MalformedURLException;
 | 
			
		||||
import java.net.URL;
 | 
			
		||||
import java.security.interfaces.RSAPublicKey;
 | 
			
		||||
import java.text.ParseException;
 | 
			
		||||
import java.time.Instant;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.LinkedHashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A low-level Nimbus implementation of {@link JwtDecoder} which takes a raw Nimbus configuration.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Josh Cummings
 | 
			
		||||
 * @author Joe Grandja
 | 
			
		||||
 * @since 5.2
 | 
			
		||||
 */
 | 
			
		||||
public final class NimbusJwtDecoder implements JwtDecoder {
 | 
			
		||||
| 
						 | 
				
			
			@ -178,8 +182,6 @@ public final class NimbusJwtDecoder implements JwtDecoder {
 | 
			
		|||
	 *
 | 
			
		||||
	 * @param jwkSetUri the JWK Set uri to use
 | 
			
		||||
	 * @return a {@link JwkSetUriJwtDecoderBuilder} for further configurations
 | 
			
		||||
	 *
 | 
			
		||||
	 * @since 5.2
 | 
			
		||||
	 */
 | 
			
		||||
	public static JwkSetUriJwtDecoderBuilder withJwkSetUri(String jwkSetUri) {
 | 
			
		||||
		return new JwkSetUriJwtDecoderBuilder(jwkSetUri);
 | 
			
		||||
| 
						 | 
				
			
			@ -190,18 +192,24 @@ public final class NimbusJwtDecoder implements JwtDecoder {
 | 
			
		|||
	 *
 | 
			
		||||
	 * @param key the public key to use
 | 
			
		||||
	 * @return a {@link PublicKeyJwtDecoderBuilder} for further configurations
 | 
			
		||||
	 *
 | 
			
		||||
	 * @since 5.2
 | 
			
		||||
	 */
 | 
			
		||||
	public static PublicKeyJwtDecoderBuilder withPublicKey(RSAPublicKey key) {
 | 
			
		||||
		return new PublicKeyJwtDecoderBuilder(key);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Use the given {@code SecretKey} to validate the MAC on a JSON Web Signature (JWS).
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param secretKey the {@code SecretKey} used to validate the MAC
 | 
			
		||||
	 * @return a {@link SecretKeyJwtDecoderBuilder} for further configurations
 | 
			
		||||
	 */
 | 
			
		||||
	public static SecretKeyJwtDecoderBuilder withSecretKey(SecretKey secretKey) {
 | 
			
		||||
		return new SecretKeyJwtDecoderBuilder(secretKey);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * A builder for creating {@link NimbusJwtDecoder} instances based on a
 | 
			
		||||
	 * <a target="_blank" href="https://tools.ietf.org/html/rfc7517#section-5">JWK Set</a> uri.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @since 5.2
 | 
			
		||||
	 */
 | 
			
		||||
	public static final class JwkSetUriJwtDecoderBuilder {
 | 
			
		||||
		private String jwkSetUri;
 | 
			
		||||
| 
						 | 
				
			
			@ -220,9 +228,9 @@ public final class NimbusJwtDecoder implements JwtDecoder {
 | 
			
		|||
		 * @param jwsAlgorithm the algorithm to use
 | 
			
		||||
		 * @return a {@link JwkSetUriJwtDecoderBuilder} for further configurations
 | 
			
		||||
		 */
 | 
			
		||||
		public JwkSetUriJwtDecoderBuilder jwsAlgorithm(String jwsAlgorithm) {
 | 
			
		||||
			Assert.hasText(jwsAlgorithm, "jwsAlgorithm cannot be empty");
 | 
			
		||||
			this.jwsAlgorithm = JWSAlgorithm.parse(jwsAlgorithm);
 | 
			
		||||
		public JwkSetUriJwtDecoderBuilder jwsAlgorithm(JwsAlgorithm jwsAlgorithm) {
 | 
			
		||||
			Assert.notNull(jwsAlgorithm, "jwsAlgorithm cannot be null");
 | 
			
		||||
			this.jwsAlgorithm = JWSAlgorithm.parse(jwsAlgorithm.getName());
 | 
			
		||||
			return this;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -303,10 +311,7 @@ public final class NimbusJwtDecoder implements JwtDecoder {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * A builder for creating {@link NimbusJwtDecoder} instances based on a
 | 
			
		||||
	 * public key.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @since 5.2
 | 
			
		||||
	 * A builder for creating {@link NimbusJwtDecoder} instances based on a public key.
 | 
			
		||||
	 */
 | 
			
		||||
	public static final class PublicKeyJwtDecoderBuilder {
 | 
			
		||||
		private JWSAlgorithm jwsAlgorithm;
 | 
			
		||||
| 
						 | 
				
			
			@ -314,7 +319,7 @@ public final class NimbusJwtDecoder implements JwtDecoder {
 | 
			
		|||
 | 
			
		||||
		private PublicKeyJwtDecoderBuilder(RSAPublicKey key) {
 | 
			
		||||
			Assert.notNull(key, "key cannot be null");
 | 
			
		||||
			this.jwsAlgorithm = JWSAlgorithm.parse(JwsAlgorithms.RS256);
 | 
			
		||||
			this.jwsAlgorithm = JWSAlgorithm.RS256;
 | 
			
		||||
			this.key = rsaKey(key);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -330,12 +335,12 @@ public final class NimbusJwtDecoder implements JwtDecoder {
 | 
			
		|||
		 * The value should be one of
 | 
			
		||||
		 * <a href="https://tools.ietf.org/html/rfc7518#section-3.3" target="_blank">RS256, RS384, or RS512</a>.
 | 
			
		||||
		 *
 | 
			
		||||
		 * @param jwsAlgorithm the algorithm to use
 | 
			
		||||
		 * @param signatureAlgorithm the algorithm to use
 | 
			
		||||
		 * @return a {@link PublicKeyJwtDecoderBuilder} for further configurations
 | 
			
		||||
		 */
 | 
			
		||||
		public PublicKeyJwtDecoderBuilder jwsAlgorithm(String jwsAlgorithm) {
 | 
			
		||||
			Assert.hasText(jwsAlgorithm, "jwsAlgorithm cannot be empty");
 | 
			
		||||
			this.jwsAlgorithm = JWSAlgorithm.parse(jwsAlgorithm);
 | 
			
		||||
		public PublicKeyJwtDecoderBuilder signatureAlgorithm(SignatureAlgorithm signatureAlgorithm) {
 | 
			
		||||
			Assert.notNull(signatureAlgorithm, "signatureAlgorithm cannot be null");
 | 
			
		||||
			this.jwsAlgorithm = JWSAlgorithm.parse(signatureAlgorithm.getName());
 | 
			
		||||
			return this;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -368,4 +373,56 @@ public final class NimbusJwtDecoder implements JwtDecoder {
 | 
			
		|||
			return new NimbusJwtDecoder(processor());
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * A builder for creating {@link NimbusJwtDecoder} instances based on a {@code SecretKey}.
 | 
			
		||||
	 */
 | 
			
		||||
	public static final class SecretKeyJwtDecoderBuilder {
 | 
			
		||||
		private final SecretKey secretKey;
 | 
			
		||||
		private JWSAlgorithm jwsAlgorithm = JWSAlgorithm.HS256;
 | 
			
		||||
 | 
			
		||||
		private SecretKeyJwtDecoderBuilder(SecretKey secretKey) {
 | 
			
		||||
			Assert.notNull(secretKey, "secretKey cannot be null");
 | 
			
		||||
			this.secretKey = secretKey;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Use the given
 | 
			
		||||
		 * <a href="https://tools.ietf.org/html/rfc7515#section-4.1.1" target="_blank">algorithm</a>
 | 
			
		||||
		 * when generating the MAC.
 | 
			
		||||
		 *
 | 
			
		||||
		 * The value should be one of
 | 
			
		||||
		 * <a href="https://tools.ietf.org/html/rfc7518#section-3.2" target="_blank">HS256, HS384 or HS512</a>.
 | 
			
		||||
		 *
 | 
			
		||||
		 * @param macAlgorithm the MAC algorithm to use
 | 
			
		||||
		 * @return a {@link SecretKeyJwtDecoderBuilder} for further configurations
 | 
			
		||||
		 */
 | 
			
		||||
		public SecretKeyJwtDecoderBuilder macAlgorithm(MacAlgorithm macAlgorithm) {
 | 
			
		||||
			Assert.notNull(macAlgorithm, "macAlgorithm cannot be null");
 | 
			
		||||
			this.jwsAlgorithm = JWSAlgorithm.parse(macAlgorithm.getName());
 | 
			
		||||
			return this;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Build the configured {@link NimbusJwtDecoder}.
 | 
			
		||||
		 *
 | 
			
		||||
		 * @return the configured {@link NimbusJwtDecoder}
 | 
			
		||||
		 */
 | 
			
		||||
		public NimbusJwtDecoder build() {
 | 
			
		||||
			return new NimbusJwtDecoder(processor());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		JWTProcessor<SecurityContext> processor() {
 | 
			
		||||
			JWKSource<SecurityContext> jwkSource = new ImmutableSecret<>(this.secretKey);
 | 
			
		||||
			JWSKeySelector<SecurityContext> jwsKeySelector =
 | 
			
		||||
					new JWSVerificationKeySelector<>(this.jwsAlgorithm, jwkSource);
 | 
			
		||||
			DefaultJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();
 | 
			
		||||
			jwtProcessor.setJWSKeySelector(jwsKeySelector);
 | 
			
		||||
 | 
			
		||||
			// Spring Security validates the claim set independent from Nimbus
 | 
			
		||||
			jwtProcessor.setJWTClaimsSetVerifier((claims, context) -> { });
 | 
			
		||||
 | 
			
		||||
			return jwtProcessor;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,15 +15,16 @@
 | 
			
		|||
 */
 | 
			
		||||
package org.springframework.security.oauth2.jwt;
 | 
			
		||||
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import org.springframework.core.convert.converter.Converter;
 | 
			
		||||
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
 | 
			
		||||
import org.springframework.util.Assert;
 | 
			
		||||
import org.springframework.web.client.RestOperations;
 | 
			
		||||
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import static org.springframework.security.oauth2.jwt.NimbusJwtDecoder.withJwkSetUri;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -75,7 +76,7 @@ public final class NimbusJwtDecoderJwkSupport implements JwtDecoder {
 | 
			
		|||
		Assert.hasText(jwkSetUrl, "jwkSetUrl cannot be empty");
 | 
			
		||||
		Assert.hasText(jwsAlgorithm, "jwsAlgorithm cannot be empty");
 | 
			
		||||
 | 
			
		||||
		this.jwtDecoderBuilder = withJwkSetUri(jwkSetUrl).jwsAlgorithm(jwsAlgorithm);
 | 
			
		||||
		this.jwtDecoderBuilder = withJwkSetUri(jwkSetUrl).jwsAlgorithm(SignatureAlgorithm.from(jwsAlgorithm));
 | 
			
		||||
		this.delegate = makeDelegate();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,13 +15,6 @@
 | 
			
		|||
 */
 | 
			
		||||
package org.springframework.security.oauth2.jwt;
 | 
			
		||||
 | 
			
		||||
import java.security.interfaces.RSAPublicKey;
 | 
			
		||||
import java.time.Instant;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.LinkedHashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
 | 
			
		||||
import com.nimbusds.jose.JOSEException;
 | 
			
		||||
import com.nimbusds.jose.JWSAlgorithm;
 | 
			
		||||
import com.nimbusds.jose.JWSHeader;
 | 
			
		||||
| 
						 | 
				
			
			@ -31,6 +24,7 @@ import com.nimbusds.jose.jwk.JWKSelector;
 | 
			
		|||
import com.nimbusds.jose.jwk.JWKSet;
 | 
			
		||||
import com.nimbusds.jose.jwk.RSAKey;
 | 
			
		||||
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
 | 
			
		||||
import com.nimbusds.jose.jwk.source.ImmutableSecret;
 | 
			
		||||
import com.nimbusds.jose.jwk.source.JWKSecurityContextJWKSet;
 | 
			
		||||
import com.nimbusds.jose.jwk.source.JWKSource;
 | 
			
		||||
import com.nimbusds.jose.proc.BadJOSEException;
 | 
			
		||||
| 
						 | 
				
			
			@ -44,26 +38,35 @@ import com.nimbusds.jwt.JWTParser;
 | 
			
		|||
import com.nimbusds.jwt.SignedJWT;
 | 
			
		||||
import com.nimbusds.jwt.proc.DefaultJWTProcessor;
 | 
			
		||||
import com.nimbusds.jwt.proc.JWTProcessor;
 | 
			
		||||
import reactor.core.publisher.Flux;
 | 
			
		||||
import reactor.core.publisher.Mono;
 | 
			
		||||
 | 
			
		||||
import org.springframework.core.convert.converter.Converter;
 | 
			
		||||
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
 | 
			
		||||
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.JwsAlgorithm;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
 | 
			
		||||
import org.springframework.util.Assert;
 | 
			
		||||
import org.springframework.web.reactive.function.client.WebClient;
 | 
			
		||||
import reactor.core.publisher.Flux;
 | 
			
		||||
import reactor.core.publisher.Mono;
 | 
			
		||||
 | 
			
		||||
import javax.crypto.SecretKey;
 | 
			
		||||
import java.security.interfaces.RSAPublicKey;
 | 
			
		||||
import java.time.Instant;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.LinkedHashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * An implementation of a {@link ReactiveJwtDecoder} that "decodes" a
 | 
			
		||||
 * JSON Web Token (JWT) and additionally verifies it's digital signature if the JWT is a
 | 
			
		||||
 * JSON Web Signature (JWS). The public key used for verification is obtained from the
 | 
			
		||||
 * JSON Web Key (JWK) Set {@code URL} supplied via the constructor.
 | 
			
		||||
 * JSON Web Signature (JWS).
 | 
			
		||||
 *
 | 
			
		||||
 * <p>
 | 
			
		||||
 * <b>NOTE:</b> This implementation uses the Nimbus JOSE + JWT SDK internally.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Rob Winch
 | 
			
		||||
 * @author Joe Grandja
 | 
			
		||||
 * @since 5.1
 | 
			
		||||
 * @see ReactiveJwtDecoder
 | 
			
		||||
 * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7519">JSON Web Token (JWT)</a>
 | 
			
		||||
| 
						 | 
				
			
			@ -75,22 +78,34 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
 | 
			
		|||
	private final Converter<SignedJWT, Mono<JWTClaimsSet>> jwtProcessor;
 | 
			
		||||
 | 
			
		||||
	private OAuth2TokenValidator<Jwt> jwtValidator = JwtValidators.createDefault();
 | 
			
		||||
	private Converter<Map<String, Object>, Map<String, Object>> claimSetConverter = MappedJwtClaimSetConverter
 | 
			
		||||
			.withDefaults(Collections.emptyMap());
 | 
			
		||||
 | 
			
		||||
	public NimbusReactiveJwtDecoder(RSAPublicKey publicKey) {
 | 
			
		||||
		this.jwtProcessor = withPublicKey(publicKey).processor();
 | 
			
		||||
	}
 | 
			
		||||
	private Converter<Map<String, Object>, Map<String, Object>> claimSetConverter =
 | 
			
		||||
			MappedJwtClaimSetConverter.withDefaults(Collections.emptyMap());
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Constructs a {@code NimbusJwtDecoderJwkSupport} using the provided parameters.
 | 
			
		||||
	 * Constructs a {@code NimbusReactiveJwtDecoder} using the provided parameters.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param jwkSetUrl the JSON Web Key (JWK) Set {@code URL}
 | 
			
		||||
	 */
 | 
			
		||||
	public NimbusReactiveJwtDecoder(String jwkSetUrl) {
 | 
			
		||||
		this.jwtProcessor = withJwkSetUri(jwkSetUrl).processor();
 | 
			
		||||
		this(withJwkSetUri(jwkSetUrl).processor());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Constructs a {@code NimbusReactiveJwtDecoder} using the provided parameters.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param publicKey the {@code RSAPublicKey} used to verify the signature
 | 
			
		||||
	 * @since 5.2
 | 
			
		||||
	 */
 | 
			
		||||
	public NimbusReactiveJwtDecoder(RSAPublicKey publicKey) {
 | 
			
		||||
		this(withPublicKey(publicKey).processor());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Constructs a {@code NimbusReactiveJwtDecoder} using the provided parameters.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param jwtProcessor the {@link Converter} used to process and verify the signed Jwt and return the Jwt Claim Set
 | 
			
		||||
	 * @since 5.2
 | 
			
		||||
	 */
 | 
			
		||||
	public NimbusReactiveJwtDecoder(Converter<SignedJWT, Mono<JWTClaimsSet>> jwtProcessor) {
 | 
			
		||||
		this.jwtProcessor = jwtProcessor;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -188,6 +203,18 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
 | 
			
		|||
		return new PublicKeyReactiveJwtDecoderBuilder(key);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Use the given {@code SecretKey} to validate the MAC on a JSON Web Signature (JWS).
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param secretKey the {@code SecretKey} used to validate the MAC
 | 
			
		||||
	 * @return a {@link SecretKeyReactiveJwtDecoderBuilder} for further configurations
 | 
			
		||||
	 *
 | 
			
		||||
	 * @since 5.2
 | 
			
		||||
	 */
 | 
			
		||||
	public static SecretKeyReactiveJwtDecoderBuilder withSecretKey(SecretKey secretKey) {
 | 
			
		||||
		return new SecretKeyReactiveJwtDecoderBuilder(secretKey);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Use the given {@link Function} to validate JWTs
 | 
			
		||||
	 *
 | 
			
		||||
| 
						 | 
				
			
			@ -207,8 +234,7 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
 | 
			
		|||
	 * @since 5.2
 | 
			
		||||
	 */
 | 
			
		||||
	public static final class JwkSetUriReactiveJwtDecoderBuilder {
 | 
			
		||||
 | 
			
		||||
		private String jwkSetUri;
 | 
			
		||||
		private final String jwkSetUri;
 | 
			
		||||
		private JWSAlgorithm jwsAlgorithm = JWSAlgorithm.RS256;
 | 
			
		||||
		private WebClient webClient = WebClient.create();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -224,9 +250,9 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
 | 
			
		|||
		 * @param jwsAlgorithm the algorithm to use
 | 
			
		||||
		 * @return a {@link JwkSetUriReactiveJwtDecoderBuilder} for further configurations
 | 
			
		||||
		 */
 | 
			
		||||
		public JwkSetUriReactiveJwtDecoderBuilder jwsAlgorithm(String jwsAlgorithm) {
 | 
			
		||||
			Assert.hasText(jwsAlgorithm, "jwsAlgorithm cannot be empty");
 | 
			
		||||
			this.jwsAlgorithm = JWSAlgorithm.parse(jwsAlgorithm);
 | 
			
		||||
		public JwkSetUriReactiveJwtDecoderBuilder jwsAlgorithm(JwsAlgorithm jwsAlgorithm) {
 | 
			
		||||
			Assert.notNull(jwsAlgorithm, "jwsAlgorithm cannot be null");
 | 
			
		||||
			this.jwsAlgorithm = JWSAlgorithm.parse(jwsAlgorithm.getName());
 | 
			
		||||
			return this;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -284,19 +310,18 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * A builder for creating Nimbus {@link JWTProcessor} instances based on a
 | 
			
		||||
	 * public key.
 | 
			
		||||
	 * A builder for creating {@link NimbusReactiveJwtDecoder} instances based on a public key.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @since 5.2
 | 
			
		||||
	 */
 | 
			
		||||
	public static final class PublicKeyReactiveJwtDecoderBuilder {
 | 
			
		||||
		private final RSAKey key;
 | 
			
		||||
		private JWSAlgorithm jwsAlgorithm;
 | 
			
		||||
		private RSAKey key;
 | 
			
		||||
 | 
			
		||||
		private PublicKeyReactiveJwtDecoderBuilder(RSAPublicKey key) {
 | 
			
		||||
			Assert.notNull(key, "key cannot be null");
 | 
			
		||||
			this.jwsAlgorithm = JWSAlgorithm.parse(JwsAlgorithms.RS256);
 | 
			
		||||
			this.key = rsaKey(key);
 | 
			
		||||
			this.jwsAlgorithm = JWSAlgorithm.RS256;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private static RSAKey rsaKey(RSAPublicKey publicKey) {
 | 
			
		||||
| 
						 | 
				
			
			@ -310,12 +335,12 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
 | 
			
		|||
		 * The value should be one of
 | 
			
		||||
		 * <a href="https://tools.ietf.org/html/rfc7518#section-3.3" target="_blank">RS256, RS384, or RS512</a>.
 | 
			
		||||
		 *
 | 
			
		||||
		 * @param jwsAlgorithm the algorithm to use
 | 
			
		||||
		 * @param signatureAlgorithm the algorithm to use
 | 
			
		||||
		 * @return a {@link PublicKeyReactiveJwtDecoderBuilder} for further configurations
 | 
			
		||||
		 */
 | 
			
		||||
		public PublicKeyReactiveJwtDecoderBuilder jwsAlgorithm(String jwsAlgorithm) {
 | 
			
		||||
			Assert.hasText(jwsAlgorithm, "jwsAlgorithm cannot be empty");
 | 
			
		||||
			this.jwsAlgorithm = JWSAlgorithm.parse(jwsAlgorithm);
 | 
			
		||||
		public PublicKeyReactiveJwtDecoderBuilder signatureAlgorithm(SignatureAlgorithm signatureAlgorithm) {
 | 
			
		||||
			Assert.notNull(signatureAlgorithm, "signatureAlgorithm cannot be null");
 | 
			
		||||
			this.jwsAlgorithm = JWSAlgorithm.parse(signatureAlgorithm.getName());
 | 
			
		||||
			return this;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -349,17 +374,71 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * A builder for creating {@link NimbusReactiveJwtDecoder} instances based on a {@code SecretKey}.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @since 5.2
 | 
			
		||||
	 */
 | 
			
		||||
	public static final class SecretKeyReactiveJwtDecoderBuilder {
 | 
			
		||||
		private final SecretKey secretKey;
 | 
			
		||||
		private JWSAlgorithm jwsAlgorithm = JWSAlgorithm.HS256;
 | 
			
		||||
 | 
			
		||||
		private SecretKeyReactiveJwtDecoderBuilder(SecretKey secretKey) {
 | 
			
		||||
			Assert.notNull(secretKey, "secretKey cannot be null");
 | 
			
		||||
			this.secretKey = secretKey;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Use the given
 | 
			
		||||
		 * <a href="https://tools.ietf.org/html/rfc7515#section-4.1.1" target="_blank">algorithm</a>
 | 
			
		||||
		 * when generating the MAC.
 | 
			
		||||
		 *
 | 
			
		||||
		 * The value should be one of
 | 
			
		||||
		 * <a href="https://tools.ietf.org/html/rfc7518#section-3.2" target="_blank">HS256, HS384 or HS512</a>.
 | 
			
		||||
		 *
 | 
			
		||||
		 * @param macAlgorithm the MAC algorithm to use
 | 
			
		||||
		 * @return a {@link SecretKeyReactiveJwtDecoderBuilder} for further configurations
 | 
			
		||||
		 */
 | 
			
		||||
		public SecretKeyReactiveJwtDecoderBuilder macAlgorithm(MacAlgorithm macAlgorithm) {
 | 
			
		||||
			Assert.notNull(macAlgorithm, "macAlgorithm cannot be null");
 | 
			
		||||
			this.jwsAlgorithm = JWSAlgorithm.parse(macAlgorithm.getName());
 | 
			
		||||
			return this;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Build the configured {@link NimbusReactiveJwtDecoder}.
 | 
			
		||||
		 *
 | 
			
		||||
		 * @return the configured {@link NimbusReactiveJwtDecoder}
 | 
			
		||||
		 */
 | 
			
		||||
		public NimbusReactiveJwtDecoder build() {
 | 
			
		||||
			return new NimbusReactiveJwtDecoder(processor());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Converter<SignedJWT, Mono<JWTClaimsSet>> processor() {
 | 
			
		||||
			JWKSource<SecurityContext> jwkSource = new ImmutableSecret<>(this.secretKey);
 | 
			
		||||
			JWSKeySelector<SecurityContext> jwsKeySelector =
 | 
			
		||||
					new JWSVerificationKeySelector<>(this.jwsAlgorithm, jwkSource);
 | 
			
		||||
			DefaultJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();
 | 
			
		||||
			jwtProcessor.setJWSKeySelector(jwsKeySelector);
 | 
			
		||||
 | 
			
		||||
			// Spring Security validates the claim set independent from Nimbus
 | 
			
		||||
			jwtProcessor.setJWTClaimsSetVerifier((claims, context) -> { });
 | 
			
		||||
 | 
			
		||||
			return signedJWT -> Mono.just(signedJWT).map(jwt -> createClaimsSet(jwtProcessor, jwt, null));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * A builder for creating {@link NimbusReactiveJwtDecoder} instances.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @since 5.2
 | 
			
		||||
	 */
 | 
			
		||||
	public static final class JwkSourceReactiveJwtDecoderBuilder {
 | 
			
		||||
		private Function<JWT, Flux<JWK>> jwkSource;
 | 
			
		||||
		private final Function<JWT, Flux<JWK>> jwkSource;
 | 
			
		||||
		private JWSAlgorithm jwsAlgorithm = JWSAlgorithm.RS256;
 | 
			
		||||
 | 
			
		||||
		private JwkSourceReactiveJwtDecoderBuilder(Function<JWT, Flux<JWK>> jwkSource) {
 | 
			
		||||
			Assert.notNull(jwkSource, "jwkSource cannot be empty");
 | 
			
		||||
			Assert.notNull(jwkSource, "jwkSource cannot be null");
 | 
			
		||||
			this.jwkSource = jwkSource;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -370,9 +449,9 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
 | 
			
		|||
		 * @param jwsAlgorithm the algorithm to use
 | 
			
		||||
		 * @return a {@link JwkSourceReactiveJwtDecoderBuilder} for further configurations
 | 
			
		||||
		 */
 | 
			
		||||
		public JwkSourceReactiveJwtDecoderBuilder jwsAlgorithm(String jwsAlgorithm) {
 | 
			
		||||
			Assert.hasText(jwsAlgorithm, "jwsAlgorithm cannot be empty");
 | 
			
		||||
			this.jwsAlgorithm = JWSAlgorithm.parse(jwsAlgorithm);
 | 
			
		||||
		public JwkSourceReactiveJwtDecoderBuilder jwsAlgorithm(JwsAlgorithm jwsAlgorithm) {
 | 
			
		||||
			Assert.notNull(jwsAlgorithm, "jwsAlgorithm cannot be null");
 | 
			
		||||
			this.jwsAlgorithm = JWSAlgorithm.parse(jwsAlgorithm.getName());
 | 
			
		||||
			return this;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2002-2019 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.jose;
 | 
			
		||||
 | 
			
		||||
import javax.crypto.SecretKey;
 | 
			
		||||
import javax.crypto.spec.SecretKeySpec;
 | 
			
		||||
import java.util.Base64;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author Joe Grandja
 | 
			
		||||
 * @since 5.2
 | 
			
		||||
 */
 | 
			
		||||
public class TestKeys {
 | 
			
		||||
	public static final String DEFAULT_ENCODED_SECRET_KEY = "bCzY/M48bbkwBEWjmNSIEPfwApcvXOnkCxORBEbPr+4=";
 | 
			
		||||
 | 
			
		||||
	public static final SecretKey DEFAULT_SECRET_KEY =
 | 
			
		||||
			new SecretKeySpec(Base64.getDecoder().decode(DEFAULT_ENCODED_SECRET_KEY), "AES");
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,41 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2002-2019 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.jose.jws;
 | 
			
		||||
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests for {@link MacAlgorithm}
 | 
			
		||||
 *
 | 
			
		||||
 * @author Joe Grandja
 | 
			
		||||
 * @since 5.2
 | 
			
		||||
 */
 | 
			
		||||
public class MacAlgorithmTests {
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void fromWhenAlgorithmValidThenResolves() {
 | 
			
		||||
		assertThat(MacAlgorithm.from(JwsAlgorithms.HS256)).isEqualTo(MacAlgorithm.HS256);
 | 
			
		||||
		assertThat(MacAlgorithm.from(JwsAlgorithms.HS384)).isEqualTo(MacAlgorithm.HS384);
 | 
			
		||||
		assertThat(MacAlgorithm.from(JwsAlgorithms.HS512)).isEqualTo(MacAlgorithm.HS512);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void fromWhenAlgorithmInvalidThenDoesNotResolve() {
 | 
			
		||||
		assertThat(MacAlgorithm.from("invalid")).isNull();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,47 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2002-2019 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.jose.jws;
 | 
			
		||||
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests for {@link SignatureAlgorithm}
 | 
			
		||||
 *
 | 
			
		||||
 * @author Joe Grandja
 | 
			
		||||
 * @since 5.2
 | 
			
		||||
 */
 | 
			
		||||
public class SignatureAlgorithmTests {
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void fromWhenAlgorithmValidThenResolves() {
 | 
			
		||||
		assertThat(SignatureAlgorithm.from(JwsAlgorithms.RS256)).isEqualTo(SignatureAlgorithm.RS256);
 | 
			
		||||
		assertThat(SignatureAlgorithm.from(JwsAlgorithms.RS384)).isEqualTo(SignatureAlgorithm.RS384);
 | 
			
		||||
		assertThat(SignatureAlgorithm.from(JwsAlgorithms.RS512)).isEqualTo(SignatureAlgorithm.RS512);
 | 
			
		||||
		assertThat(SignatureAlgorithm.from(JwsAlgorithms.ES256)).isEqualTo(SignatureAlgorithm.ES256);
 | 
			
		||||
		assertThat(SignatureAlgorithm.from(JwsAlgorithms.ES384)).isEqualTo(SignatureAlgorithm.ES384);
 | 
			
		||||
		assertThat(SignatureAlgorithm.from(JwsAlgorithms.ES512)).isEqualTo(SignatureAlgorithm.ES512);
 | 
			
		||||
		assertThat(SignatureAlgorithm.from(JwsAlgorithms.PS256)).isEqualTo(SignatureAlgorithm.PS256);
 | 
			
		||||
		assertThat(SignatureAlgorithm.from(JwsAlgorithms.PS384)).isEqualTo(SignatureAlgorithm.PS384);
 | 
			
		||||
		assertThat(SignatureAlgorithm.from(JwsAlgorithms.PS512)).isEqualTo(SignatureAlgorithm.PS512);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void fromWhenAlgorithmInvalidThenDoesNotResolve() {
 | 
			
		||||
		assertThat(SignatureAlgorithm.from("invalid")).isNull();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -16,19 +16,11 @@
 | 
			
		|||
 | 
			
		||||
package org.springframework.security.oauth2.jwt;
 | 
			
		||||
 | 
			
		||||
import java.security.KeyFactory;
 | 
			
		||||
import java.security.NoSuchAlgorithmException;
 | 
			
		||||
import java.security.interfaces.RSAPublicKey;
 | 
			
		||||
import java.security.spec.EncodedKeySpec;
 | 
			
		||||
import java.security.spec.InvalidKeySpecException;
 | 
			
		||||
import java.security.spec.X509EncodedKeySpec;
 | 
			
		||||
import java.text.ParseException;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Base64;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import com.nimbusds.jose.JOSEException;
 | 
			
		||||
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.proc.BadJOSEException;
 | 
			
		||||
import com.nimbusds.jose.proc.SecurityContext;
 | 
			
		||||
import com.nimbusds.jwt.JWTClaimsSet;
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +32,6 @@ import okhttp3.mockwebserver.MockWebServer;
 | 
			
		|||
import org.assertj.core.api.Assertions;
 | 
			
		||||
import org.junit.BeforeClass;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
 | 
			
		||||
import org.springframework.core.convert.converter.Converter;
 | 
			
		||||
import org.springframework.http.HttpStatus;
 | 
			
		||||
import org.springframework.http.RequestEntity;
 | 
			
		||||
| 
						 | 
				
			
			@ -48,9 +39,26 @@ import org.springframework.http.ResponseEntity;
 | 
			
		|||
import org.springframework.security.oauth2.core.OAuth2Error;
 | 
			
		||||
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
 | 
			
		||||
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
 | 
			
		||||
import org.springframework.security.oauth2.jose.TestKeys;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
 | 
			
		||||
import org.springframework.web.client.RestOperations;
 | 
			
		||||
 | 
			
		||||
import javax.crypto.SecretKey;
 | 
			
		||||
import java.security.KeyFactory;
 | 
			
		||||
import java.security.NoSuchAlgorithmException;
 | 
			
		||||
import java.security.interfaces.RSAPublicKey;
 | 
			
		||||
import java.security.spec.EncodedKeySpec;
 | 
			
		||||
import java.security.spec.InvalidKeySpecException;
 | 
			
		||||
import java.security.spec.X509EncodedKeySpec;
 | 
			
		||||
import java.text.ParseException;
 | 
			
		||||
import java.time.Instant;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Base64;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode;
 | 
			
		||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
 | 
			
		||||
| 
						 | 
				
			
			@ -58,13 +66,13 @@ import static org.mockito.ArgumentMatchers.any;
 | 
			
		|||
import static org.mockito.ArgumentMatchers.eq;
 | 
			
		||||
import static org.mockito.Mockito.mock;
 | 
			
		||||
import static org.mockito.Mockito.when;
 | 
			
		||||
import static org.springframework.security.oauth2.jwt.NimbusJwtDecoder.withJwkSetUri;
 | 
			
		||||
import static org.springframework.security.oauth2.jwt.NimbusJwtDecoder.withPublicKey;
 | 
			
		||||
import static org.springframework.security.oauth2.jwt.NimbusJwtDecoder.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests for {@link NimbusJwtDecoder}
 | 
			
		||||
 *
 | 
			
		||||
 * @author Josh Cummings
 | 
			
		||||
 * @author Joe Grandja
 | 
			
		||||
 */
 | 
			
		||||
public class NimbusJwtDecoderTests {
 | 
			
		||||
	private static final String JWK_SET = "{\"keys\":[{\"p\":\"49neceJFs8R6n7WamRGy45F5Tv0YM-R2ODK3eSBUSLOSH2tAqjEVKOkLE5fiNA3ygqq15NcKRadB2pTVf-Yb5ZIBuKzko8bzYIkIqYhSh_FAdEEr0vHF5fq_yWSvc6swsOJGqvBEtuqtJY027u-G2gAQasCQdhyejer68zsTn8M\",\"kty\":\"RSA\",\"q\":\"tWR-ysspjZ73B6p2vVRVyHwP3KQWL5KEQcdgcmMOE_P_cPs98vZJfLhxobXVmvzuEWBpRSiqiuyKlQnpstKt94Cy77iO8m8ISfF3C9VyLWXi9HUGAJb99irWABFl3sNDff5K2ODQ8CmuXLYM25OwN3ikbrhEJozlXg_NJFSGD4E\",\"d\":\"FkZHYZlw5KSoqQ1i2RA2kCUygSUOf1OqMt3uomtXuUmqKBm_bY7PCOhmwbvbn4xZYEeHuTR8Xix-0KpHe3NKyWrtRjkq1T_un49_1LLVUhJ0dL-9_x0xRquVjhl_XrsRXaGMEHs8G9pLTvXQ1uST585gxIfmCe0sxPZLvwoic-bXf64UZ9BGRV3lFexWJQqCZp2S21HfoU7wiz6kfLRNi-K4xiVNB1gswm_8o5lRuY7zB9bRARQ3TS2G4eW7p5sxT3CgsGiQD3_wPugU8iDplqAjgJ5ofNJXZezoj0t6JMB_qOpbrmAM1EnomIPebSLW7Ky9SugEd6KMdL5lW6AuAQ\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"one\",\"qi\":\"wdkFu_tV2V1l_PWUUimG516Zvhqk2SWDw1F7uNDD-Lvrv_WNRIJVzuffZ8WYiPy8VvYQPJUrT2EXL8P0ocqwlaSTuXctrORcbjwgxDQDLsiZE0C23HYzgi0cofbScsJdhcBg7d07LAf7cdJWG0YVl1FkMCsxUlZ2wTwHfKWf-v4\",\"dp\":\"uwnPxqC-IxG4r33-SIT02kZC1IqC4aY7PWq0nePiDEQMQWpjjNH50rlq9EyLzbtdRdIouo-jyQXB01K15-XXJJ60dwrGLYNVqfsTd0eGqD1scYJGHUWG9IDgCsxyEnuG3s0AwbW2UolWVSsU2xMZGb9PurIUZECeD1XDZwMp2s0\",\"dq\":\"hra786AunB8TF35h8PpROzPoE9VJJMuLrc6Esm8eZXMwopf0yhxfN2FEAvUoTpLJu93-UH6DKenCgi16gnQ0_zt1qNNIVoRfg4rw_rjmsxCYHTVL3-RDeC8X_7TsEySxW0EgFTHh-nr6I6CQrAJjPM88T35KHtdFATZ7BCBB8AE\",\"n\":\"oXJ8OyOv_eRnce4akdanR4KYRfnC2zLV4uYNQpcFn6oHL0dj7D6kxQmsXoYgJV8ZVDn71KGmuLvolxsDncc2UrhyMBY6DVQVgMSVYaPCTgW76iYEKGgzTEw5IBRQL9w3SRJWd3VJTZZQjkXef48Ocz06PGF3lhbz4t5UEZtdF4rIe7u-977QwHuh7yRPBQ3sII-cVoOUMgaXB9SHcGF2iZCtPzL_IffDUcfhLQteGebhW8A6eUHgpD5A1PQ-JCw_G7UOzZAjjDjtNM2eqm8j-Ms_gqnm4MiCZ4E-9pDN77CAAPVN7kuX6ejs9KBXpk01z48i9fORYk9u7rAkh1HuQw\"}]}";
 | 
			
		||||
| 
						 | 
				
			
			@ -217,11 +225,9 @@ public class NimbusJwtDecoderTests {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void jwsAlgorithmWhenNullOrEmptyThenThrowsException() {
 | 
			
		||||
	public void jwsAlgorithmWhenNullThenThrowsException() {
 | 
			
		||||
		NimbusJwtDecoder.JwkSetUriJwtDecoderBuilder builder = withJwkSetUri(JWK_SET_URI);
 | 
			
		||||
		Assertions.assertThatCode(() -> builder.jwsAlgorithm(null)).isInstanceOf(IllegalArgumentException.class);
 | 
			
		||||
		Assertions.assertThatCode(() -> builder.jwsAlgorithm("")).isInstanceOf(IllegalArgumentException.class);
 | 
			
		||||
		Assertions.assertThatCode(() -> builder.jwsAlgorithm("RS4096")).doesNotThrowAnyException();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
| 
						 | 
				
			
			@ -239,7 +245,7 @@ public class NimbusJwtDecoderTests {
 | 
			
		|||
	@Test
 | 
			
		||||
	public void buildWhenSignatureAlgorithmMismatchesKeyTypeThenThrowsException() {
 | 
			
		||||
		Assertions.assertThatCode(() -> withPublicKey(key())
 | 
			
		||||
				.jwsAlgorithm(JwsAlgorithms.ES256)
 | 
			
		||||
				.signatureAlgorithm(SignatureAlgorithm.ES256)
 | 
			
		||||
				.build())
 | 
			
		||||
				.isInstanceOf(IllegalStateException.class);
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -254,7 +260,7 @@ public class NimbusJwtDecoderTests {
 | 
			
		|||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void decodeWhenUsingPublicKeyWithRs512ThenSuccessfullyDecodes() throws Exception {
 | 
			
		||||
		NimbusJwtDecoder decoder = withPublicKey(key()).jwsAlgorithm(JwsAlgorithms.RS512).build();
 | 
			
		||||
		NimbusJwtDecoder decoder = withPublicKey(key()).signatureAlgorithm(SignatureAlgorithm.RS512).build();
 | 
			
		||||
		assertThat(decoder.decode(RS512_SIGNED_JWT))
 | 
			
		||||
				.extracting(Jwt::getSubject)
 | 
			
		||||
				.isEqualTo("test-subject");
 | 
			
		||||
| 
						 | 
				
			
			@ -262,17 +268,69 @@ public class NimbusJwtDecoderTests {
 | 
			
		|||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void decodeWhenSignatureMismatchesAlgorithmThenThrowsException() throws Exception {
 | 
			
		||||
		NimbusJwtDecoder decoder = withPublicKey(key()).jwsAlgorithm(JwsAlgorithms.RS512).build();
 | 
			
		||||
		NimbusJwtDecoder decoder = withPublicKey(key()).signatureAlgorithm(SignatureAlgorithm.RS512).build();
 | 
			
		||||
		Assertions.assertThatCode(() -> decoder.decode(RS256_SIGNED_JWT))
 | 
			
		||||
				.isInstanceOf(JwtException.class);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void withSecretKeyWhenNullThenThrowsIllegalArgumentException() {
 | 
			
		||||
		assertThatThrownBy(() -> withSecretKey(null))
 | 
			
		||||
				.isInstanceOf(IllegalArgumentException.class)
 | 
			
		||||
				.hasMessage("secretKey cannot be null");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void withSecretKeyWhenMacAlgorithmNullThenThrowsIllegalArgumentException() {
 | 
			
		||||
		SecretKey secretKey = TestKeys.DEFAULT_SECRET_KEY;
 | 
			
		||||
		assertThatThrownBy(() -> withSecretKey(secretKey).macAlgorithm(null))
 | 
			
		||||
				.isInstanceOf(IllegalArgumentException.class)
 | 
			
		||||
				.hasMessage("macAlgorithm cannot be null");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void decodeWhenUsingSecretKeyThenSuccessfullyDecodes() throws Exception {
 | 
			
		||||
		SecretKey secretKey = TestKeys.DEFAULT_SECRET_KEY;
 | 
			
		||||
		MacAlgorithm macAlgorithm = MacAlgorithm.HS256;
 | 
			
		||||
		JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
 | 
			
		||||
				.subject("test-subject")
 | 
			
		||||
				.expirationTime(Date.from(Instant.now().plusSeconds(60)))
 | 
			
		||||
				.build();
 | 
			
		||||
		SignedJWT signedJWT = signedJwt(secretKey, macAlgorithm, claimsSet);
 | 
			
		||||
		NimbusJwtDecoder decoder = withSecretKey(secretKey).macAlgorithm(macAlgorithm).build();
 | 
			
		||||
		assertThat(decoder.decode(signedJWT.serialize()))
 | 
			
		||||
				.extracting(Jwt::getSubject)
 | 
			
		||||
				.isEqualTo("test-subject");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void decodeWhenUsingSecretKeyAndIncorrectAlgorithmThenThrowsJwtException() throws Exception {
 | 
			
		||||
		SecretKey secretKey = TestKeys.DEFAULT_SECRET_KEY;
 | 
			
		||||
		MacAlgorithm macAlgorithm = MacAlgorithm.HS256;
 | 
			
		||||
		JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
 | 
			
		||||
				.subject("test-subject")
 | 
			
		||||
				.expirationTime(Date.from(Instant.now().plusSeconds(60)))
 | 
			
		||||
				.build();
 | 
			
		||||
		SignedJWT signedJWT = signedJwt(secretKey, macAlgorithm, claimsSet);
 | 
			
		||||
		NimbusJwtDecoder decoder = withSecretKey(secretKey).macAlgorithm(MacAlgorithm.HS512).build();
 | 
			
		||||
		assertThatThrownBy(() -> decoder.decode(signedJWT.serialize()))
 | 
			
		||||
				.isInstanceOf(JwtException.class)
 | 
			
		||||
				.hasMessage("An error occurred while attempting to decode the Jwt: Signed JWT rejected: Another algorithm expected, or no matching key(s) found");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private RSAPublicKey key() throws InvalidKeySpecException {
 | 
			
		||||
		byte[] decoded = Base64.getDecoder().decode(VERIFY_KEY.getBytes());
 | 
			
		||||
		EncodedKeySpec spec = new X509EncodedKeySpec(decoded);
 | 
			
		||||
		return (RSAPublicKey) kf.generatePublic(spec);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private SignedJWT signedJwt(SecretKey secretKey, MacAlgorithm jwsAlgorithm, JWTClaimsSet claimsSet) throws Exception {
 | 
			
		||||
		SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.parse(jwsAlgorithm.getName())), claimsSet);
 | 
			
		||||
		JWSSigner signer = new MACSigner(secretKey);
 | 
			
		||||
		signedJWT.sign(signer);
 | 
			
		||||
		return signedJWT;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static JWTProcessor<SecurityContext> withSigning(String jwkResponse) {
 | 
			
		||||
		RestOperations restOperations = mock(RestOperations.class);
 | 
			
		||||
		when(restOperations.exchange(any(RequestEntity.class), eq(String.class)))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,6 +16,31 @@
 | 
			
		|||
 | 
			
		||||
package org.springframework.security.oauth2.jwt;
 | 
			
		||||
 | 
			
		||||
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.jwt.JWTClaimsSet;
 | 
			
		||||
import com.nimbusds.jwt.SignedJWT;
 | 
			
		||||
import okhttp3.mockwebserver.MockResponse;
 | 
			
		||||
import okhttp3.mockwebserver.MockWebServer;
 | 
			
		||||
import org.junit.After;
 | 
			
		||||
import org.junit.Before;
 | 
			
		||||
import org.junit.BeforeClass;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
import org.springframework.core.convert.converter.Converter;
 | 
			
		||||
import org.springframework.security.oauth2.core.OAuth2Error;
 | 
			
		||||
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
 | 
			
		||||
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
 | 
			
		||||
import org.springframework.security.oauth2.jose.TestKeys;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
 | 
			
		||||
import org.springframework.web.reactive.function.client.WebClient;
 | 
			
		||||
import reactor.core.publisher.Flux;
 | 
			
		||||
import reactor.core.publisher.Mono;
 | 
			
		||||
 | 
			
		||||
import javax.crypto.SecretKey;
 | 
			
		||||
import java.net.UnknownHostException;
 | 
			
		||||
import java.security.KeyFactory;
 | 
			
		||||
import java.security.NoSuchAlgorithmException;
 | 
			
		||||
| 
						 | 
				
			
			@ -27,39 +52,19 @@ import java.text.ParseException;
 | 
			
		|||
import java.time.Instant;
 | 
			
		||||
import java.util.Base64;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import com.nimbusds.jose.jwk.JWKSet;
 | 
			
		||||
import okhttp3.mockwebserver.MockResponse;
 | 
			
		||||
import okhttp3.mockwebserver.MockWebServer;
 | 
			
		||||
import org.junit.After;
 | 
			
		||||
import org.junit.Before;
 | 
			
		||||
import org.junit.BeforeClass;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
import reactor.core.publisher.Flux;
 | 
			
		||||
import reactor.core.publisher.Mono;
 | 
			
		||||
 | 
			
		||||
import org.springframework.core.convert.converter.Converter;
 | 
			
		||||
import org.springframework.security.oauth2.core.OAuth2Error;
 | 
			
		||||
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
 | 
			
		||||
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
 | 
			
		||||
import org.springframework.web.reactive.function.client.WebClient;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThatCode;
 | 
			
		||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
 | 
			
		||||
import static org.mockito.ArgumentMatchers.any;
 | 
			
		||||
import static org.mockito.Mockito.mock;
 | 
			
		||||
import static org.mockito.Mockito.spy;
 | 
			
		||||
import static org.mockito.Mockito.verify;
 | 
			
		||||
import static org.mockito.Mockito.when;
 | 
			
		||||
import static org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder.withJwkSetUri;
 | 
			
		||||
import static org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder.withJwkSource;
 | 
			
		||||
import static org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder.withPublicKey;
 | 
			
		||||
import static org.mockito.Mockito.*;
 | 
			
		||||
import static org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author Rob Winch
 | 
			
		||||
 * @author Joe Grandja
 | 
			
		||||
 * @since 5.1
 | 
			
		||||
 */
 | 
			
		||||
public class NimbusReactiveJwtDecoderTests {
 | 
			
		||||
| 
						 | 
				
			
			@ -236,11 +241,9 @@ public class NimbusReactiveJwtDecoderTests {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void jwsAlgorithmWhenNullOrEmptyThenThrowsException() {
 | 
			
		||||
	public void jwsAlgorithmWhenNullThenThrowsException() {
 | 
			
		||||
		NimbusReactiveJwtDecoder.JwkSetUriReactiveJwtDecoderBuilder builder = withJwkSetUri(this.jwkSetUri);
 | 
			
		||||
		assertThatCode(() -> builder.jwsAlgorithm(null)).isInstanceOf(IllegalArgumentException.class);
 | 
			
		||||
		assertThatCode(() -> builder.jwsAlgorithm("")).isInstanceOf(IllegalArgumentException.class);
 | 
			
		||||
		assertThatCode(() -> builder.jwsAlgorithm("RS4096")).doesNotThrowAnyException();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
| 
						 | 
				
			
			@ -269,7 +272,7 @@ public class NimbusReactiveJwtDecoderTests {
 | 
			
		|||
	@Test
 | 
			
		||||
	public void buildWhenSignatureAlgorithmMismatchesKeyTypeThenThrowsException() {
 | 
			
		||||
		assertThatCode(() -> withPublicKey(key())
 | 
			
		||||
				.jwsAlgorithm(JwsAlgorithms.ES256)
 | 
			
		||||
				.signatureAlgorithm(SignatureAlgorithm.ES256)
 | 
			
		||||
				.build())
 | 
			
		||||
				.isInstanceOf(IllegalStateException.class);
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -285,7 +288,7 @@ public class NimbusReactiveJwtDecoderTests {
 | 
			
		|||
	@Test
 | 
			
		||||
	public void decodeWhenUsingPublicKeyWithRs512ThenSuccessfullyDecodes() throws Exception {
 | 
			
		||||
		NimbusReactiveJwtDecoder decoder =
 | 
			
		||||
				withPublicKey(key()).jwsAlgorithm(JwsAlgorithms.RS512).build();
 | 
			
		||||
				withPublicKey(key()).signatureAlgorithm(SignatureAlgorithm.RS512).build();
 | 
			
		||||
		assertThat(decoder.decode(this.rsa512).block())
 | 
			
		||||
				.extracting(Jwt::getSubject)
 | 
			
		||||
				.isEqualTo("test-subject");
 | 
			
		||||
| 
						 | 
				
			
			@ -294,7 +297,7 @@ public class NimbusReactiveJwtDecoderTests {
 | 
			
		|||
	@Test
 | 
			
		||||
	public void decodeWhenSignatureMismatchesAlgorithmThenThrowsException() throws Exception {
 | 
			
		||||
		NimbusReactiveJwtDecoder decoder =
 | 
			
		||||
				withPublicKey(key()).jwsAlgorithm(JwsAlgorithms.RS512).build();
 | 
			
		||||
				withPublicKey(key()).signatureAlgorithm(SignatureAlgorithm.RS512).build();
 | 
			
		||||
		assertThatCode(() -> decoder.decode(this.rsa256).block())
 | 
			
		||||
				.isInstanceOf(JwtException.class);
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -316,6 +319,58 @@ public class NimbusReactiveJwtDecoderTests {
 | 
			
		|||
				.isNotNull();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void withSecretKeyWhenSecretKeyNullThenThrowsIllegalArgumentException() {
 | 
			
		||||
		assertThatThrownBy(() -> withSecretKey(null))
 | 
			
		||||
				.isInstanceOf(IllegalArgumentException.class)
 | 
			
		||||
				.hasMessage("secretKey cannot be null");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void withSecretKeyWhenMacAlgorithmNullThenThrowsIllegalArgumentException() {
 | 
			
		||||
		SecretKey secretKey = TestKeys.DEFAULT_SECRET_KEY;
 | 
			
		||||
		assertThatThrownBy(() -> withSecretKey(secretKey).macAlgorithm(null))
 | 
			
		||||
				.isInstanceOf(IllegalArgumentException.class)
 | 
			
		||||
				.hasMessage("macAlgorithm cannot be null");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void decodeWhenSecretKeyThenSuccess() throws Exception {
 | 
			
		||||
		SecretKey secretKey = TestKeys.DEFAULT_SECRET_KEY;
 | 
			
		||||
		MacAlgorithm macAlgorithm = MacAlgorithm.HS256;
 | 
			
		||||
		JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
 | 
			
		||||
				.subject("test-subject")
 | 
			
		||||
				.expirationTime(Date.from(Instant.now().plusSeconds(60)))
 | 
			
		||||
				.build();
 | 
			
		||||
		SignedJWT signedJWT = signedJwt(secretKey, macAlgorithm, claimsSet);
 | 
			
		||||
 | 
			
		||||
		this.decoder = withSecretKey(secretKey).macAlgorithm(macAlgorithm).build();
 | 
			
		||||
		Jwt jwt = this.decoder.decode(signedJWT.serialize()).block();
 | 
			
		||||
		assertThat(jwt.getSubject()).isEqualTo("test-subject");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void decodeWhenSecretKeyAndAlgorithmMismatchThenThrowsJwtException() throws Exception {
 | 
			
		||||
		SecretKey secretKey = TestKeys.DEFAULT_SECRET_KEY;
 | 
			
		||||
		MacAlgorithm macAlgorithm = MacAlgorithm.HS256;
 | 
			
		||||
		JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
 | 
			
		||||
				.subject("test-subject")
 | 
			
		||||
				.expirationTime(Date.from(Instant.now().plusSeconds(60)))
 | 
			
		||||
				.build();
 | 
			
		||||
		SignedJWT signedJWT = signedJwt(secretKey, macAlgorithm, claimsSet);
 | 
			
		||||
 | 
			
		||||
		this.decoder = withSecretKey(secretKey).macAlgorithm(MacAlgorithm.HS512).build();
 | 
			
		||||
		assertThatThrownBy(() -> this.decoder.decode(signedJWT.serialize()).block())
 | 
			
		||||
				.isInstanceOf(JwtException.class);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private SignedJWT signedJwt(SecretKey secretKey, MacAlgorithm jwsAlgorithm, JWTClaimsSet claimsSet) throws Exception {
 | 
			
		||||
		SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.parse(jwsAlgorithm.getName())), claimsSet);
 | 
			
		||||
		JWSSigner signer = new MACSigner(secretKey);
 | 
			
		||||
		signedJWT.sign(signer);
 | 
			
		||||
		return signedJWT;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private JWKSet parseJWKSet(String jwkSet) {
 | 
			
		||||
		try {
 | 
			
		||||
			return JWKSet.parse(jwkSet);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue