This is an automated email from the ASF dual-hosted git repository. coheigea pushed a commit to branch 4.1.x-fixes in repository https://gitbox.apache.org/repos/asf/cxf.git
commit 2cc87705122ec2d343e632763e174706fec979d0 Author: Colm O hEigeartaigh <[email protected]> AuthorDate: Thu May 21 15:26:26 2026 +0100 CXF-9216 - Switch default OAuth2 code verifier to Digest (#3132) * Consolidating test code in PublicClientTest * CXF-9216 - Switch default OAuth2 code verifier to Digest * Make the default code verifier configurable (cherry picked from commit bb22563ba46b33fee4478e5c4815216da4b49347) # Conflicts: # systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/grants/grants-server-public-session-fips.xml --- .../grants/code/AuthorizationCodeGrantHandler.java | 20 +++- .../security/oauth2/grants/PublicClientTest.java | 126 +++++++++------------ ...n.xml => grants-server-public-session-fips.xml} | 54 +++++++-- .../oauth2/grants/grants-server-public-session.xml | 36 ++++++ .../oauth2/grants/grants-server-public.xml | 35 ++++++ 5 files changed, 185 insertions(+), 86 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrantHandler.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrantHandler.java index b4cd8e93cfb..39e6c79cfb5 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrantHandler.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrantHandler.java @@ -40,6 +40,7 @@ import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils; public class AuthorizationCodeGrantHandler extends AbstractGrantHandler { private List<CodeVerifierTransformer> codeVerifierTransformers = Collections.emptyList(); + private CodeVerifierTransformer defaultCodeVerifierTransformer = new DigestCodeVerifier(); private boolean expectCodeVerifierForPublicClients; private boolean requireCodeVerifier; @@ -173,9 +174,13 @@ public class AuthorizationCodeGrantHandler extends AbstractGrantHandler { return false; } } - // Fall back to plain + + // Fall back to configured default transformer if (codeVerifierTransformer == null) { - codeVerifierTransformer = new PlainCodeVerifier(); + if (defaultCodeVerifierTransformer == null) { + return false; + } + codeVerifierTransformer = defaultCodeVerifierTransformer; } String transformedCodeVerifier = codeVerifierTransformer.transformCodeVerifier(clientCodeVerifier); return clientCodeChallenge.equals(transformedCodeVerifier); @@ -189,8 +194,17 @@ public class AuthorizationCodeGrantHandler extends AbstractGrantHandler { public void setCodeVerifierTransformers(List<CodeVerifierTransformer> codeVerifierTransformers) { if (codeVerifierTransformers == null) { this.codeVerifierTransformers = Collections.emptyList(); + } else { + this.codeVerifierTransformers = new ArrayList<>(codeVerifierTransformers); } - this.codeVerifierTransformers = new ArrayList<>(codeVerifierTransformers); + } + + /** + * Set a default code verifier transformer used when no specific challenge method transformer is matched. + * @param defaultCodeVerifierTransformer default code verifier transformer + */ + public void setDefaultCodeVerifierTransformer(CodeVerifierTransformer defaultCodeVerifierTransformer) { + this.defaultCodeVerifierTransformer = defaultCodeVerifierTransformer; } /** diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/grants/PublicClientTest.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/grants/PublicClientTest.java index 68693acb74c..abff6f0c121 100644 --- a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/grants/PublicClientTest.java +++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/grants/PublicClientTest.java @@ -62,11 +62,14 @@ public class PublicClientTest extends AbstractClientServerTestBase { final String port; private final String tokenServiceAddress; + private final String digestOnlyTokenServiceAddress; public PublicClientTest(String port) { this.port = port; // services2 doesn't require basic auth tokenServiceAddress = "https://localhost:" + port + "/services2/"; + // services3 is configured with DigestCodeVerifier only + digestOnlyTokenServiceAddress = "https://localhost:" + port + "/services3/"; } @BeforeClass @@ -123,12 +126,22 @@ public class PublicClientTest extends AbstractClientServerTestBase { @org.junit.Test public void testPKCEPlainMissingVerifier() throws Exception { - testPKCEMissingVerifier(new PlainCodeVerifier()); + try { + testPKCE(new PlainCodeVerifier(), false, false); + fail("Failure expected on a missing verifier"); + } catch (OAuthServiceException ex) { + assertFalse(ex.getError().getError().isEmpty()); + } } @org.junit.Test public void testPKCEPlainDifferentVerifier() throws Exception { - testPKCEDifferentVerifier(new PlainCodeVerifier()); + try { + testPKCE(new PlainCodeVerifier(), true, true); + fail("Failure expected on a different verifier"); + } catch (OAuthServiceException ex) { + assertFalse(ex.getError().getError().isEmpty()); + } } @org.junit.Test @@ -138,83 +151,46 @@ public class PublicClientTest extends AbstractClientServerTestBase { @org.junit.Test public void testPKCEDigestMissingVerifier() { - testPKCEMissingVerifier(new DigestCodeVerifier()); + try { + testPKCE(new DigestCodeVerifier(), false, false); + fail("Failure expected on a missing verifier"); + } catch (OAuthServiceException ex) { + assertFalse(ex.getError().getError().isEmpty()); + } } @org.junit.Test public void testPKCEDigestDifferentVerifier() { - testPKCEDifferentVerifier(new DigestCodeVerifier()); - } - - private void testPKCE(CodeVerifierTransformer transformer) { - URL busFile = PublicClientTest.class.getResource("publicclient.xml"); - - String address = "https://localhost:" + port + "/services/"; - WebClient client = WebClient.create(address, OAuth2TestUtils.setupProviders(), - "alice", "security", busFile.toString()); - // Save the Cookie for the second request... - WebClient.getConfig(client).getRequestContext().put( - org.apache.cxf.message.Message.MAINTAIN_SESSION, Boolean.TRUE); - - // Get Authorization Code - AuthorizationCodeParameters parameters = new AuthorizationCodeParameters(); - parameters.setConsumerId("consumer-id"); - String codeVerifier = Base64UrlUtility.encode(CryptoUtils.generateSecureRandomBytes(32)); - parameters.setCodeChallenge(transformer.transformCodeVerifier(codeVerifier)); - parameters.setCodeChallengeMethod(transformer.getChallengeMethod()); - parameters.setResponseType(OAuthConstants.CODE_RESPONSE_TYPE); - parameters.setPath("authorize/"); - - String location = OAuth2TestUtils.getLocation(client, parameters); - String code = OAuth2TestUtils.getSubstring(location, "code"); - assertNotNull(code); - client.close(); - - // Now get the access token - client = WebClient.create(tokenServiceAddress, busFile.toString()); - ClientAccessToken accessToken = - OAuth2TestUtils.getAccessTokenWithAuthorizationCode(client, code, "consumer-id", null, codeVerifier); - assertNotNull(accessToken.getTokenKey()); - client.close(); + try { + testPKCE(new DigestCodeVerifier(), true, true); + fail("Failure expected on a different verifier"); + } catch (OAuthServiceException ex) { + assertFalse(ex.getError().getError().isEmpty()); + } } - private void testPKCEMissingVerifier(CodeVerifierTransformer transformer) { - URL busFile = PublicClientTest.class.getResource("publicclient.xml"); - - String address = "https://localhost:" + port + "/services/"; - WebClient client = WebClient.create(address, OAuth2TestUtils.setupProviders(), - "alice", "security", busFile.toString()); - // Save the Cookie for the second request... - WebClient.getConfig(client).getRequestContext().put( - org.apache.cxf.message.Message.MAINTAIN_SESSION, Boolean.TRUE); - - // Get Authorization Code - AuthorizationCodeParameters parameters = new AuthorizationCodeParameters(); - parameters.setConsumerId("consumer-id"); - String codeVerifier = Base64UrlUtility.encode(CryptoUtils.generateSecureRandomBytes(32)); - parameters.setCodeChallenge(transformer.transformCodeVerifier(codeVerifier)); - parameters.setCodeChallengeMethod(transformer.getChallengeMethod()); - parameters.setResponseType(OAuthConstants.CODE_RESPONSE_TYPE); - parameters.setPath("authorize/"); - - String location = OAuth2TestUtils.getLocation(client, parameters); - String code = OAuth2TestUtils.getSubstring(location, "code"); - assertNotNull(code); - client.close(); - - // Now get the access token - client = WebClient.create(tokenServiceAddress, busFile.toString()); + @org.junit.Test + public void testPKCEDigestOnlyServer() { try { - OAuth2TestUtils.getAccessTokenWithAuthorizationCode(client, code, "consumer-id", null); - fail("Failure expected on a missing verifier"); + testPKCE(new PlainCodeVerifier(), true, false, digestOnlyTokenServiceAddress); + fail("Failure expected when plain verifier is not supported"); } catch (OAuthServiceException ex) { assertFalse(ex.getError().getError().isEmpty()); - } finally { - client.close(); } + + testPKCE(new DigestCodeVerifier(), true, false, digestOnlyTokenServiceAddress); } - private void testPKCEDifferentVerifier(CodeVerifierTransformer transformer) { + private void testPKCE(CodeVerifierTransformer transformer) { + testPKCE(transformer, true, false, tokenServiceAddress); + } + + private void testPKCE(CodeVerifierTransformer transformer, boolean sendVerifier, boolean sendFakeVerifier) { + testPKCE(transformer, sendVerifier, sendFakeVerifier, tokenServiceAddress); + } + + private void testPKCE(CodeVerifierTransformer transformer, boolean sendVerifier, + boolean sendFakeVerifier, String serviceAddress) { URL busFile = PublicClientTest.class.getResource("publicclient.xml"); String address = "https://localhost:" + port + "/services/"; @@ -239,14 +215,16 @@ public class PublicClientTest extends AbstractClientServerTestBase { client.close(); // Now get the access token - client = WebClient.create(tokenServiceAddress, busFile.toString()); - - codeVerifier = Base64UrlUtility.encode(CryptoUtils.generateSecureRandomBytes(32)); + client = WebClient.create(serviceAddress, busFile.toString()); + if (!sendVerifier) { + codeVerifier = null; + } else if (sendFakeVerifier) { + codeVerifier = Base64UrlUtility.encode(CryptoUtils.generateSecureRandomBytes(32)); + } try { - OAuth2TestUtils.getAccessTokenWithAuthorizationCode(client, code, "consumer-id", null, codeVerifier); - fail("Failure expected on a different verifier"); - } catch (OAuthServiceException ex) { - assertFalse(ex.getError().getError().isEmpty()); + ClientAccessToken accessToken = + OAuth2TestUtils.getAccessTokenWithAuthorizationCode(client, code, "consumer-id", null, codeVerifier); + assertNotNull(accessToken.getTokenKey()); } finally { client.close(); } diff --git a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/grants/grants-server-public-session.xml b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/grants/grants-server-public-session-fips.xml similarity index 77% copy from systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/grants/grants-server-public-session.xml copy to systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/grants/grants-server-public-session-fips.xml index 0b6471a2247..ef090a2c950 100644 --- a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/grants/grants-server-public-session.xml +++ b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/grants/grants-server-public-session-fips.xml @@ -35,7 +35,7 @@ under the License. <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/> <cxf:bus> <cxf:features> - <cxf:logging/> + <bean class="org.apache.cxf.ext.logging.LoggingFeature"/> </cxf:features> <cxf:properties> <entry key="org.apache.cxf.jaxrs.bus.providers" value-ref="busProviders"/> @@ -124,10 +124,10 @@ under the License. <ref bean="basicAuthFilter"/> </jaxrs:providers> <jaxrs:properties> - <entry key="rs.security.signature.out.properties" value="org/apache/cxf/systest/jaxrs/security/alice.rs.properties"/> - <entry key="rs.security.encryption.in.properties" value="org/apache/cxf/systest/jaxrs/security/bob.rs.properties"/> - <entry key="rs.security.encryption.out.properties" value="org/apache/cxf/systest/jaxrs/security/bob.rs.properties"/> - <entry key="rs.security.signature.in.properties" value="org/apache/cxf/systest/jaxrs/security/alice.rs.properties"/> + <entry key="rs.security.signature.out.properties" value="org/apache/cxf/systest/jaxrs/security/alice.rs-fips.properties"/> + <entry key="rs.security.encryption.in.properties" value="org/apache/cxf/systest/jaxrs/security/bob.rs-fips.properties"/> + <entry key="rs.security.encryption.out.properties" value="org/apache/cxf/systest/jaxrs/security/bob.rs-fips.properties"/> + <entry key="rs.security.signature.in.properties" value="org/apache/cxf/systest/jaxrs/security/alice.rs-fips.properties"/> <entry key="rs.security.signature.key.password.provider" value-ref="keyPasswordProvider"/> <entry key="rs.security.decryption.key.password.provider" value-ref="keyPasswordProvider"/> </jaxrs:properties> @@ -154,6 +154,26 @@ under the License. </list> </property> </bean> + + <bean id="digestOnlyCodeGrantHandler" class="org.apache.cxf.rs.security.oauth2.grants.code.AuthorizationCodeGrantHandler"> + <property name="dataProvider" ref="oauthProvider"/> + <property name="requireCodeVerifier" value="true"/> + <property name="codeVerifierTransformers"> + <list> + <ref bean="digestVerifier"/> + </list> + </property> + </bean> + + <bean id="digestOnlyTokenService" class="org.apache.cxf.rs.security.oauth2.services.AccessTokenService"> + <property name="dataProvider" ref="oauthProvider"/> + <property name="canSupportPublicClients" value="true"/> + <property name="grantHandlers"> + <list> + <ref bean="digestOnlyCodeGrantHandler"/> + </list> + </property> + </bean> <jaxrs:server depends-on="tls-config" @@ -162,10 +182,26 @@ under the License. <ref bean="digestTokenService"/> </jaxrs:serviceBeans> <jaxrs:properties> - <entry key="rs.security.signature.out.properties" value="org/apache/cxf/systest/jaxrs/security/alice.rs.properties"/> - <entry key="rs.security.encryption.in.properties" value="org/apache/cxf/systest/jaxrs/security/bob.rs.properties"/> - <entry key="rs.security.encryption.out.properties" value="org/apache/cxf/systest/jaxrs/security/bob.rs.properties"/> - <entry key="rs.security.signature.in.properties" value="org/apache/cxf/systest/jaxrs/security/alice.rs.properties"/> + <entry key="rs.security.signature.out.properties" value="org/apache/cxf/systest/jaxrs/security/alice.rs-fips.properties"/> + <entry key="rs.security.encryption.in.properties" value="org/apache/cxf/systest/jaxrs/security/bob.rs-fips.properties"/> + <entry key="rs.security.encryption.out.properties" value="org/apache/cxf/systest/jaxrs/security/bob.rs-fips.properties"/> + <entry key="rs.security.signature.in.properties" value="org/apache/cxf/systest/jaxrs/security/alice.rs-fips.properties"/> + <entry key="rs.security.signature.key.password.provider" value-ref="keyPasswordProvider"/> + <entry key="rs.security.decryption.key.password.provider" value-ref="keyPasswordProvider"/> + </jaxrs:properties> + </jaxrs:server> + + <jaxrs:server + depends-on="tls-config" + address="https://localhost:${testutil.ports.jaxrs-oauth2-grants-jcache-public-session}/services3"> + <jaxrs:serviceBeans> + <ref bean="digestOnlyTokenService"/> + </jaxrs:serviceBeans> + <jaxrs:properties> + <entry key="rs.security.signature.out.properties" value="org/apache/cxf/systest/jaxrs/security/alice.rs-fips.properties"/> + <entry key="rs.security.encryption.in.properties" value="org/apache/cxf/systest/jaxrs/security/bob.rs-fips.properties"/> + <entry key="rs.security.encryption.out.properties" value="org/apache/cxf/systest/jaxrs/security/bob.rs-fips.properties"/> + <entry key="rs.security.signature.in.properties" value="org/apache/cxf/systest/jaxrs/security/alice.rs-fips.properties"/> <entry key="rs.security.signature.key.password.provider" value-ref="keyPasswordProvider"/> <entry key="rs.security.decryption.key.password.provider" value-ref="keyPasswordProvider"/> </jaxrs:properties> diff --git a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/grants/grants-server-public-session.xml b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/grants/grants-server-public-session.xml index 0b6471a2247..490ba31a651 100644 --- a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/grants/grants-server-public-session.xml +++ b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/grants/grants-server-public-session.xml @@ -154,6 +154,26 @@ under the License. </list> </property> </bean> + + <bean id="digestOnlyCodeGrantHandler" class="org.apache.cxf.rs.security.oauth2.grants.code.AuthorizationCodeGrantHandler"> + <property name="dataProvider" ref="oauthProvider"/> + <property name="requireCodeVerifier" value="true"/> + <property name="codeVerifierTransformers"> + <list> + <ref bean="digestVerifier"/> + </list> + </property> + </bean> + + <bean id="digestOnlyTokenService" class="org.apache.cxf.rs.security.oauth2.services.AccessTokenService"> + <property name="dataProvider" ref="oauthProvider"/> + <property name="canSupportPublicClients" value="true"/> + <property name="grantHandlers"> + <list> + <ref bean="digestOnlyCodeGrantHandler"/> + </list> + </property> + </bean> <jaxrs:server depends-on="tls-config" @@ -170,6 +190,22 @@ under the License. <entry key="rs.security.decryption.key.password.provider" value-ref="keyPasswordProvider"/> </jaxrs:properties> </jaxrs:server> + + <jaxrs:server + depends-on="tls-config" + address="https://localhost:${testutil.ports.jaxrs-oauth2-grants-jcache-public-session}/services3"> + <jaxrs:serviceBeans> + <ref bean="digestOnlyTokenService"/> + </jaxrs:serviceBeans> + <jaxrs:properties> + <entry key="rs.security.signature.out.properties" value="org/apache/cxf/systest/jaxrs/security/alice.rs.properties"/> + <entry key="rs.security.encryption.in.properties" value="org/apache/cxf/systest/jaxrs/security/bob.rs.properties"/> + <entry key="rs.security.encryption.out.properties" value="org/apache/cxf/systest/jaxrs/security/bob.rs.properties"/> + <entry key="rs.security.signature.in.properties" value="org/apache/cxf/systest/jaxrs/security/alice.rs.properties"/> + <entry key="rs.security.signature.key.password.provider" value-ref="keyPasswordProvider"/> + <entry key="rs.security.decryption.key.password.provider" value-ref="keyPasswordProvider"/> + </jaxrs:properties> + </jaxrs:server> </beans> diff --git a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/grants/grants-server-public.xml b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/grants/grants-server-public.xml index 48a38ab70b6..cd665511fe4 100644 --- a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/grants/grants-server-public.xml +++ b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/grants/grants-server-public.xml @@ -148,6 +148,26 @@ under the License. </list> </property> </bean> + + <bean id="digestOnlyCodeGrantHandler" class="org.apache.cxf.rs.security.oauth2.grants.code.AuthorizationCodeGrantHandler"> + <property name="dataProvider" ref="oauthProvider"/> + <property name="requireCodeVerifier" value="true"/> + <property name="codeVerifierTransformers"> + <list> + <ref bean="digestVerifier"/> + </list> + </property> + </bean> + + <bean id="digestOnlyTokenService" class="org.apache.cxf.rs.security.oauth2.services.AccessTokenService"> + <property name="dataProvider" ref="oauthProvider"/> + <property name="canSupportPublicClients" value="true"/> + <property name="grantHandlers"> + <list> + <ref bean="digestOnlyCodeGrantHandler"/> + </list> + </property> + </bean> <jaxrs:server depends-on="tls-config" @@ -163,6 +183,21 @@ under the License. <entry key="rs.security.signature.algorithm" value="RS256" /> </jaxrs:properties> </jaxrs:server> + + <jaxrs:server + depends-on="tls-config" + address="https://localhost:${testutil.ports.jaxrs-oauth2-grants-jcache-public}/services3"> + <jaxrs:serviceBeans> + <ref bean="digestOnlyTokenService"/> + </jaxrs:serviceBeans> + <jaxrs:properties> + <entry key="rs.security.keystore.type" value="jks" /> + <entry key="rs.security.keystore.alias" value="alice"/> + <entry key="rs.security.keystore.password" value="password"/> + <entry key="rs.security.keystore.file" value="keys/alice.jks" /> + <entry key="rs.security.signature.algorithm" value="RS256" /> + </jaxrs:properties> + </jaxrs:server> </beans>
