You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by co...@apache.org on 2026/08/24 08:07:01 UTC
(cxf) branch 4.1.x-fixes updated (f121277c5d9 -> dbb54decb1a)
This is an automated email from the ASF dual-hosted git repository.
coheigea pushed a change to branch 4.1.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git
from f121277c5d9 Introduce a TTL in the AbstractAccessTokenValidator cache (#3382)
new f32400241f2 Validate token hashes if they are available (#3385)
new dbb54decb1a [CXF-9241] - Remove ImplicitConfidentialGrantService (#3386)
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../services/ImplicitConfidentialGrantService.java | 50 --------------
.../cxf/rs/security/oidc/rp/IdTokenReader.java | 16 ++++-
.../cxf/rs/security/oidc/rp/IdTokenReaderTest.java | 78 +++++++++++++++++++++-
3 files changed, 92 insertions(+), 52 deletions(-)
delete mode 100644 rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitConfidentialGrantService.java
(cxf) 02/02: [CXF-9241] - Remove ImplicitConfidentialGrantService (#3386)
Posted by co...@apache.org.
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 dbb54decb1aa17fac07e0c2d00dc0f4df3c8e571
Author: Colm O hEigeartaigh <co...@users.noreply.github.com>
AuthorDate: Mon Aug 24 08:57:43 2026 +0100
[CXF-9241] - Remove ImplicitConfidentialGrantService (#3386)
(cherry picked from commit 57d9f6369689c113e2b5d8740cf61777367ce0a5)
---
.../services/ImplicitConfidentialGrantService.java | 50 ----------------------
1 file changed, 50 deletions(-)
diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitConfidentialGrantService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitConfidentialGrantService.java
deleted file mode 100644
index c12c44330c8..00000000000
--- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitConfidentialGrantService.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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
- *
- * http://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.apache.cxf.rs.security.oauth2.services;
-
-import jakarta.ws.rs.Path;
-import org.apache.cxf.rs.security.oauth2.common.Client;
-import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants;
-
-
-/**
- * Redirection-based Implicit Grant Service which returns tokens for the confidential clients
- * directly to a human user.
- */
-@Path("/implicit-confidential")
-public class ImplicitConfidentialGrantService extends AbstractImplicitGrantService {
-
- public ImplicitConfidentialGrantService() {
- super(OAuthConstants.TOKEN_RESPONSE_TYPE, OAuthConstants.IMPLICIT_CONFIDENTIAL_GRANT);
- }
-
- @Override
- protected void processRefreshToken(StringBuilder sb, String refreshToken) {
- sb.append('&').append(OAuthConstants.REFRESH_TOKEN).append('=').append(refreshToken);
- }
- @Override
- protected boolean canSupportPublicClient(Client c) {
- return false;
- }
-
-
-}
-
-
(cxf) 01/02: Validate token hashes if they are available (#3385)
Posted by co...@apache.org.
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 f32400241f2ea41096308cc01b640473b577d015
Author: Colm O hEigeartaigh <co...@users.noreply.github.com>
AuthorDate: Mon Aug 24 08:57:19 2026 +0100
Validate token hashes if they are available (#3385)
(cherry picked from commit 94d69c2963060fa7a58d69a62ebb4decbd8153af)
---
.../cxf/rs/security/oidc/rp/IdTokenReader.java | 16 ++++-
.../cxf/rs/security/oidc/rp/IdTokenReaderTest.java | 78 +++++++++++++++++++++-
2 files changed, 92 insertions(+), 2 deletions(-)
diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/IdTokenReader.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/IdTokenReader.java
index 38e0488e838..9ed1d18cf4e 100644
--- a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/IdTokenReader.java
+++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/IdTokenReader.java
@@ -21,6 +21,7 @@ package org.apache.cxf.rs.security.oidc.rp;
import org.apache.cxf.rs.security.jose.jwt.JwtToken;
import org.apache.cxf.rs.security.oauth2.client.Consumer;
import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken;
+import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException;
import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants;
import org.apache.cxf.rs.security.oidc.common.IdToken;
import org.apache.cxf.rs.security.oidc.utils.OidcUtils;
@@ -43,7 +44,7 @@ public class IdTokenReader extends OidcClaimsValidator {
}
public JwtToken getIdJwtToken(ClientAccessToken at, String code, Consumer client) {
String idJwtToken = at.getParameters().get(OidcUtils.ID_TOKEN);
- JwtToken jwt = getIdJwtToken(idJwtToken, client);
+ JwtToken jwt = parseAndValidateClaims(idJwtToken, client);
OidcUtils.validateAccessTokenHash(at, jwt, requireAtHash);
if (code != null) {
// The spec requires c_hash to be present in the id_token for hybrid flows,
@@ -55,7 +56,20 @@ public class IdTokenReader extends OidcClaimsValidator {
public JwtToken getIdJwtToken(ClientAccessToken at, Consumer client) {
return getIdJwtToken(at, null, client);
}
+ // No access_token/code is available on this path, so at_hash/c_hash binding cannot be
+ // verified here; reject tokens that assert such a binding rather than silently accepting it.
public JwtToken getIdJwtToken(String idJwtToken, Consumer client) {
+ JwtToken jwt = parseAndValidateClaims(idJwtToken, client);
+ if (requireAtHash && jwt.getClaims().getClaim(IdToken.ACCESS_TOKEN_HASH_CLAIM) != null) {
+ throw new OAuthServiceException("at_hash claim cannot be validated without an access token");
+ }
+ if (requireCodeHash && jwt.getClaims().getClaim(IdToken.AUTH_CODE_HASH_CLAIM) != null) {
+ throw new OAuthServiceException("c_hash claim cannot be validated without an authorization code");
+ }
+ return jwt;
+ }
+
+ private JwtToken parseAndValidateClaims(String idJwtToken, Consumer client) {
JwtToken jwt = getJwtToken(idJwtToken, client.getClientSecret());
validateJwtClaims(jwt.getClaims(), client.getClientId(), true);
return jwt;
diff --git a/rt/rs/security/sso/oidc/src/test/java/org/apache/cxf/rs/security/oidc/rp/IdTokenReaderTest.java b/rt/rs/security/sso/oidc/src/test/java/org/apache/cxf/rs/security/oidc/rp/IdTokenReaderTest.java
index 7f8bb97f2e8..d997697ed2b 100644
--- a/rt/rs/security/sso/oidc/src/test/java/org/apache/cxf/rs/security/oidc/rp/IdTokenReaderTest.java
+++ b/rt/rs/security/sso/oidc/src/test/java/org/apache/cxf/rs/security/oidc/rp/IdTokenReaderTest.java
@@ -24,6 +24,7 @@ import org.apache.cxf.rs.security.oauth2.client.Consumer;
import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken;
import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException;
import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants;
+import org.apache.cxf.rs.security.oidc.common.IdToken;
import org.apache.cxf.rs.security.oidc.utils.OidcUtils;
import org.junit.Test;
@@ -55,6 +56,61 @@ public class IdTokenReaderTest {
idTokenReader.getIdJwtToken(accessToken, "auth-code", new Consumer("client-id"));
}
+ // The String overload has no access_token/code to check at_hash/c_hash against: if the
+ // id_token asserts such a claim while hash validation is required, it must be rejected.
+ @Test(expected = OAuthServiceException.class)
+ public void testStringOverloadRejectsUnverifiableAtHashByDefault() {
+ JwtClaims claims = validClaims();
+ claims.setClaim(IdToken.ACCESS_TOKEN_HASH_CLAIM, "some-hash");
+ IdTokenReader idTokenReader = new StubJwtParsingIdTokenReader(new JwtToken(claims));
+ idTokenReader.setIssuerId("https://idp.example.com");
+
+ idTokenReader.getIdJwtToken("id-token", new Consumer("client-id"));
+ }
+
+ @Test(expected = OAuthServiceException.class)
+ public void testStringOverloadRejectsUnverifiableCodeHashWhenRequired() {
+ JwtClaims claims = validClaims();
+ claims.setClaim(IdToken.AUTH_CODE_HASH_CLAIM, "some-hash");
+ IdTokenReader idTokenReader = new StubJwtParsingIdTokenReader(new JwtToken(claims));
+ idTokenReader.setIssuerId("https://idp.example.com");
+ idTokenReader.setRequireAccessTokenHash(false);
+ idTokenReader.setRequireCodeHash(true);
+
+ idTokenReader.getIdJwtToken("id-token", new Consumer("client-id"));
+ }
+
+ @Test
+ public void testStringOverloadAcceptsTokenWithoutHashClaimsByDefault() {
+ JwtClaims claims = validClaims();
+ IdTokenReader idTokenReader = new StubJwtParsingIdTokenReader(new JwtToken(claims));
+ idTokenReader.setIssuerId("https://idp.example.com");
+
+ assertNotNull(idTokenReader.getIdJwtToken("id-token", new Consumer("client-id")));
+ }
+
+ @Test
+ public void testStringOverloadAcceptsAtHashWhenNotRequired() {
+ JwtClaims claims = validClaims();
+ claims.setClaim(IdToken.ACCESS_TOKEN_HASH_CLAIM, "some-hash");
+ IdTokenReader idTokenReader = new StubJwtParsingIdTokenReader(new JwtToken(claims));
+ idTokenReader.setIssuerId("https://idp.example.com");
+ idTokenReader.setRequireAccessTokenHash(false);
+
+ assertNotNull(idTokenReader.getIdJwtToken("id-token", new Consumer("client-id")));
+ }
+
+ private static JwtClaims validClaims() {
+ JwtClaims claims = new JwtClaims();
+ claims.setIssuer("https://idp.example.com");
+ claims.setSubject("subject");
+ claims.setAudience("client-id");
+ long now = System.currentTimeMillis() / 1000L;
+ claims.setIssuedAt(now);
+ claims.setExpiryTime(now + 300L);
+ return claims;
+ }
+
private static final class StubIdTokenReader extends IdTokenReader {
private final JwtToken jwt;
@@ -63,7 +119,27 @@ public class IdTokenReaderTest {
}
@Override
- public JwtToken getIdJwtToken(String idJwtToken, Consumer client) {
+ public JwtToken getJwtToken(String wrappedJwtToken, String clientSecret) {
+ return jwt;
+ }
+
+ @Override
+ public void validateJwtClaims(JwtClaims claims, String clientId, boolean validateClaimsAlways) {
+ // Claims validation is exercised separately in OidcClaimsValidatorTest.
+ }
+ }
+
+ // Bypasses actual JWS parsing/signature verification so the real getIdJwtToken(String, Consumer)
+ // logic (claims validation + hash-claim guard) under test still executes.
+ private static final class StubJwtParsingIdTokenReader extends IdTokenReader {
+ private final JwtToken jwt;
+
+ private StubJwtParsingIdTokenReader(JwtToken jwt) {
+ this.jwt = jwt;
+ }
+
+ @Override
+ public JwtToken getJwtToken(String wrappedJwtToken, String clientSecret) {
return jwt;
}
}