Polish gh-17080
	
		
			
	
		
	
	
		
			
				
	
				CodeQL Advanced / Analyze (${{ matrix.language }}) (none, actions) (push) Waiting to run
				
					Details
				
			
		
			
				
	
				CI / Build (17, ubuntu-latest) (push) Waiting to run
				
					Details
				
			
		
			
				
	
				CI / Build (17, windows-latest) (push) Waiting to run
				
					Details
				
			
		
			
				
	
				CI / Test Against Snapshots (17, 17) (push) Waiting to run
				
					Details
				
			
		
			
				
	
				CI / Test Against Snapshots (21-ea, 21) (push) Waiting to run
				
					Details
				
			
		
			
				
	
				CI / Check Samples (push) Waiting to run
				
					Details
				
			
		
			
				
	
				CI / Deploy Artifacts (push) Blocked by required conditions
				
					Details
				
			
		
			
				
	
				CI / Deploy Docs (push) Blocked by required conditions
				
					Details
				
			
		
			
				
	
				CI / Deploy Schema (push) Blocked by required conditions
				
					Details
				
			
		
			
				
	
				CI / Perform Release (push) Blocked by required conditions
				
					Details
				
			
		
			
				
	
				CI / Send Notification (push) Blocked by required conditions
				
					Details
				
			
		
			
				
	
				Deploy Docs / build (push) Waiting to run
				
					Details
				
			
		
	
				
					
				
			
				
	
				CodeQL Advanced / Analyze (${{ matrix.language }}) (none, actions) (push) Waiting to run
				
					Details
				
			
		
			
				
	
				CI / Build (17, ubuntu-latest) (push) Waiting to run
				
					Details
				
			
		
			
				
	
				CI / Build (17, windows-latest) (push) Waiting to run
				
					Details
				
			
		
			
				
	
				CI / Test Against Snapshots (17, 17) (push) Waiting to run
				
					Details
				
			
		
			
				
	
				CI / Test Against Snapshots (21-ea, 21) (push) Waiting to run
				
					Details
				
			
		
			
				
	
				CI / Check Samples (push) Waiting to run
				
					Details
				
			
		
			
				
	
				CI / Deploy Artifacts (push) Blocked by required conditions
				
					Details
				
			
		
			
				
	
				CI / Deploy Docs (push) Blocked by required conditions
				
					Details
				
			
		
			
				
	
				CI / Deploy Schema (push) Blocked by required conditions
				
					Details
				
			
		
			
				
	
				CI / Perform Release (push) Blocked by required conditions
				
					Details
				
			
		
			
				
	
				CI / Send Notification (push) Blocked by required conditions
				
					Details
				
			
		
			
				
	
				Deploy Docs / build (push) Waiting to run
				
					Details
				
			
		
	This commit is contained in:
		
							parent
							
								
									2090f44f74
								
							
						
					
					
						commit
						a265ac6ae7
					
				| 
						 | 
				
			
			@ -18,7 +18,6 @@ package org.springframework.security.config.annotation.web.configurers.oauth2.se
 | 
			
		|||
 | 
			
		||||
import java.nio.charset.StandardCharsets;
 | 
			
		||||
import java.security.MessageDigest;
 | 
			
		||||
import java.security.PublicKey;
 | 
			
		||||
import java.security.interfaces.ECPrivateKey;
 | 
			
		||||
import java.security.interfaces.ECPublicKey;
 | 
			
		||||
import java.security.interfaces.RSAPrivateKey;
 | 
			
		||||
| 
						 | 
				
			
			@ -33,6 +32,7 @@ import java.util.Set;
 | 
			
		|||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
import com.nimbusds.jose.jwk.ECKey;
 | 
			
		||||
import com.nimbusds.jose.jwk.JWK;
 | 
			
		||||
import com.nimbusds.jose.jwk.JWKSet;
 | 
			
		||||
import com.nimbusds.jose.jwk.RSAKey;
 | 
			
		||||
import com.nimbusds.jose.jwk.source.JWKSource;
 | 
			
		||||
| 
						 | 
				
			
			@ -89,6 +89,8 @@ public class DPoPAuthenticationConfigurerTests {
 | 
			
		|||
 | 
			
		||||
	private static final ECPrivateKey CLIENT_EC_PRIVATE_KEY = (ECPrivateKey) TestKeys.DEFAULT_EC_KEY_PAIR.getPrivate();
 | 
			
		||||
 | 
			
		||||
	private static final ECKey CLIENT_EC_KEY = TestJwks.jwk(CLIENT_EC_PUBLIC_KEY, CLIENT_EC_PRIVATE_KEY).build();
 | 
			
		||||
 | 
			
		||||
	private static NimbusJwtEncoder providerJwtEncoder;
 | 
			
		||||
 | 
			
		||||
	private static NimbusJwtEncoder clientJwtEncoder;
 | 
			
		||||
| 
						 | 
				
			
			@ -104,9 +106,8 @@ public class DPoPAuthenticationConfigurerTests {
 | 
			
		|||
		JWKSource<SecurityContext> providerJwkSource = (jwkSelector, securityContext) -> jwkSelector
 | 
			
		||||
			.select(new JWKSet(providerRsaKey));
 | 
			
		||||
		providerJwtEncoder = new NimbusJwtEncoder(providerJwkSource);
 | 
			
		||||
		ECKey clientEcKey = TestJwks.jwk(CLIENT_EC_PUBLIC_KEY, CLIENT_EC_PRIVATE_KEY).build();
 | 
			
		||||
		JWKSource<SecurityContext> clientJwkSource = (jwkSelector, securityContext) -> jwkSelector
 | 
			
		||||
			.select(new JWKSet(clientEcKey));
 | 
			
		||||
			.select(new JWKSet(CLIENT_EC_KEY));
 | 
			
		||||
		clientJwtEncoder = new NimbusJwtEncoder(clientJwkSource);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -114,7 +115,7 @@ public class DPoPAuthenticationConfigurerTests {
 | 
			
		|||
	public void requestWhenDPoPAndBearerAuthenticationThenUnauthorized() throws Exception {
 | 
			
		||||
		this.spring.register(SecurityConfig.class, ResourceEndpoints.class).autowire();
 | 
			
		||||
		Set<String> scope = Collections.singleton("resource1.read");
 | 
			
		||||
		String accessToken = generateAccessToken(scope, CLIENT_EC_PUBLIC_KEY);
 | 
			
		||||
		String accessToken = generateAccessToken(scope, CLIENT_EC_KEY);
 | 
			
		||||
		String dPoPProof = generateDPoPProof(HttpMethod.GET.name(), "http://localhost/resource1", accessToken);
 | 
			
		||||
		// @formatter:off
 | 
			
		||||
		this.mvc.perform(get("/resource1")
 | 
			
		||||
| 
						 | 
				
			
			@ -131,7 +132,7 @@ public class DPoPAuthenticationConfigurerTests {
 | 
			
		|||
	public void requestWhenDPoPAccessTokenMalformedThenUnauthorized() throws Exception {
 | 
			
		||||
		this.spring.register(SecurityConfig.class, ResourceEndpoints.class).autowire();
 | 
			
		||||
		Set<String> scope = Collections.singleton("resource1.read");
 | 
			
		||||
		String accessToken = generateAccessToken(scope, CLIENT_EC_PUBLIC_KEY);
 | 
			
		||||
		String accessToken = generateAccessToken(scope, CLIENT_EC_KEY);
 | 
			
		||||
		String dPoPProof = generateDPoPProof(HttpMethod.GET.name(), "http://localhost/resource1", accessToken);
 | 
			
		||||
		// @formatter:off
 | 
			
		||||
		this.mvc.perform(get("/resource1")
 | 
			
		||||
| 
						 | 
				
			
			@ -147,7 +148,7 @@ public class DPoPAuthenticationConfigurerTests {
 | 
			
		|||
	public void requestWhenMultipleDPoPProofsThenUnauthorized() throws Exception {
 | 
			
		||||
		this.spring.register(SecurityConfig.class, ResourceEndpoints.class).autowire();
 | 
			
		||||
		Set<String> scope = Collections.singleton("resource1.read");
 | 
			
		||||
		String accessToken = generateAccessToken(scope, CLIENT_EC_PUBLIC_KEY);
 | 
			
		||||
		String accessToken = generateAccessToken(scope, CLIENT_EC_KEY);
 | 
			
		||||
		String dPoPProof = generateDPoPProof(HttpMethod.GET.name(), "http://localhost/resource1", accessToken);
 | 
			
		||||
		// @formatter:off
 | 
			
		||||
		this.mvc.perform(get("/resource1")
 | 
			
		||||
| 
						 | 
				
			
			@ -164,7 +165,7 @@ public class DPoPAuthenticationConfigurerTests {
 | 
			
		|||
	public void requestWhenDPoPAuthenticationValidThenAccessed() throws Exception {
 | 
			
		||||
		this.spring.register(SecurityConfig.class, ResourceEndpoints.class).autowire();
 | 
			
		||||
		Set<String> scope = Collections.singleton("resource1.read");
 | 
			
		||||
		String accessToken = generateAccessToken(scope, CLIENT_EC_PUBLIC_KEY);
 | 
			
		||||
		String accessToken = generateAccessToken(scope, CLIENT_EC_KEY);
 | 
			
		||||
		String dPoPProof = generateDPoPProof(HttpMethod.GET.name(), "http://localhost/resource1", accessToken);
 | 
			
		||||
		// @formatter:off
 | 
			
		||||
		this.mvc.perform(get("/resource1")
 | 
			
		||||
| 
						 | 
				
			
			@ -175,11 +176,11 @@ public class DPoPAuthenticationConfigurerTests {
 | 
			
		|||
		// @formatter:on
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static String generateAccessToken(Set<String> scope, PublicKey clientPublicKey) {
 | 
			
		||||
	private static String generateAccessToken(Set<String> scope, JWK jwk) {
 | 
			
		||||
		Map<String, Object> jktClaim = null;
 | 
			
		||||
		if (clientPublicKey != null) {
 | 
			
		||||
		if (jwk != null) {
 | 
			
		||||
			try {
 | 
			
		||||
				String sha256Thumbprint = computeSHA256(clientPublicKey);
 | 
			
		||||
				String sha256Thumbprint = jwk.toPublicJWK().computeThumbprint().toString();
 | 
			
		||||
				jktClaim = new HashMap<>();
 | 
			
		||||
				jktClaim.put("jkt", sha256Thumbprint);
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -207,10 +208,7 @@ public class DPoPAuthenticationConfigurerTests {
 | 
			
		|||
 | 
			
		||||
	private static String generateDPoPProof(String method, String resourceUri, String accessToken) throws Exception {
 | 
			
		||||
		// @formatter:off
 | 
			
		||||
		Map<String, Object> publicJwk = TestJwks.jwk(CLIENT_EC_PUBLIC_KEY, CLIENT_EC_PRIVATE_KEY)
 | 
			
		||||
				.build()
 | 
			
		||||
				.toPublicJWK()
 | 
			
		||||
				.toJSONObject();
 | 
			
		||||
		Map<String, Object> publicJwk = CLIENT_EC_KEY.toPublicJWK().toJSONObject();
 | 
			
		||||
		JwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.ES256)
 | 
			
		||||
				.type("dpop+jwt")
 | 
			
		||||
				.jwk(publicJwk)
 | 
			
		||||
| 
						 | 
				
			
			@ -233,12 +231,6 @@ public class DPoPAuthenticationConfigurerTests {
 | 
			
		|||
		return Base64.getUrlEncoder().withoutPadding().encodeToString(digest);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static String computeSHA256(PublicKey publicKey) throws Exception {
 | 
			
		||||
		MessageDigest md = MessageDigest.getInstance("SHA-256");
 | 
			
		||||
		byte[] digest = md.digest(publicKey.getEncoded());
 | 
			
		||||
		return Base64.getUrlEncoder().withoutPadding().encodeToString(digest);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Configuration
 | 
			
		||||
	@EnableWebSecurity
 | 
			
		||||
	@EnableWebMvc
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,13 +18,11 @@ package org.springframework.security.oauth2.server.resource.authentication;
 | 
			
		|||
 | 
			
		||||
import java.nio.charset.StandardCharsets;
 | 
			
		||||
import java.security.MessageDigest;
 | 
			
		||||
import java.security.PublicKey;
 | 
			
		||||
import java.time.Instant;
 | 
			
		||||
import java.util.Base64;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
 | 
			
		||||
import com.nimbusds.jose.jwk.AsymmetricJWK;
 | 
			
		||||
import com.nimbusds.jose.jwk.JWK;
 | 
			
		||||
 | 
			
		||||
import org.springframework.security.authentication.AuthenticationManager;
 | 
			
		||||
| 
						 | 
				
			
			@ -243,12 +241,6 @@ public final class DPoPAuthenticationProvider implements AuthenticationProvider
 | 
			
		|||
			return new OAuth2Error(OAuth2ErrorCodes.INVALID_DPOP_PROOF, reason, null);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private static String computeSHA256(PublicKey publicKey) throws Exception {
 | 
			
		||||
			MessageDigest md = MessageDigest.getInstance("SHA-256");
 | 
			
		||||
			byte[] digest = md.digest(publicKey.getEncoded());
 | 
			
		||||
			return Base64.getUrlEncoder().withoutPadding().encodeToString(digest);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static final class OAuth2AccessTokenClaims implements OAuth2Token, ClaimAccessor {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,7 +18,6 @@ package org.springframework.security.oauth2.server.resource.authentication;
 | 
			
		|||
 | 
			
		||||
import java.nio.charset.StandardCharsets;
 | 
			
		||||
import java.security.MessageDigest;
 | 
			
		||||
import java.security.PublicKey;
 | 
			
		||||
import java.time.Instant;
 | 
			
		||||
import java.time.temporal.ChronoUnit;
 | 
			
		||||
import java.util.Base64;
 | 
			
		||||
| 
						 | 
				
			
			@ -37,7 +36,6 @@ import org.springframework.security.authentication.AuthenticationManager;
 | 
			
		|||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
 | 
			
		||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
 | 
			
		||||
import org.springframework.security.oauth2.jose.TestJwks;
 | 
			
		||||
import org.springframework.security.oauth2.jose.TestKeys;
 | 
			
		||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
 | 
			
		||||
import org.springframework.security.oauth2.jwt.JwsHeader;
 | 
			
		||||
import org.springframework.security.oauth2.jwt.Jwt;
 | 
			
		||||
| 
						 | 
				
			
			@ -293,7 +291,7 @@ public class DPoPAuthenticationProviderTests {
 | 
			
		|||
		Map<String, Object> jktClaim = null;
 | 
			
		||||
		if (clientJwk != null) {
 | 
			
		||||
			try {
 | 
			
		||||
				String sha256Thumbprint = clientJwk.computeThumbprint().toString();
 | 
			
		||||
				String sha256Thumbprint = clientJwk.toPublicJWK().computeThumbprint().toString();
 | 
			
		||||
				jktClaim = new HashMap<>();
 | 
			
		||||
				jktClaim.put("jkt", sha256Thumbprint);
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -322,4 +320,5 @@ public class DPoPAuthenticationProviderTests {
 | 
			
		|||
		byte[] digest = md.digest(value.getBytes(StandardCharsets.UTF_8));
 | 
			
		||||
		return Base64.getUrlEncoder().withoutPadding().encodeToString(digest);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue