This is an automated email from the ASF dual-hosted git repository.
coheigea pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cxf.git
The following commit(s) were added to refs/heads/main by this push:
new bb22563ba46 CXF-9216 - Switch default OAuth2 code verifier to Digest
(#3132)
bb22563ba46 is described below
commit bb22563ba46b33fee4478e5c4815216da4b49347
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
---
.../grants/code/AuthorizationCodeGrantHandler.java | 20 +++-
.../security/oauth2/grants/PublicClientTest.java | 126 +++++++++------------
.../grants/grants-server-public-session-fips.xml | 36 ++++++
.../oauth2/grants/grants-server-public-session.xml | 36 ++++++
.../oauth2/grants/grants-server-public.xml | 35 ++++++
5 files changed, 176 insertions(+), 77 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 a5f758b0c4d..f3ebc98b8ae 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
@@ -63,11 +63,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
@@ -124,12 +127,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
@@ -139,83 +152,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/";
@@ -240,14 +216,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-fips.xml
b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/grants/grants-server-public-session-fips.xml
index 77d0a3f78e2..ef090a2c950 100644
---
a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/grants/grants-server-public-session-fips.xml
+++
b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/grants/grants-server-public-session-fips.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-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>
</beans>
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 7e638e740d2..5f72adc03f4 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 dea11d8a033..11e3779bfeb 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>