| 
					
				 | 
			
			
				@@ -1,5 +1,5 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /* 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- * Copyright 2002-2020 the original author or authors. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * Copyright 2002-2022 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. 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -57,7 +57,7 @@ public class DefaultOAuth2AuthorizationRequestResolverTests { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	private ClientRegistration fineRedirectUriTemplateRegistration; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	private ClientRegistration pkceRegistration; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private ClientRegistration publicClientRegistration; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	private ClientRegistration oidcRegistration; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -73,9 +73,9 @@ public class DefaultOAuth2AuthorizationRequestResolverTests { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		this.registration2 = TestClientRegistrations.clientRegistration2().build(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		this.fineRedirectUriTemplateRegistration = fineRedirectUriTemplateClientRegistration().build(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		// @formatter:off 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		this.pkceRegistration = TestClientRegistrations.clientRegistration() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				.registrationId("pkce-client-registration-id") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				.clientId("pkce-client-id") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		this.publicClientRegistration = TestClientRegistrations.clientRegistration() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				.registrationId("public-client-registration-id") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				.clientId("public-client-id") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 				.clientAuthenticationMethod(ClientAuthenticationMethod.NONE) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 				.clientSecret(null) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 				.build(); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -85,7 +85,7 @@ public class DefaultOAuth2AuthorizationRequestResolverTests { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 				.build(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		// @formatter:on 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		this.clientRegistrationRepository = new InMemoryClientRegistrationRepository(this.registration1, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				this.registration2, this.fineRedirectUriTemplateRegistration, this.pkceRegistration, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				this.registration2, this.fineRedirectUriTemplateRegistration, this.publicClientRegistration, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 				this.oidcRegistration); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		this.resolver = new DefaultOAuth2AuthorizationRequestResolver(this.clientRegistrationRepository, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 				this.authorizationRequestBaseUri); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -371,8 +371,8 @@ public class DefaultOAuth2AuthorizationRequestResolverTests { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	@Test 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	public void resolveWhenAuthorizationRequestWithValidPkceClientThenResolves() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		ClientRegistration clientRegistration = this.pkceRegistration; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	public void resolveWhenAuthorizationRequestWithValidPublicClientThenResolves() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		ClientRegistration clientRegistration = this.publicClientRegistration; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		String requestUri = this.authorizationRequestBaseUri + "/" + clientRegistration.getRegistrationId(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		request.setServletPath(requestUri); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -398,10 +398,84 @@ public class DefaultOAuth2AuthorizationRequestResolverTests { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		assertThat((String) authorizationRequest.getAttribute(PkceParameterNames.CODE_VERIFIER)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 				.matches("^([a-zA-Z0-9\\-\\.\\_\\~]){128}$"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		assertThat(authorizationRequest.getAuthorizationRequestUri()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				.matches("https://example.com/login/oauth/authorize\\?" + "response_type=code&client_id=pkce-client-id&" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						+ "scope=read:user&state=.{15,}&" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						+ "redirect_uri=http://localhost/login/oauth2/code/pkce-client-registration-id&" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						+ "code_challenge_method=S256&" + "code_challenge=([a-zA-Z0-9\\-\\.\\_\\~]){43}"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				.matches("https://example.com/login/oauth/authorize\\?" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						+ "response_type=code&client_id=public-client-id&" + "scope=read:user&state=.{15,}&" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						+ "redirect_uri=http://localhost/login/oauth2/code/public-client-registration-id&" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						+ "code_challenge=([a-zA-Z0-9\\-\\.\\_\\~]){43}&" + "code_challenge_method=S256"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	// gh-6548 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	@Test 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	public void resolveWhenAuthorizationRequestApplyPkceToConfidentialClientsThenApplied() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		this.resolver.setAuthorizationRequestCustomizer(OAuth2AuthorizationRequestCustomizers.withPkce()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		ClientRegistration clientRegistration = this.registration1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		String requestUri = this.authorizationRequestBaseUri + "/" + clientRegistration.getRegistrationId(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		request.setServletPath(requestUri); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		OAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		assertPkceApplied(authorizationRequest, clientRegistration); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		clientRegistration = this.registration2; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		requestUri = this.authorizationRequestBaseUri + "/" + clientRegistration.getRegistrationId(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		request = new MockHttpServletRequest("GET", requestUri); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		request.setServletPath(requestUri); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		authorizationRequest = this.resolver.resolve(request); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		assertPkceApplied(authorizationRequest, clientRegistration); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	// gh-6548 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	@Test 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	public void resolveWhenAuthorizationRequestApplyPkceToSpecificConfidentialClientThenApplied() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		this.resolver.setAuthorizationRequestCustomizer((builder) -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			builder.attributes((attrs) -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				String registrationId = (String) attrs.get(OAuth2ParameterNames.REGISTRATION_ID); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				if (this.registration1.getRegistrationId().equals(registrationId)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					OAuth2AuthorizationRequestCustomizers.withPkce().accept(builder); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		ClientRegistration clientRegistration = this.registration1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		String requestUri = this.authorizationRequestBaseUri + "/" + clientRegistration.getRegistrationId(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		request.setServletPath(requestUri); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		OAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		assertPkceApplied(authorizationRequest, clientRegistration); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		clientRegistration = this.registration2; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		requestUri = this.authorizationRequestBaseUri + "/" + clientRegistration.getRegistrationId(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		request = new MockHttpServletRequest("GET", requestUri); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		request.setServletPath(requestUri); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		authorizationRequest = this.resolver.resolve(request); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		assertPkceNotApplied(authorizationRequest, clientRegistration); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private void assertPkceApplied(OAuth2AuthorizationRequest authorizationRequest, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			ClientRegistration clientRegistration) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		assertThat(authorizationRequest.getAdditionalParameters()).containsKey(PkceParameterNames.CODE_CHALLENGE); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		assertThat(authorizationRequest.getAdditionalParameters()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				.contains(entry(PkceParameterNames.CODE_CHALLENGE_METHOD, "S256")); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		assertThat(authorizationRequest.getAttributes()).containsKey(PkceParameterNames.CODE_VERIFIER); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		assertThat((String) authorizationRequest.getAttribute(PkceParameterNames.CODE_VERIFIER)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				.matches("^([a-zA-Z0-9\\-\\.\\_\\~]){128}$"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		assertThat(authorizationRequest.getAuthorizationRequestUri()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				.matches("https://example.com/login/oauth/authorize\\?" + "response_type=code&" + "client_id=" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						+ clientRegistration.getClientId() + "&" + "scope=read:user&" + "state=.{15,}&" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						+ "redirect_uri=http://localhost/login/oauth2/code/" + clientRegistration.getRegistrationId() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						+ "&" + "code_challenge=([a-zA-Z0-9\\-\\.\\_\\~]){43}&" + "code_challenge_method=S256"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private void assertPkceNotApplied(OAuth2AuthorizationRequest authorizationRequest, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			ClientRegistration clientRegistration) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		assertThat(authorizationRequest.getAdditionalParameters()).doesNotContainKey(PkceParameterNames.CODE_CHALLENGE); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		assertThat(authorizationRequest.getAdditionalParameters()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				.doesNotContainKey(PkceParameterNames.CODE_CHALLENGE_METHOD); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		assertThat(authorizationRequest.getAttributes()).doesNotContainKey(PkceParameterNames.CODE_VERIFIER); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		assertThat(authorizationRequest.getAuthorizationRequestUri()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				.matches("https://example.com/login/oauth/authorize\\?" + "response_type=code&" + "client_id=" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						+ clientRegistration.getClientId() + "&" + "scope=read:user&" + "state=.{15,}&" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						+ "redirect_uri=http://localhost/login/oauth2/code/" + clientRegistration.getRegistrationId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	@Test 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -444,7 +518,7 @@ public class DefaultOAuth2AuthorizationRequestResolverTests { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		request.setServletPath(requestUri); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		this.resolver.setAuthorizationRequestCustomizer( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				(customizer) -> customizer.additionalParameters((params) -> params.remove(OidcParameterNames.NONCE)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				(builder) -> builder.additionalParameters((params) -> params.remove(OidcParameterNames.NONCE)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 						.attributes((attrs) -> attrs.remove(OidcParameterNames.NONCE))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		OAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		assertThat(authorizationRequest.getAdditionalParameters()).doesNotContainKey(OidcParameterNames.NONCE); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -462,11 +536,10 @@ public class DefaultOAuth2AuthorizationRequestResolverTests { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		String requestUri = this.authorizationRequestBaseUri + "/" + clientRegistration.getRegistrationId(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		request.setServletPath(requestUri); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		this.resolver 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				.setAuthorizationRequestCustomizer((customizer) -> customizer.authorizationRequestUri((uriBuilder) -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					uriBuilder.queryParam("param1", "value1"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					return uriBuilder.build(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				})); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		this.resolver.setAuthorizationRequestCustomizer((builder) -> builder.authorizationRequestUri((uriBuilder) -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			uriBuilder.queryParam("param1", "value1"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			return uriBuilder.build(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		})); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		OAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		assertThat(authorizationRequest.getAuthorizationRequestUri()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 				.matches("https://example.com/login/oauth/authorize\\?" + "response_type=code&client_id=client-id&" 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -481,7 +554,7 @@ public class DefaultOAuth2AuthorizationRequestResolverTests { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		String requestUri = this.authorizationRequestBaseUri + "/" + clientRegistration.getRegistrationId(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		request.setServletPath(requestUri); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		this.resolver.setAuthorizationRequestCustomizer((customizer) -> customizer.parameters((params) -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		this.resolver.setAuthorizationRequestCustomizer((builder) -> builder.parameters((params) -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			params.put("appid", params.get("client_id")); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			params.remove("client_id"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		})); 
			 |