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/07/29 10:25:01 UTC
(cxf) 02/02: Make sure the refresh token client matches that of the authenticated client (#3346)
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 a2a604a4ffeef1b21a5ddda95f82059da33dafd6
Author: Colm O hEigeartaigh <co...@users.noreply.github.com>
AuthorDate: Wed Jul 29 11:24:18 2026 +0100
Make sure the refresh token client matches that of the authenticated client (#3346)
(cherry picked from commit c95fb6366930ce16c32481f230a5afa89489bd25)
---
.../oauth2/provider/AbstractOAuthDataProvider.java | 3 ++
.../provider/AbstractOAuthDataProviderTest.java | 32 ++++++++++++++++++++++
2 files changed, 35 insertions(+)
diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProvider.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProvider.java
index 8a35996a968..d425d3c8fbe 100644
--- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProvider.java
+++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProvider.java
@@ -240,6 +240,9 @@ public abstract class AbstractOAuthDataProvider implements OAuthDataProvider, Cl
if (currentRefreshToken == null) {
throw new OAuthServiceException(OAuthConstants.ACCESS_DENIED);
}
+ if (!currentRefreshToken.getClient().getClientId().equals(client.getClientId())) {
+ throw new OAuthServiceException(OAuthConstants.INVALID_GRANT);
+ }
if (OAuthUtils.isExpired(currentRefreshToken.getIssuedAt(), currentRefreshToken.getExpiresIn())) {
if (!recycleRefreshTokens) {
revokeRefreshToken(client, refreshTokenKey);
diff --git a/rt/rs/security/oauth-parent/oauth2/src/test/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProviderTest.java b/rt/rs/security/oauth-parent/oauth2/src/test/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProviderTest.java
index 0d160d352cd..782cad88b07 100644
--- a/rt/rs/security/oauth-parent/oauth2/src/test/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProviderTest.java
+++ b/rt/rs/security/oauth-parent/oauth2/src/test/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProviderTest.java
@@ -346,6 +346,38 @@ abstract class AbstractOAuthDataProviderTest {
assertNull(getProvider().getRefreshToken(rt.getTokenKey()));
}
+ /**
+ * Regression test for cross-client refresh token acceptance when recycleRefreshTokens=false.
+ * Client B must not be able to exchange Client A's refresh token for an access token.
+ */
+ @Test
+ public void testCrossClientRefreshTokenRejectedWhenRecycleDisabled() {
+ getProvider().setRecycleRefreshTokens(false);
+
+ // Client A obtains a refresh token for its resource owner.
+ Client clientA = addClient("101", "alice");
+ AccessTokenRegistration atrA = new AccessTokenRegistration();
+ atrA.setClient(clientA);
+ atrA.setApprovedScope(Arrays.asList("a", "refreshToken"));
+ atrA.setSubject(clientA.getResourceOwnerSubject());
+ ServerAccessToken atA = getProvider().createAccessToken(atrA);
+ assertNotNull(atA.getRefreshToken());
+
+ // Client B is a separate registered client controlled by an attacker.
+ Client clientB = addClient("102", "bob");
+
+ // Attacker uses Client B credentials to present Client A's refresh token.
+ try {
+ getProvider().refreshAccessToken(clientB, atA.getRefreshToken(), Collections.emptyList());
+ fail("Cross-client refresh token use must be rejected with OAuthServiceException");
+ } catch (OAuthServiceException ex) {
+ assertEquals(OAuthConstants.INVALID_GRANT, ex.getMessage());
+ }
+
+ // Client A's original access token must remain usable.
+ assertNotNull(getProvider().getAccessToken(atA.getTokenKey()));
+ }
+
protected Client addClient(String clientId, String userLogin) {
Client c = new Client();
c.setRedirectUris(Collections.singletonList("http://client/redirect"));