Polish JWT Signature Algorithm Discovery
- Moved support to JwtDecoders and ReactiveJwtDecoders since there is already the expectation that those classes make an outbound connection to complete configuration. Since there's no outbound connection when configuring a NimbusJwtDecoder or NimbusReactiveJwtDecoder, it would be more intrusive to change that. Closes gh-7160
This commit is contained in:
		
							parent
							
								
									290786438c
								
							
						
					
					
						commit
						366146ff80
					
				| 
						 | 
				
			
			@ -223,7 +223,6 @@ public class OAuth2ResourceServerConfigurerTests {
 | 
			
		|||
	public void getWhenUsingJwkSetUriThenAcceptsRequest() throws Exception {
 | 
			
		||||
		this.spring.register(WebServerConfig.class, JwkSetUriConfig.class, BasicController.class).autowire();
 | 
			
		||||
		mockWebServer(jwks("Default"));
 | 
			
		||||
		mockWebServer(jwks("Default"));
 | 
			
		||||
		String token = this.token("ValidNoScopes");
 | 
			
		||||
		// @formatter:off
 | 
			
		||||
		this.mvc.perform(get("/").with(bearerToken(token)))
 | 
			
		||||
| 
						 | 
				
			
			@ -236,7 +235,6 @@ public class OAuth2ResourceServerConfigurerTests {
 | 
			
		|||
	public void getWhenUsingJwkSetUriInLambdaThenAcceptsRequest() throws Exception {
 | 
			
		||||
		this.spring.register(WebServerConfig.class, JwkSetUriInLambdaConfig.class, BasicController.class).autowire();
 | 
			
		||||
		mockWebServer(jwks("Default"));
 | 
			
		||||
		mockWebServer(jwks("Default"));
 | 
			
		||||
		String token = this.token("ValidNoScopes");
 | 
			
		||||
		// @formatter:off
 | 
			
		||||
		this.mvc.perform(get("/").with(bearerToken(token)))
 | 
			
		||||
| 
						 | 
				
			
			@ -1203,6 +1201,7 @@ public class OAuth2ResourceServerConfigurerTests {
 | 
			
		|||
		// @formatter:on
 | 
			
		||||
		mockWebServer(String.format(metadata, issuerThree, issuerThree));
 | 
			
		||||
		mockWebServer(jwkSet);
 | 
			
		||||
		mockWebServer(jwkSet);
 | 
			
		||||
		// @formatter:off
 | 
			
		||||
		this.mvc.perform(get("/authenticated").with(bearerToken(jwtThree)))
 | 
			
		||||
				.andExpect(status().isUnauthorized())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -148,7 +148,6 @@ public class OAuth2ResourceServerBeanDefinitionParserTests {
 | 
			
		|||
	public void getWhenUsingJwkSetUriThenAcceptsRequest() throws Exception {
 | 
			
		||||
		this.spring.configLocations(xml("WebServer"), xml("JwkSetUri")).autowire();
 | 
			
		||||
		mockWebServer(jwks("Default"));
 | 
			
		||||
		mockWebServer(jwks("Default"));
 | 
			
		||||
		String token = this.token("ValidNoScopes");
 | 
			
		||||
		// @formatter:off
 | 
			
		||||
		this.mvc.perform(get("/").header("Authorization", "Bearer " + token))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -261,7 +261,6 @@ public class OAuth2ResourceServerSpecTests {
 | 
			
		|||
		this.spring.register(JwkSetUriConfig.class, RootController.class).autowire();
 | 
			
		||||
		MockWebServer mockWebServer = this.spring.getContext().getBean(MockWebServer.class);
 | 
			
		||||
		mockWebServer.enqueue(new MockResponse().setBody(this.jwkSet));
 | 
			
		||||
		mockWebServer.enqueue(new MockResponse().setBody(this.jwkSet));
 | 
			
		||||
		// @formatter:off
 | 
			
		||||
		this.client.get()
 | 
			
		||||
				.headers((headers) -> headers
 | 
			
		||||
| 
						 | 
				
			
			@ -277,7 +276,6 @@ public class OAuth2ResourceServerSpecTests {
 | 
			
		|||
		this.spring.register(JwkSetUriInLambdaConfig.class, RootController.class).autowire();
 | 
			
		||||
		MockWebServer mockWebServer = this.spring.getContext().getBean(MockWebServer.class);
 | 
			
		||||
		mockWebServer.enqueue(new MockResponse().setBody(this.jwkSet));
 | 
			
		||||
		mockWebServer.enqueue(new MockResponse().setBody(this.jwkSet));
 | 
			
		||||
		// @formatter:off
 | 
			
		||||
		this.client.get()
 | 
			
		||||
				.headers((headers) -> headers
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -160,7 +160,6 @@ class ServerJwtDslTests {
 | 
			
		|||
    fun `jwt when using custom JWK Set URI then custom URI used`() {
 | 
			
		||||
        this.spring.register(CustomJwkSetUriConfig::class.java).autowire()
 | 
			
		||||
 | 
			
		||||
        CustomJwkSetUriConfig.MOCK_WEB_SERVER.enqueue(MockResponse().setBody(jwkSet))
 | 
			
		||||
        CustomJwkSetUriConfig.MOCK_WEB_SERVER.enqueue(MockResponse().setBody(jwkSet))
 | 
			
		||||
 | 
			
		||||
        this.client.get()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -99,9 +99,10 @@ When this property and these dependencies are used, Resource Server will automat
 | 
			
		|||
 | 
			
		||||
It achieves this through a deterministic startup process:
 | 
			
		||||
 | 
			
		||||
1. Hit the Provider Configuration or Authorization Server Metadata endpoint, processing the response for the `jwks_url` property
 | 
			
		||||
2. Configure the validation strategy to query `jwks_url` for valid public keys
 | 
			
		||||
3. Configure the validation strategy to validate each JWTs `iss` claim against `https://idp.example.com`.
 | 
			
		||||
1. Query the Provider Configuration or Authorization Server Metadata endpoint for the `jwks_url` property
 | 
			
		||||
2. Query the `jwks_url` endpoint for supported algorithms
 | 
			
		||||
3. Configure the validation strategy to query `jwks_url` for valid public keys of the algorithms found
 | 
			
		||||
4. Configure the validation strategy to validate each JWTs `iss` claim against `https://idp.example.com`.
 | 
			
		||||
 | 
			
		||||
A consequence of this process is that the authorization server must be up and receiving requests in order for Resource Server to successfully start up.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,11 +18,25 @@ package org.springframework.security.oauth2.jwt;
 | 
			
		|||
 | 
			
		||||
import java.net.URI;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import com.nimbusds.jose.JWSAlgorithm;
 | 
			
		||||
import com.nimbusds.jose.KeySourceException;
 | 
			
		||||
import com.nimbusds.jose.jwk.JWK;
 | 
			
		||||
import com.nimbusds.jose.jwk.JWKMatcher;
 | 
			
		||||
import com.nimbusds.jose.jwk.JWKSelector;
 | 
			
		||||
import com.nimbusds.jose.jwk.KeyType;
 | 
			
		||||
import com.nimbusds.jose.jwk.KeyUse;
 | 
			
		||||
import com.nimbusds.jose.jwk.source.JWKSource;
 | 
			
		||||
import com.nimbusds.jose.proc.SecurityContext;
 | 
			
		||||
 | 
			
		||||
import org.springframework.core.ParameterizedTypeReference;
 | 
			
		||||
import org.springframework.http.RequestEntity;
 | 
			
		||||
import org.springframework.http.ResponseEntity;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
 | 
			
		||||
import org.springframework.util.Assert;
 | 
			
		||||
import org.springframework.web.client.HttpClientErrorException;
 | 
			
		||||
import org.springframework.web.client.RestTemplate;
 | 
			
		||||
| 
						 | 
				
			
			@ -68,6 +82,40 @@ final class JwtDecoderProviderConfigurationUtils {
 | 
			
		|||
				+ "\" provided in the configuration did not " + "match the requested issuer \"" + issuer + "\"");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static Set<SignatureAlgorithm> getSignatureAlgorithms(JWKSource<SecurityContext> jwkSource) {
 | 
			
		||||
		JWKMatcher jwkMatcher = new JWKMatcher.Builder().publicOnly(true).keyUses(KeyUse.SIGNATURE, null)
 | 
			
		||||
				.keyTypes(KeyType.RSA, KeyType.EC).build();
 | 
			
		||||
		Set<JWSAlgorithm> jwsAlgorithms = new HashSet<>();
 | 
			
		||||
		try {
 | 
			
		||||
			List<? extends JWK> jwks = jwkSource.get(new JWKSelector(jwkMatcher), null);
 | 
			
		||||
			for (JWK jwk : jwks) {
 | 
			
		||||
				if (jwk.getAlgorithm() != null) {
 | 
			
		||||
					jwsAlgorithms.add((JWSAlgorithm) jwk.getAlgorithm());
 | 
			
		||||
				}
 | 
			
		||||
				else {
 | 
			
		||||
					if (jwk.getKeyType() == KeyType.RSA) {
 | 
			
		||||
						jwsAlgorithms.addAll(JWSAlgorithm.Family.RSA);
 | 
			
		||||
					}
 | 
			
		||||
					else if (jwk.getKeyType() == KeyType.EC) {
 | 
			
		||||
						jwsAlgorithms.addAll(JWSAlgorithm.Family.EC);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		catch (KeySourceException ex) {
 | 
			
		||||
			throw new IllegalStateException(ex);
 | 
			
		||||
		}
 | 
			
		||||
		Set<SignatureAlgorithm> signatureAlgorithms = new HashSet<>();
 | 
			
		||||
		for (JWSAlgorithm jwsAlgorithm : jwsAlgorithms) {
 | 
			
		||||
			SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.from(jwsAlgorithm.getName());
 | 
			
		||||
			if (signatureAlgorithm != null) {
 | 
			
		||||
				signatureAlgorithms.add(signatureAlgorithm);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		Assert.notEmpty(signatureAlgorithms, "Failed to find any algorithms from the JWK set");
 | 
			
		||||
		return signatureAlgorithms;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static String getMetadataIssuer(Map<String, Object> configuration) {
 | 
			
		||||
		if (configuration.containsKey("issuer")) {
 | 
			
		||||
			return configuration.get("issuer").toString();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,9 +16,17 @@
 | 
			
		|||
 | 
			
		||||
package org.springframework.security.oauth2.jwt;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.UncheckedIOException;
 | 
			
		||||
import java.net.URL;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import com.nimbusds.jose.jwk.source.RemoteJWKSet;
 | 
			
		||||
import com.nimbusds.jose.proc.SecurityContext;
 | 
			
		||||
 | 
			
		||||
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
 | 
			
		||||
import org.springframework.util.Assert;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -106,9 +114,23 @@ public final class JwtDecoders {
 | 
			
		|||
	private static JwtDecoder withProviderConfiguration(Map<String, Object> configuration, String issuer) {
 | 
			
		||||
		JwtDecoderProviderConfigurationUtils.validateIssuer(configuration, issuer);
 | 
			
		||||
		OAuth2TokenValidator<Jwt> jwtValidator = JwtValidators.createDefaultWithIssuer(issuer);
 | 
			
		||||
		NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(configuration.get("jwks_uri").toString()).build();
 | 
			
		||||
		String jwkSetUri = configuration.get("jwks_uri").toString();
 | 
			
		||||
		RemoteJWKSet<SecurityContext> jwkSource = new RemoteJWKSet<>(url(jwkSetUri));
 | 
			
		||||
		Set<SignatureAlgorithm> signatureAlgorithms = JwtDecoderProviderConfigurationUtils
 | 
			
		||||
				.getSignatureAlgorithms(jwkSource);
 | 
			
		||||
		NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri)
 | 
			
		||||
				.jwsAlgorithms((algs) -> algs.addAll(signatureAlgorithms)).build();
 | 
			
		||||
		jwtDecoder.setJwtValidator(jwtValidator);
 | 
			
		||||
		return jwtDecoder;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static URL url(String url) {
 | 
			
		||||
		try {
 | 
			
		||||
			return new URL(url);
 | 
			
		||||
		}
 | 
			
		||||
		catch (IOException ex) {
 | 
			
		||||
			throw new UncheckedIOException(ex);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,26 +21,21 @@ import java.net.MalformedURLException;
 | 
			
		|||
import java.net.URL;
 | 
			
		||||
import java.security.interfaces.RSAPublicKey;
 | 
			
		||||
import java.text.ParseException;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.LinkedHashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.function.Consumer;
 | 
			
		||||
 | 
			
		||||
import javax.crypto.SecretKey;
 | 
			
		||||
 | 
			
		||||
import com.nimbusds.jose.Algorithm;
 | 
			
		||||
import com.nimbusds.jose.JOSEException;
 | 
			
		||||
import com.nimbusds.jose.JWSAlgorithm;
 | 
			
		||||
import com.nimbusds.jose.RemoteKeySourceException;
 | 
			
		||||
import com.nimbusds.jose.jwk.JWK;
 | 
			
		||||
import com.nimbusds.jose.jwk.JWKSet;
 | 
			
		||||
import com.nimbusds.jose.jwk.KeyUse;
 | 
			
		||||
import com.nimbusds.jose.jwk.source.JWKSetCache;
 | 
			
		||||
import com.nimbusds.jose.jwk.source.JWKSource;
 | 
			
		||||
import com.nimbusds.jose.jwk.source.RemoteJWKSet;
 | 
			
		||||
| 
						 | 
				
			
			@ -239,8 +234,6 @@ public final class NimbusJwtDecoder implements JwtDecoder {
 | 
			
		|||
	 */
 | 
			
		||||
	public static final class JwkSetUriJwtDecoderBuilder {
 | 
			
		||||
 | 
			
		||||
		private static final Log log = LogFactory.getLog(JwkSetUriJwtDecoderBuilder.class);
 | 
			
		||||
 | 
			
		||||
		private String jwkSetUri;
 | 
			
		||||
 | 
			
		||||
		private Set<SignatureAlgorithm> signatureAlgorithms = new HashSet<>();
 | 
			
		||||
| 
						 | 
				
			
			@ -329,60 +322,17 @@ public final class NimbusJwtDecoder implements JwtDecoder {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		JWSKeySelector<SecurityContext> jwsKeySelector(JWKSource<SecurityContext> jwkSource) {
 | 
			
		||||
			Set<SignatureAlgorithm> algorithms = new HashSet<>();
 | 
			
		||||
			if (!this.signatureAlgorithms.isEmpty()) {
 | 
			
		||||
				algorithms.addAll(this.signatureAlgorithms);
 | 
			
		||||
			} else {
 | 
			
		||||
				algorithms.addAll(fetchSignatureAlgorithms());
 | 
			
		||||
			if (this.signatureAlgorithms.isEmpty()) {
 | 
			
		||||
				return new JWSVerificationKeySelector<>(JWSAlgorithm.RS256, jwkSource);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (algorithms.isEmpty()) {
 | 
			
		||||
				algorithms.add(SignatureAlgorithm.RS256);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			Set<JWSAlgorithm> jwsAlgorithms = new HashSet<>();
 | 
			
		||||
			for (SignatureAlgorithm signatureAlgorithm : algorithms) {
 | 
			
		||||
				jwsAlgorithms.add(JWSAlgorithm.parse(signatureAlgorithm.getName()));
 | 
			
		||||
			for (SignatureAlgorithm signatureAlgorithm : this.signatureAlgorithms) {
 | 
			
		||||
				JWSAlgorithm jwsAlgorithm = JWSAlgorithm.parse(signatureAlgorithm.getName());
 | 
			
		||||
				jwsAlgorithms.add(jwsAlgorithm);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return new JWSVerificationKeySelector<>(jwsAlgorithms, jwkSource);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private Set<SignatureAlgorithm> fetchSignatureAlgorithms() {
 | 
			
		||||
			try {
 | 
			
		||||
				return parseAlgorithms(JWKSet.load(toURL(jwkSetUri), 5000, 5000, 0));
 | 
			
		||||
			} catch (Exception ex) {
 | 
			
		||||
				throw new IllegalArgumentException("Failed to load Signature Algorithms from remote JWK source.", ex);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private Set<SignatureAlgorithm> parseAlgorithms(JWKSet jwkSet) {
 | 
			
		||||
			if (jwkSet == null) {
 | 
			
		||||
				throw new IllegalArgumentException(String.format("No JWKs received from %s", jwkSetUri));
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			List<JWK> jwks = new ArrayList<>();
 | 
			
		||||
			for (JWK jwk : jwkSet.getKeys()) {
 | 
			
		||||
				KeyUse keyUse = jwk.getKeyUse();
 | 
			
		||||
				if (keyUse != null && keyUse.equals(KeyUse.SIGNATURE)) {
 | 
			
		||||
					jwks.add(jwk);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			Set<SignatureAlgorithm> algorithms = new HashSet<>();
 | 
			
		||||
			for (JWK jwk : jwks) {
 | 
			
		||||
				Algorithm algorithm = jwk.getAlgorithm();
 | 
			
		||||
				if (algorithm != null) {
 | 
			
		||||
					SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.from(algorithm.getName());
 | 
			
		||||
					if (signatureAlgorithm != null) {
 | 
			
		||||
						algorithms.add(signatureAlgorithm);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return algorithms;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		JWKSource<SecurityContext> jwkSource(ResourceRetriever jwkSetRetriever) {
 | 
			
		||||
			if (this.cache == null) {
 | 
			
		||||
				return new RemoteJWKSet<>(toURL(this.jwkSetUri), jwkSetRetriever);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,14 +16,11 @@
 | 
			
		|||
 | 
			
		||||
package org.springframework.security.oauth2.jwt;
 | 
			
		||||
 | 
			
		||||
import java.net.MalformedURLException;
 | 
			
		||||
import java.net.URL;
 | 
			
		||||
import java.security.interfaces.RSAPublicKey;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.LinkedHashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.function.Consumer;
 | 
			
		||||
| 
						 | 
				
			
			@ -31,7 +28,6 @@ import java.util.function.Function;
 | 
			
		|||
 | 
			
		||||
import javax.crypto.SecretKey;
 | 
			
		||||
 | 
			
		||||
import com.nimbusds.jose.Algorithm;
 | 
			
		||||
import com.nimbusds.jose.Header;
 | 
			
		||||
import com.nimbusds.jose.JOSEException;
 | 
			
		||||
import com.nimbusds.jose.JWSAlgorithm;
 | 
			
		||||
| 
						 | 
				
			
			@ -39,8 +35,6 @@ import com.nimbusds.jose.JWSHeader;
 | 
			
		|||
import com.nimbusds.jose.jwk.JWK;
 | 
			
		||||
import com.nimbusds.jose.jwk.JWKMatcher;
 | 
			
		||||
import com.nimbusds.jose.jwk.JWKSelector;
 | 
			
		||||
import com.nimbusds.jose.jwk.JWKSet;
 | 
			
		||||
import com.nimbusds.jose.jwk.KeyUse;
 | 
			
		||||
import com.nimbusds.jose.jwk.source.JWKSecurityContextJWKSet;
 | 
			
		||||
import com.nimbusds.jose.jwk.source.JWKSource;
 | 
			
		||||
import com.nimbusds.jose.proc.BadJOSEException;
 | 
			
		||||
| 
						 | 
				
			
			@ -56,8 +50,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.apache.commons.logging.Log;
 | 
			
		||||
import org.apache.commons.logging.LogFactory;
 | 
			
		||||
import reactor.core.publisher.Flux;
 | 
			
		||||
import reactor.core.publisher.Mono;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -281,8 +273,6 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
 | 
			
		|||
	 */
 | 
			
		||||
	public static final class JwkSetUriReactiveJwtDecoderBuilder {
 | 
			
		||||
 | 
			
		||||
		private static final Log log = LogFactory.getLog(JwkSetUriReactiveJwtDecoderBuilder.class);
 | 
			
		||||
 | 
			
		||||
		private final String jwkSetUri;
 | 
			
		||||
 | 
			
		||||
		private Set<SignatureAlgorithm> signatureAlgorithms = new HashSet<>();
 | 
			
		||||
| 
						 | 
				
			
			@ -364,63 +354,17 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		JWSKeySelector<JWKSecurityContext> jwsKeySelector(JWKSource<JWKSecurityContext> jwkSource) {
 | 
			
		||||
			Set<SignatureAlgorithm> algorithms = new HashSet<>();
 | 
			
		||||
			if (!this.signatureAlgorithms.isEmpty()) {
 | 
			
		||||
				algorithms.addAll(this.signatureAlgorithms);
 | 
			
		||||
			} else {
 | 
			
		||||
				algorithms.addAll(fetchSignatureAlgorithms());
 | 
			
		||||
			if (this.signatureAlgorithms.isEmpty()) {
 | 
			
		||||
				return new JWSVerificationKeySelector<>(JWSAlgorithm.RS256, jwkSource);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (algorithms.isEmpty()) {
 | 
			
		||||
				algorithms.add(SignatureAlgorithm.RS256);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			Set<JWSAlgorithm> jwsAlgorithms = new HashSet<>();
 | 
			
		||||
			for (SignatureAlgorithm signatureAlgorithm : algorithms) {
 | 
			
		||||
				jwsAlgorithms.add(JWSAlgorithm.parse(signatureAlgorithm.getName()));
 | 
			
		||||
			for (SignatureAlgorithm signatureAlgorithm : this.signatureAlgorithms) {
 | 
			
		||||
				JWSAlgorithm jwsAlgorithm = JWSAlgorithm.parse(signatureAlgorithm.getName());
 | 
			
		||||
				jwsAlgorithms.add(jwsAlgorithm);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return new JWSVerificationKeySelector<>(jwsAlgorithms, jwkSource);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private Set<SignatureAlgorithm> fetchSignatureAlgorithms() {
 | 
			
		||||
			if (StringUtils.isEmpty(jwkSetUri)) {
 | 
			
		||||
				return Collections.emptySet();
 | 
			
		||||
			}
 | 
			
		||||
			try {
 | 
			
		||||
				return parseAlgorithms(JWKSet.load(toURL(jwkSetUri), 5000, 5000, 0));
 | 
			
		||||
			} catch (Exception ex) {
 | 
			
		||||
				throw new IllegalArgumentException("Failed to load Signature Algorithms from remote JWK source.", ex);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private Set<SignatureAlgorithm> parseAlgorithms(JWKSet jwkSet) {
 | 
			
		||||
			if (jwkSet == null) {
 | 
			
		||||
				throw new IllegalArgumentException(String.format("No JWKs received from %s", jwkSetUri));
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			List<JWK> jwks = new ArrayList<>();
 | 
			
		||||
			for (JWK jwk : jwkSet.getKeys()) {
 | 
			
		||||
				KeyUse keyUse = jwk.getKeyUse();
 | 
			
		||||
				if (keyUse != null && keyUse.equals(KeyUse.SIGNATURE)) {
 | 
			
		||||
					jwks.add(jwk);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			Set<SignatureAlgorithm> algorithms = new HashSet<>();
 | 
			
		||||
			for (JWK jwk : jwks) {
 | 
			
		||||
				Algorithm algorithm = jwk.getAlgorithm();
 | 
			
		||||
				if (algorithm != null) {
 | 
			
		||||
					SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.from(algorithm.getName());
 | 
			
		||||
					if (signatureAlgorithm != null) {
 | 
			
		||||
						algorithms.add(signatureAlgorithm);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return algorithms;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Converter<JWT, Mono<JWTClaimsSet>> processor() {
 | 
			
		||||
			JWKSecurityContextJWKSet jwkSource = new JWKSecurityContextJWKSet();
 | 
			
		||||
			DefaultJWTProcessor<JWKSecurityContext> jwtProcessor = new DefaultJWTProcessor<>();
 | 
			
		||||
| 
						 | 
				
			
			@ -455,13 +399,6 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
 | 
			
		|||
			return new JWKSelector(JWKMatcher.forJWSHeader(jwsHeader));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private static URL toURL(String url) {
 | 
			
		||||
			try {
 | 
			
		||||
				return new URL(url);
 | 
			
		||||
			} catch (MalformedURLException ex) {
 | 
			
		||||
				throw new IllegalArgumentException("Invalid JWK Set URL \"" + url + "\" : " + ex.getMessage(), ex);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,9 +16,17 @@
 | 
			
		|||
 | 
			
		||||
package org.springframework.security.oauth2.jwt;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.UncheckedIOException;
 | 
			
		||||
import java.net.URL;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import com.nimbusds.jose.jwk.source.RemoteJWKSet;
 | 
			
		||||
import com.nimbusds.jose.proc.SecurityContext;
 | 
			
		||||
 | 
			
		||||
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
 | 
			
		||||
import org.springframework.util.Assert;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -106,10 +114,23 @@ public final class ReactiveJwtDecoders {
 | 
			
		|||
	private static ReactiveJwtDecoder withProviderConfiguration(Map<String, Object> configuration, String issuer) {
 | 
			
		||||
		JwtDecoderProviderConfigurationUtils.validateIssuer(configuration, issuer);
 | 
			
		||||
		OAuth2TokenValidator<Jwt> jwtValidator = JwtValidators.createDefaultWithIssuer(issuer);
 | 
			
		||||
		NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder
 | 
			
		||||
				.withJwkSetUri(configuration.get("jwks_uri").toString()).build();
 | 
			
		||||
		String jwkSetUri = configuration.get("jwks_uri").toString();
 | 
			
		||||
		RemoteJWKSet<SecurityContext> jwkSource = new RemoteJWKSet<>(url(jwkSetUri));
 | 
			
		||||
		Set<SignatureAlgorithm> signatureAlgorithms = JwtDecoderProviderConfigurationUtils
 | 
			
		||||
				.getSignatureAlgorithms(jwkSource);
 | 
			
		||||
		NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri)
 | 
			
		||||
				.jwsAlgorithms((algs) -> algs.addAll(signatureAlgorithms)).build();
 | 
			
		||||
		jwtDecoder.setJwtValidator(jwtValidator);
 | 
			
		||||
		return jwtDecoder;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static URL url(String url) {
 | 
			
		||||
		try {
 | 
			
		||||
			return new URL(url);
 | 
			
		||||
		}
 | 
			
		||||
		catch (IOException ex) {
 | 
			
		||||
			throw new UncheckedIOException(ex);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,76 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2002-2020 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      https://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.springframework.security.oauth2.jwt;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import com.nimbusds.jose.JWSAlgorithm;
 | 
			
		||||
import com.nimbusds.jose.jwk.Curve;
 | 
			
		||||
import com.nimbusds.jose.jwk.ECKey;
 | 
			
		||||
import com.nimbusds.jose.jwk.JWKSelector;
 | 
			
		||||
import com.nimbusds.jose.jwk.KeyUse;
 | 
			
		||||
import com.nimbusds.jose.jwk.RSAKey;
 | 
			
		||||
import com.nimbusds.jose.jwk.source.JWKSource;
 | 
			
		||||
import com.nimbusds.jose.proc.SecurityContext;
 | 
			
		||||
import com.nimbusds.jose.util.Base64URL;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
 | 
			
		||||
import org.springframework.security.oauth2.jose.TestKeys;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
 | 
			
		||||
import static org.mockito.ArgumentMatchers.any;
 | 
			
		||||
import static org.mockito.ArgumentMatchers.isNull;
 | 
			
		||||
import static org.mockito.BDDMockito.mock;
 | 
			
		||||
import static org.mockito.BDDMockito.when;
 | 
			
		||||
 | 
			
		||||
public class JwtDecoderProviderConfigurationUtilsTests {
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void getSignatureAlgorithmsWhenJwkSetSpecifiesAlgorithmThenUses() throws Exception {
 | 
			
		||||
		JWKSource<SecurityContext> jwkSource = mock(JWKSource.class);
 | 
			
		||||
		RSAKey key = new RSAKey.Builder(TestKeys.DEFAULT_PUBLIC_KEY).keyUse(KeyUse.SIGNATURE)
 | 
			
		||||
				.algorithm(JWSAlgorithm.RS384).build();
 | 
			
		||||
		when(jwkSource.get(any(JWKSelector.class), isNull())).thenReturn(Collections.singletonList(key));
 | 
			
		||||
		Set<SignatureAlgorithm> algorithms = JwtDecoderProviderConfigurationUtils.getSignatureAlgorithms(jwkSource);
 | 
			
		||||
		assertThat(algorithms).containsOnly(SignatureAlgorithm.RS384);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void getSignatureAlgorithmsWhenJwkSetIsEmptyThenIllegalArgumentException() throws Exception {
 | 
			
		||||
		JWKSource<SecurityContext> jwkSource = mock(JWKSource.class);
 | 
			
		||||
		when(jwkSource.get(any(JWKSelector.class), isNull())).thenReturn(Collections.emptyList());
 | 
			
		||||
		assertThatIllegalArgumentException()
 | 
			
		||||
				.isThrownBy(() -> JwtDecoderProviderConfigurationUtils.getSignatureAlgorithms(jwkSource));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void getSignatureAlgorithmsWhenJwkSetSpecifiesFamilyThenUses() throws Exception {
 | 
			
		||||
		JWKSource<SecurityContext> jwkSource = mock(JWKSource.class);
 | 
			
		||||
		// Test parameters are from Anders Rundgren, public only
 | 
			
		||||
		ECKey ecKey = new ECKey.Builder(Curve.P_256, new Base64URL("3l2Da_flYc-AuUTm2QzxgyvJxYM_2TeB9DMlwz7j1PE"),
 | 
			
		||||
				new Base64URL("-kjT7Wrfhwsi9SG6H4UXiyUiVE9GHCLauslksZ3-_t0")).keyUse(KeyUse.SIGNATURE).build();
 | 
			
		||||
		RSAKey rsaKey = new RSAKey.Builder(TestKeys.DEFAULT_PUBLIC_KEY).keyUse(KeyUse.ENCRYPTION).build();
 | 
			
		||||
		when(jwkSource.get(any(JWKSelector.class), isNull())).thenReturn(Arrays.asList(ecKey, rsaKey));
 | 
			
		||||
		Set<SignatureAlgorithm> algorithms = JwtDecoderProviderConfigurationUtils.getSignatureAlgorithms(jwkSource);
 | 
			
		||||
		assertThat(algorithms).contains(SignatureAlgorithm.ES256, SignatureAlgorithm.ES384, SignatureAlgorithm.ES512);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +80,7 @@ public class JwtDecodersTests {
 | 
			
		|||
			+ "}";
 | 
			
		||||
	// @formatter:on
 | 
			
		||||
 | 
			
		||||
	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\"}]}";
 | 
			
		||||
	private static final String JWK_SET = "{\"keys\":[{\"kty\":\"RSA\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"one\",\"n\":\"oXJ8OyOv_eRnce4akdanR4KYRfnC2zLV4uYNQpcFn6oHL0dj7D6kxQmsXoYgJV8ZVDn71KGmuLvolxsDncc2UrhyMBY6DVQVgMSVYaPCTgW76iYEKGgzTEw5IBRQL9w3SRJWd3VJTZZQjkXef48Ocz06PGF3lhbz4t5UEZtdF4rIe7u-977QwHuh7yRPBQ3sII-cVoOUMgaXB9SHcGF2iZCtPzL_IffDUcfhLQteGebhW8A6eUHgpD5A1PQ-JCw_G7UOzZAjjDjtNM2eqm8j-Ms_gqnm4MiCZ4E-9pDN77CAAPVN7kuX6ejs9KBXpk01z48i9fORYk9u7rAkh1HuQw\"}]}";
 | 
			
		||||
 | 
			
		||||
	private static final String ISSUER_MISMATCH = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczpcL1wvd3Jvbmdpc3N1ZXIiLCJleHAiOjQ2ODcyNTYwNDl9.Ax8LMI6rhB9Pv_CE3kFi1JPuLj9gZycifWrLeDpkObWEEVAsIls9zAhNFyJlG-Oo7up6_mDhZgeRfyKnpSF5GhKJtXJDCzwg0ZDVUE6rS0QadSxsMMGbl7c4y0lG_7TfLX2iWeNJukJj_oSW9KzW4FsBp1BoocWjrreesqQU3fZHbikH-c_Fs2TsAIpHnxflyEzfOFWpJ8D4DtzHXqfvieMwpy42xsPZK3LR84zlasf0Ne1tC_hLHvyHRdAXwn0CMoKxc7-8j0r9Mq8kAzUsPn9If7bMLqGkxUcTPdk5x7opAUajDZx95SXHLmtztNtBa2S6EfPJXuPKG6tM5Wq5Ug";
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -136,7 +136,6 @@ public class NimbusJwtDecoderJwkSupportTests {
 | 
			
		|||
	@Test
 | 
			
		||||
	public void decodeWhenJwkResponseIsMalformedThenReturnsStockException() throws Exception {
 | 
			
		||||
		try (MockWebServer server = new MockWebServer()) {
 | 
			
		||||
			server.enqueue(new MockResponse().setBody(MALFORMED_JWK_SET));
 | 
			
		||||
			server.enqueue(new MockResponse().setBody(MALFORMED_JWK_SET));
 | 
			
		||||
			String jwkSetUrl = server.url("/.well-known/jwks.json").toString();
 | 
			
		||||
			NimbusJwtDecoderJwkSupport jwtDecoder = new NimbusJwtDecoderJwkSupport(jwkSetUrl);
 | 
			
		||||
| 
						 | 
				
			
			@ -152,7 +151,6 @@ public class NimbusJwtDecoderJwkSupportTests {
 | 
			
		|||
	@Test
 | 
			
		||||
	public void decodeWhenJwkEndpointIsUnresponsiveThenReturnsJwtException() throws Exception {
 | 
			
		||||
		try (MockWebServer server = new MockWebServer()) {
 | 
			
		||||
			server.enqueue(new MockResponse().setBody(MALFORMED_JWK_SET));
 | 
			
		||||
			server.enqueue(new MockResponse().setBody(MALFORMED_JWK_SET));
 | 
			
		||||
			String jwkSetUrl = server.url("/.well-known/jwks.json").toString();
 | 
			
		||||
			NimbusJwtDecoderJwkSupport jwtDecoder = new NimbusJwtDecoderJwkSupport(jwkSetUrl);
 | 
			
		||||
| 
						 | 
				
			
			@ -169,7 +167,6 @@ public class NimbusJwtDecoderJwkSupportTests {
 | 
			
		|||
	@Test
 | 
			
		||||
	public void decodeWhenCustomRestOperationsSetThenUsed() throws Exception {
 | 
			
		||||
		try (MockWebServer server = new MockWebServer()) {
 | 
			
		||||
			server.enqueue(new MockResponse().setBody(JWK_SET));
 | 
			
		||||
			server.enqueue(new MockResponse().setBody(JWK_SET));
 | 
			
		||||
			String jwkSetUrl = server.url("/.well-known/jwks.json").toString();
 | 
			
		||||
			NimbusJwtDecoderJwkSupport jwtDecoder = new NimbusJwtDecoderJwkSupport(jwkSetUrl);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -53,7 +53,6 @@ import com.nimbusds.jwt.SignedJWT;
 | 
			
		|||
import com.nimbusds.jwt.proc.BadJWTException;
 | 
			
		||||
import com.nimbusds.jwt.proc.DefaultJWTProcessor;
 | 
			
		||||
import com.nimbusds.jwt.proc.JWTProcessor;
 | 
			
		||||
import okhttp3.mockwebserver.MockResponse;
 | 
			
		||||
import okhttp3.mockwebserver.MockWebServer;
 | 
			
		||||
import org.junit.BeforeClass;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
| 
						 | 
				
			
			@ -98,33 +97,6 @@ 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\"}]}";
 | 
			
		||||
 | 
			
		||||
	private static final String JWK_SET_MULTIPLE = "{\n" +
 | 
			
		||||
			"  \"keys\": [\n" +
 | 
			
		||||
			"    {\n" +
 | 
			
		||||
			"      \"kty\": \"EC\",\n" +
 | 
			
		||||
			"      \"use\": \"sig\",\n" +
 | 
			
		||||
			"      \"crv\": \"P-256\",\n" +
 | 
			
		||||
			"      \"x\": \"9w9ddaCKCdOfyKsENWI_cf90XmWRDISBrWf2vNo-TpE\",\n" +
 | 
			
		||||
			"      \"y\": \"CThkQsCBR6dC-Y8-MVf6NFTYvMiJtjBx1x0Pbr-kP5c\",\n" +
 | 
			
		||||
			"      \"alg\": \"ES256\"\n" +
 | 
			
		||||
			"    },\n" +
 | 
			
		||||
			"    {\n" +
 | 
			
		||||
			"      \"kty\": \"RSA\",\n" +
 | 
			
		||||
			"      \"e\": \"AQAB\",\n" +
 | 
			
		||||
			"      \"use\": \"sig\",\n" +
 | 
			
		||||
			"      \"alg\": \"RS256\",\n" +
 | 
			
		||||
			"      \"n\": \"rNXfHmPwwPcmyjIG0gfBdera44Y6C6jhqgGAxCFlxrhveOAy12ff3Z0oyu0fsB-q2eVQ1amBYUWaNCopVuZEBx9GcNs0KmkAmh0bQVAT9rI81CE6thuZiNfnNaqcIHnvUa__1wnR1PzX7mDyvcVtxSC6VbQo9jt6ouBXaW6ZolqzlfbDAU-2FJpE2YLoqMs1PtSss_gYiXrP0f9GLomcQTWgsw-VNc9iYJZG5K8kIKlo_bu6YQf7GoGt4IEUd-dQBpavIBL7jjRKp30zY94J4QAwPo_UnO_EpDuUa9QyO6kuk6A3yv0nfstK-4wE1Jr42tlDO1SFzRzy_aYAjT7Ozw\"\n" +
 | 
			
		||||
			"    },\n" +
 | 
			
		||||
			"    {\n" +
 | 
			
		||||
			"      \"kty\": \"EC\",\n" +
 | 
			
		||||
			"      \"use\": \"sig\",\n" +
 | 
			
		||||
			"      \"crv\": \"P-384\",\n" +
 | 
			
		||||
			"      \"x\": \"71M1BlzONOc9LYuOB-xmK8Y3njqqGTJLguDLd7geILqYDiWrH5ELb9SKtVYcQvD1\",\n" +
 | 
			
		||||
			"      \"y\": \"Lv8lK0ukUNFa1Vhlzbi8VDdIfHrd2IEmUp21fmLNwPwTMJLbDGYoPm4DgYfzOfSm\"\n" +
 | 
			
		||||
			"    }\n" +
 | 
			
		||||
			"  ]\n" +
 | 
			
		||||
			"}";
 | 
			
		||||
 | 
			
		||||
	private static final String MALFORMED_JWK_SET = "malformed";
 | 
			
		||||
 | 
			
		||||
	private static final String SIGNED_JWT = "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ0ZXN0LXN1YmplY3QiLCJzY3AiOlsibWVzc2FnZTpyZWFkIl0sImV4cCI6NDY4Mzg5Nzc3Nn0.LtMVtIiRIwSyc3aX35Zl0JVwLTcQZAB3dyBOMHNaHCKUljwMrf20a_gT79LfhjDzE_fUVUmFiAO32W1vFnYpZSVaMDUgeIOIOpxfoe9shj_uYenAwIS-_UxqGVIJiJoXNZh_MK80ShNpvsQwamxWEEOAMBtpWNiVYNDMdfgho9n3o5_Z7Gjy8RLBo1tbDREbO9kTFwGIxm_EYpezmRCRq4w1DdS6UDW321hkwMxPnCMSWOvp-hRpmgY2yjzLgPJ6Aucmg9TJ8jloAP1DjJoF1gRR7NTAk8LOGkSjTzVYDYMbCF51YdpojhItSk80YzXiEsv1mTz4oMM49jXBmfXFMA";
 | 
			
		||||
| 
						 | 
				
			
			@ -307,8 +279,8 @@ public class NimbusJwtDecoderTests {
 | 
			
		|||
	public void decodeWhenJwkEndpointIsUnresponsiveThenReturnsJwtException() throws Exception {
 | 
			
		||||
		try (MockWebServer server = new MockWebServer()) {
 | 
			
		||||
			String jwkSetUri = server.url("/.well-known/jwks.json").toString();
 | 
			
		||||
			server.shutdown();
 | 
			
		||||
			NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build();
 | 
			
		||||
			server.shutdown();
 | 
			
		||||
			// @formatter:off
 | 
			
		||||
			assertThatExceptionOfType(JwtException.class)
 | 
			
		||||
					.isThrownBy(() -> jwtDecoder.decode(SIGNED_JWT))
 | 
			
		||||
| 
						 | 
				
			
			@ -323,8 +295,8 @@ public class NimbusJwtDecoderTests {
 | 
			
		|||
		try (MockWebServer server = new MockWebServer()) {
 | 
			
		||||
			Cache cache = new ConcurrentMapCache("test-jwk-set-cache");
 | 
			
		||||
			String jwkSetUri = server.url("/.well-known/jwks.json").toString();
 | 
			
		||||
			server.shutdown();
 | 
			
		||||
			NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).cache(cache).build();
 | 
			
		||||
			server.shutdown();
 | 
			
		||||
			// @formatter:off
 | 
			
		||||
			assertThatExceptionOfType(JwtException.class)
 | 
			
		||||
					.isThrownBy(() -> jwtDecoder.decode(SIGNED_JWT))
 | 
			
		||||
| 
						 | 
				
			
			@ -632,22 +604,6 @@ public class NimbusJwtDecoderTests {
 | 
			
		|||
		assertThat(jwsAlgorithmMapKeySelector.isAllowed(JWSAlgorithm.RS512)).isTrue();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void jwsKeySetWithMultipleJWKThenMultipleAlgorithmsInSelector() throws Exception {
 | 
			
		||||
		try ( MockWebServer server = new MockWebServer() ) {
 | 
			
		||||
			Cache cache = new ConcurrentMapCache("test-jwk-set-cache");
 | 
			
		||||
			server.enqueue(new MockResponse().setBody(JWK_SET_MULTIPLE));
 | 
			
		||||
			String jwkSetUri = server.url("/.well-known/jwks.json").toString();
 | 
			
		||||
			NimbusJwtDecoder.JwkSetUriJwtDecoderBuilder builder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri);
 | 
			
		||||
			builder.cache(cache);
 | 
			
		||||
			DefaultJWTProcessor<SecurityContext> processor = (DefaultJWTProcessor<SecurityContext>) builder.processor();
 | 
			
		||||
			JWSVerificationKeySelector<SecurityContext> selector = (JWSVerificationKeySelector<SecurityContext>) processor.getJWSKeySelector();
 | 
			
		||||
			server.shutdown();
 | 
			
		||||
			assertThat(selector.isAllowed(JWSAlgorithm.RS256)).isTrue();
 | 
			
		||||
			assertThat(selector.isAllowed(JWSAlgorithm.ES256)).isTrue();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// gh-7290
 | 
			
		||||
	@Test
 | 
			
		||||
	public void decodeWhenJwkSetRequestedThenAcceptHeaderJsonAndJwkSetJson() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,7 +38,6 @@ import com.nimbusds.jose.JWSHeader;
 | 
			
		|||
import com.nimbusds.jose.JWSSigner;
 | 
			
		||||
import com.nimbusds.jose.crypto.MACSigner;
 | 
			
		||||
import com.nimbusds.jose.jwk.JWKSet;
 | 
			
		||||
import com.nimbusds.jose.jwk.source.JWKSecurityContextJWKSet;
 | 
			
		||||
import com.nimbusds.jose.jwk.source.JWKSource;
 | 
			
		||||
import com.nimbusds.jose.proc.DefaultJOSEObjectTypeVerifier;
 | 
			
		||||
import com.nimbusds.jose.proc.JWKSecurityContext;
 | 
			
		||||
| 
						 | 
				
			
			@ -101,33 +100,6 @@ public class NimbusReactiveJwtDecoderTests {
 | 
			
		|||
			+ "}";
 | 
			
		||||
	// @formatter:on
 | 
			
		||||
 | 
			
		||||
	private static final String JWK_SET_MULTIPLE = "{\n" +
 | 
			
		||||
			"  \"keys\": [\n" +
 | 
			
		||||
			"    {\n" +
 | 
			
		||||
			"      \"kty\": \"EC\",\n" +
 | 
			
		||||
			"      \"use\": \"sig\",\n" +
 | 
			
		||||
			"      \"crv\": \"P-256\",\n" +
 | 
			
		||||
			"      \"x\": \"9w9ddaCKCdOfyKsENWI_cf90XmWRDISBrWf2vNo-TpE\",\n" +
 | 
			
		||||
			"      \"y\": \"CThkQsCBR6dC-Y8-MVf6NFTYvMiJtjBx1x0Pbr-kP5c\",\n" +
 | 
			
		||||
			"      \"alg\": \"ES256\"\n" +
 | 
			
		||||
			"    },\n" +
 | 
			
		||||
			"    {\n" +
 | 
			
		||||
			"      \"kty\": \"RSA\",\n" +
 | 
			
		||||
			"      \"e\": \"AQAB\",\n" +
 | 
			
		||||
			"      \"use\": \"sig\",\n" +
 | 
			
		||||
			"      \"alg\": \"RS256\",\n" +
 | 
			
		||||
			"      \"n\": \"rNXfHmPwwPcmyjIG0gfBdera44Y6C6jhqgGAxCFlxrhveOAy12ff3Z0oyu0fsB-q2eVQ1amBYUWaNCopVuZEBx9GcNs0KmkAmh0bQVAT9rI81CE6thuZiNfnNaqcIHnvUa__1wnR1PzX7mDyvcVtxSC6VbQo9jt6ouBXaW6ZolqzlfbDAU-2FJpE2YLoqMs1PtSss_gYiXrP0f9GLomcQTWgsw-VNc9iYJZG5K8kIKlo_bu6YQf7GoGt4IEUd-dQBpavIBL7jjRKp30zY94J4QAwPo_UnO_EpDuUa9QyO6kuk6A3yv0nfstK-4wE1Jr42tlDO1SFzRzy_aYAjT7Ozw\"\n" +
 | 
			
		||||
			"    },\n" +
 | 
			
		||||
			"    {\n" +
 | 
			
		||||
			"      \"kty\": \"EC\",\n" +
 | 
			
		||||
			"      \"use\": \"sig\",\n" +
 | 
			
		||||
			"      \"crv\": \"P-384\",\n" +
 | 
			
		||||
			"      \"x\": \"71M1BlzONOc9LYuOB-xmK8Y3njqqGTJLguDLd7geILqYDiWrH5ELb9SKtVYcQvD1\",\n" +
 | 
			
		||||
			"      \"y\": \"Lv8lK0ukUNFa1Vhlzbi8VDdIfHrd2IEmUp21fmLNwPwTMJLbDGYoPm4DgYfzOfSm\"\n" +
 | 
			
		||||
			"    }\n" +
 | 
			
		||||
			"  ]\n" +
 | 
			
		||||
			"}";
 | 
			
		||||
 | 
			
		||||
	private String jwkSetUri = "https://issuer/certs";
 | 
			
		||||
 | 
			
		||||
	private String rsa512 = "eyJhbGciOiJSUzUxMiJ9.eyJzdWIiOiJ0ZXN0LXN1YmplY3QiLCJleHAiOjE5NzQzMjYxMTl9.LKAx-60EBfD7jC1jb1eKcjO4uLvf3ssISV-8tN-qp7gAjSvKvj4YA9-V2mIb6jcS1X_xGmNy6EIimZXpWaBR3nJmeu-jpe85u4WaW2Ztr8ecAi-dTO7ZozwdtljKuBKKvj4u1nF70zyCNl15AozSG0W1ASrjUuWrJtfyDG6WoZ8VfNMuhtU-xUYUFvscmeZKUYQcJ1KS-oV5tHeF8aNiwQoiPC_9KXCOZtNEJFdq6-uzFdHxvOP2yex5Gbmg5hXonauIFXG2ZPPGdXzm-5xkhBpgM8U7A_6wb3So8wBvLYYm2245QUump63AJRAy8tQpwt4n9MvQxQgS3z9R-NK92A";
 | 
			
		||||
| 
						 | 
				
			
			@ -152,7 +124,6 @@ public class NimbusReactiveJwtDecoderTests {
 | 
			
		|||
		this.server = new MockWebServer();
 | 
			
		||||
		this.server.start();
 | 
			
		||||
		this.server.enqueue(new MockResponse().setBody(this.jwkSet));
 | 
			
		||||
		this.server.enqueue(new MockResponse().setBody(this.jwkSet));
 | 
			
		||||
		this.decoder = new NimbusReactiveJwtDecoder(this.server.url("/certs").toString());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -618,19 +589,6 @@ public class NimbusReactiveJwtDecoderTests {
 | 
			
		|||
		assertThat(jwsVerificationKeySelector.isAllowed(JWSAlgorithm.RS256)).isTrue();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void jwsKeySetWithMultipleJWKThenMultipleAlgorithmsInSelector() throws Exception {
 | 
			
		||||
		try (MockWebServer server = new MockWebServer()) {
 | 
			
		||||
			server.enqueue(new MockResponse().setBody(JWK_SET_MULTIPLE));
 | 
			
		||||
			String jwkSetUri = server.url("/.well-known/jwks.json").toString();
 | 
			
		||||
			NimbusReactiveJwtDecoder.JwkSetUriReactiveJwtDecoderBuilder builder = NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri);
 | 
			
		||||
			JWSVerificationKeySelector<JWKSecurityContext> selector = (JWSVerificationKeySelector<JWKSecurityContext>) builder.jwsKeySelector(new JWKSecurityContextJWKSet());
 | 
			
		||||
			server.shutdown();
 | 
			
		||||
			assertThat(selector.isAllowed(JWSAlgorithm.RS256)).isTrue();
 | 
			
		||||
			assertThat(selector.isAllowed(JWSAlgorithm.ES256)).isTrue();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void jwsKeySelectorWhenOneAlgorithmThenReturnsSingleSelector() {
 | 
			
		||||
		JWKSource<JWKSecurityContext> jwkSource = mock(JWKSource.class);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -79,7 +79,7 @@ public class ReactiveJwtDecodersTests {
 | 
			
		|||
			+ "}";
 | 
			
		||||
	// @formatter:on
 | 
			
		||||
 | 
			
		||||
	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\"}]}";
 | 
			
		||||
	private static final String JWK_SET = "{\"keys\":[{\"kty\":\"RSA\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"one\",\"n\":\"oXJ8OyOv_eRnce4akdanR4KYRfnC2zLV4uYNQpcFn6oHL0dj7D6kxQmsXoYgJV8ZVDn71KGmuLvolxsDncc2UrhyMBY6DVQVgMSVYaPCTgW76iYEKGgzTEw5IBRQL9w3SRJWd3VJTZZQjkXef48Ocz06PGF3lhbz4t5UEZtdF4rIe7u-977QwHuh7yRPBQ3sII-cVoOUMgaXB9SHcGF2iZCtPzL_IffDUcfhLQteGebhW8A6eUHgpD5A1PQ-JCw_G7UOzZAjjDjtNM2eqm8j-Ms_gqnm4MiCZ4E-9pDN77CAAPVN7kuX6ejs9KBXpk01z48i9fORYk9u7rAkh1HuQw\"}]}";
 | 
			
		||||
 | 
			
		||||
	private static final String ISSUER_MISMATCH = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczpcL1wvd3Jvbmdpc3N1ZXIiLCJleHAiOjQ2ODcyNTYwNDl9.Ax8LMI6rhB9Pv_CE3kFi1JPuLj9gZycifWrLeDpkObWEEVAsIls9zAhNFyJlG-Oo7up6_mDhZgeRfyKnpSF5GhKJtXJDCzwg0ZDVUE6rS0QadSxsMMGbl7c4y0lG_7TfLX2iWeNJukJj_oSW9KzW4FsBp1BoocWjrreesqQU3fZHbikH-c_Fs2TsAIpHnxflyEzfOFWpJ8D4DtzHXqfvieMwpy42xsPZK3LR84zlasf0Ne1tC_hLHvyHRdAXwn0CMoKxc7-8j0r9Mq8kAzUsPn9If7bMLqGkxUcTPdk5x7opAUajDZx95SXHLmtztNtBa2S6EfPJXuPKG6tM5Wq5Ug";
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -53,6 +53,8 @@ public class JwtIssuerAuthenticationManagerResolverTests {
 | 
			
		|||
	private static final String DEFAULT_RESPONSE_TEMPLATE = "{\n" + "    \"issuer\": \"%s\", \n"
 | 
			
		||||
			+ "    \"jwks_uri\": \"%s/.well-known/jwks.json\" \n" + "}";
 | 
			
		||||
 | 
			
		||||
	private static final String JWK_SET = "{\"keys\":[{\"kty\":\"RSA\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"one\",\"n\":\"oXJ8OyOv_eRnce4akdanR4KYRfnC2zLV4uYNQpcFn6oHL0dj7D6kxQmsXoYgJV8ZVDn71KGmuLvolxsDncc2UrhyMBY6DVQVgMSVYaPCTgW76iYEKGgzTEw5IBRQL9w3SRJWd3VJTZZQjkXef48Ocz06PGF3lhbz4t5UEZtdF4rIe7u-977QwHuh7yRPBQ3sII-cVoOUMgaXB9SHcGF2iZCtPzL_IffDUcfhLQteGebhW8A6eUHgpD5A1PQ-JCw_G7UOzZAjjDjtNM2eqm8j-Ms_gqnm4MiCZ4E-9pDN77CAAPVN7kuX6ejs9KBXpk01z48i9fORYk9u7rAkh1HuQw\"}]}";
 | 
			
		||||
 | 
			
		||||
	private String jwt = jwt("iss", "trusted");
 | 
			
		||||
 | 
			
		||||
	private String evil = jwt("iss", "\"");
 | 
			
		||||
| 
						 | 
				
			
			@ -69,8 +71,11 @@ public class JwtIssuerAuthenticationManagerResolverTests {
 | 
			
		|||
					.setHeader("Content-Type", "application/json")
 | 
			
		||||
					.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer)
 | 
			
		||||
			));
 | 
			
		||||
			server.enqueue(new MockResponse().setResponseCode(200)
 | 
			
		||||
					.setHeader("Content-Type", "application/json")
 | 
			
		||||
					.setBody(JWK_SET)
 | 
			
		||||
			);
 | 
			
		||||
			// @formatter:on
 | 
			
		||||
			server.enqueue(new MockResponse().setResponseCode(200));
 | 
			
		||||
			JWSObject jws = new JWSObject(new JWSHeader(JWSAlgorithm.RS256),
 | 
			
		||||
					new Payload(new JSONObject(Collections.singletonMap(JwtClaimNames.ISS, issuer))));
 | 
			
		||||
			jws.sign(new RSASSASigner(TestKeys.DEFAULT_PRIVATE_KEY));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -59,6 +59,8 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
 | 
			
		|||
			+ "}";
 | 
			
		||||
	// @formatter:on
 | 
			
		||||
 | 
			
		||||
	private static final String JWK_SET = "{\"keys\":[{\"kty\":\"RSA\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"one\",\"n\":\"oXJ8OyOv_eRnce4akdanR4KYRfnC2zLV4uYNQpcFn6oHL0dj7D6kxQmsXoYgJV8ZVDn71KGmuLvolxsDncc2UrhyMBY6DVQVgMSVYaPCTgW76iYEKGgzTEw5IBRQL9w3SRJWd3VJTZZQjkXef48Ocz06PGF3lhbz4t5UEZtdF4rIe7u-977QwHuh7yRPBQ3sII-cVoOUMgaXB9SHcGF2iZCtPzL_IffDUcfhLQteGebhW8A6eUHgpD5A1PQ-JCw_G7UOzZAjjDjtNM2eqm8j-Ms_gqnm4MiCZ4E-9pDN77CAAPVN7kuX6ejs9KBXpk01z48i9fORYk9u7rAkh1HuQw\"}]}";
 | 
			
		||||
 | 
			
		||||
	private String jwt = jwt("iss", "trusted");
 | 
			
		||||
 | 
			
		||||
	private String evil = jwt("iss", "\"");
 | 
			
		||||
| 
						 | 
				
			
			@ -71,7 +73,8 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
 | 
			
		|||
			String issuer = server.url("").toString();
 | 
			
		||||
			server.enqueue(new MockResponse().setResponseCode(200).setHeader("Content-Type", "application/json")
 | 
			
		||||
					.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer)));
 | 
			
		||||
			server.enqueue(new MockResponse().setResponseCode(200));
 | 
			
		||||
			server.enqueue(new MockResponse().setResponseCode(200).setHeader("Content-Type", "application/json")
 | 
			
		||||
					.setBody(JWK_SET));
 | 
			
		||||
			JWSObject jws = new JWSObject(new JWSHeader(JWSAlgorithm.RS256),
 | 
			
		||||
					new Payload(new JSONObject(Collections.singletonMap(JwtClaimNames.ISS, issuer))));
 | 
			
		||||
			jws.sign(new RSASSASigner(TestKeys.DEFAULT_PRIVATE_KEY));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue