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 35abc431568b999292604897e7819671b35552fb
Author: Colm O hEigeartaigh <[email protected]>
AuthorDate: Thu May 21 07:41:17 2026 +0100

    Fix the OAuth2 client ip address check and add a test (#3127)
    
    (cherry picked from commit 02b8e46137a0c628d4daa93bda235949b2eb5ab2)
---
 .../oauth2/filters/OAuthRequestFilter.java         |  2 +-
 .../oauth2/common/JCacheOAuthDataProviderImpl.java | 54 +++++++++++++-------
 .../oauth2/filters/OAuth2JwtFiltersTest.java       | 58 ++++++++++++++++++++--
 3 files changed, 93 insertions(+), 21 deletions(-)

diff --git 
a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java
 
b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java
index 68a4ada20c5..0f5d8a04e62 100644
--- 
a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java
+++ 
b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java
@@ -138,7 +138,7 @@ public class OAuthRequestFilter extends 
AbstractAccessTokenValidator
 
         if (accessTokenV.getClientIpAddress() != null) {
             String remoteAddress = 
getMessageContext().getHttpServletRequest().getRemoteAddr();
-            if (remoteAddress == null || 
accessTokenV.getClientIpAddress().equals(remoteAddress)) {
+            if (remoteAddress == null || 
!accessTokenV.getClientIpAddress().equals(remoteAddress)) {
                 String message = "Client IP Address is invalid";
                 LOG.warning(message);
                 throw ExceptionUtils.toForbiddenException(null, null);
diff --git 
a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/common/JCacheOAuthDataProviderImpl.java
 
b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/common/JCacheOAuthDataProviderImpl.java
index 03e002130d3..d4edc835cfb 100644
--- 
a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/common/JCacheOAuthDataProviderImpl.java
+++ 
b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/common/JCacheOAuthDataProviderImpl.java
@@ -78,25 +78,10 @@ public class JCacheOAuthDataProviderImpl extends 
JCacheCodeDataProvider {
             redirectUris.add("https://localhost:"; + partnerPort + 
"/partnerservice/bookstore/books");
         }
         client.setRedirectUris(redirectUris);
-
-        client.getAllowedGrantTypes().add("authorization_code");
-        client.getAllowedGrantTypes().add("refresh_token");
-        client.getAllowedGrantTypes().add("implicit");
-        client.getAllowedGrantTypes().add("hybrid");
-        client.getAllowedGrantTypes().add("password");
-        client.getAllowedGrantTypes().add("client_credentials");
-        
client.getAllowedGrantTypes().add("urn:ietf:params:oauth:grant-type:saml2-bearer");
-        
client.getAllowedGrantTypes().add("urn:ietf:params:oauth:grant-type:jwt-bearer");
-
-        client.getRegisteredScopes().add("read_balance");
-        client.getRegisteredScopes().add("create_balance");
-        client.getRegisteredScopes().add("read_data");
-        client.getRegisteredScopes().add("read_book");
-        client.getRegisteredScopes().add("create_book");
-        client.getRegisteredScopes().add("create_image");
-        client.getRegisteredScopes().add("openid");
+        configureDefaultClientGrantsAndScopes(client);
 
         this.setClient(client);
+        addClientIpTestClients(createPublicClients, redirectUris);
 
         // OIDC filters test client
         client = createPublicClients ? new Client("consumer-id-oidc", null, 
false)
@@ -186,6 +171,41 @@ public class JCacheOAuthDataProviderImpl extends 
JCacheCodeDataProvider {
         }
     }
 
+    private static void configureDefaultClientGrantsAndScopes(Client client) {
+        client.getAllowedGrantTypes().add("authorization_code");
+        client.getAllowedGrantTypes().add("refresh_token");
+        client.getAllowedGrantTypes().add("implicit");
+        client.getAllowedGrantTypes().add("hybrid");
+        client.getAllowedGrantTypes().add("password");
+        client.getAllowedGrantTypes().add("client_credentials");
+        
client.getAllowedGrantTypes().add("urn:ietf:params:oauth:grant-type:saml2-bearer");
+        
client.getAllowedGrantTypes().add("urn:ietf:params:oauth:grant-type:jwt-bearer");
+
+        client.getRegisteredScopes().add("read_balance");
+        client.getRegisteredScopes().add("create_balance");
+        client.getRegisteredScopes().add("read_data");
+        client.getRegisteredScopes().add("read_book");
+        client.getRegisteredScopes().add("create_book");
+        client.getRegisteredScopes().add("create_image");
+        client.getRegisteredScopes().add("openid");
+    }
+
+    private void addClientIpTestClients(boolean createPublicClients, 
List<String> redirectUris) {
+        Client ipPassClient = createPublicClients ? new 
Client("consumer-id-ip-pass", null, false)
+            : new Client("consumer-id-ip-pass", "this-is-a-secret", true);
+        ipPassClient.setRedirectUris(new ArrayList<>(redirectUris));
+        configureDefaultClientGrantsAndScopes(ipPassClient);
+        ipPassClient.setClientIpAddress("127.0.0.1");
+        this.setClient(ipPassClient);
+
+        Client ipFailClient = createPublicClients ? new 
Client("consumer-id-ip-fail", null, false)
+            : new Client("consumer-id-ip-fail", "this-is-a-secret", true);
+        ipFailClient.setRedirectUris(new ArrayList<>(redirectUris));
+        configureDefaultClientGrantsAndScopes(ipFailClient);
+        ipFailClient.setClientIpAddress("203.0.113.10");
+        this.setClient(ipFailClient);
+    }
+
     @Override
     protected ServerAccessToken createNewAccessToken(Client client, 
UserSubject userSub) {
         ServerAccessToken token = super.createNewAccessToken(client, userSub);
diff --git 
a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/filters/OAuth2JwtFiltersTest.java
 
b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/filters/OAuth2JwtFiltersTest.java
index a372aeca98c..a723fa08cd6 100644
--- 
a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/filters/OAuth2JwtFiltersTest.java
+++ 
b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/filters/OAuth2JwtFiltersTest.java
@@ -24,6 +24,7 @@ import java.util.Collections;
 
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.Response.Status.Family;
 import org.apache.cxf.bus.spring.SpringBusFactory;
 import org.apache.cxf.jaxrs.client.WebClient;
 import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer;
@@ -98,14 +99,65 @@ public class OAuth2JwtFiltersTest extends 
AbstractBusClientServerTestBase {
         doTestServiceWithJwtTokenAndScope(oauthServiceAddress, rsAddress);
     }
 
+    @org.junit.Test
+    public void testServiceWithJwtTokenAndClientIpMatch() throws Exception {
+        String oauthServiceAddress = "https://localhost:"; + OAUTH_PORT + 
"/services/";
+        String rsAddress = "https://127.0.0.1:"; + PORT + 
"/secured/bookstore/books";
+        doTestServiceWithJwtTokenAndScope(oauthServiceAddress, rsAddress, 
"consumer-id-ip-pass");
+    }
+
+    @org.junit.Test
+    public void testServiceWithJwtTokenAndClientIpMismatch() throws Exception {
+        String oauthServiceAddress = "https://localhost:"; + OAUTH_PORT + 
"/services/";
+        String rsAddress = "https://127.0.0.1:"; + PORT + 
"/secured/bookstore/books";
+
+        final AuthorizationMetadata authorizationMetadata =
+            OAuthClientUtils.getAuthorizationMetadata(oauthServiceAddress);
+        final String scope = "create_book";
+        final String clientId = "consumer-id-ip-fail";
+
+        final URI authorizationURI = OAuthClientUtils.getAuthorizationURI(
+            authorizationMetadata.getAuthorizationEndpoint().toString(),
+            clientId, null, null, scope);
+
+        WebClient oauthClient = WebClient.create(authorizationURI.toString(), 
OAuth2TestUtils.setupProviders(),
+                                                 "alice", "security", null);
+        WebClient.getConfig(oauthClient).getRequestContext().put(
+            org.apache.cxf.message.Message.MAINTAIN_SESSION, Boolean.TRUE);
+
+        final String location = OAuth2TestUtils.getLocation(oauthClient,
+            
oauthClient.accept(MediaType.APPLICATION_JSON).get(OAuthAuthorizationData.class),
+            null);
+        final String code = OAuth2TestUtils.getSubstring(location, "code");
+        assertNotNull(code);
+
+        final ClientAccessToken accessToken = OAuthClientUtils.getAccessToken(
+            authorizationMetadata.getTokenEndpoint().toString(),
+            new Consumer(clientId, "this-is-a-secret"),
+            new AuthorizationCodeGrant(code),
+            true);
+        assertNotNull(accessToken.getTokenKey());
+
+        WebClient client = WebClient.create(rsAddress, 
OAuth2TestUtils.setupProviders())
+            .authorization(new ClientAccessToken(BEARER_AUTHORIZATION_SCHEME, 
accessToken.getTokenKey()));
+        Response response = client.type("application/xml").post(new 
Book("book", 123L));
+        assertEquals(Family.CLIENT_ERROR, 
response.getStatusInfo().getFamily());
+    }
+
     private void doTestServiceWithJwtTokenAndScope(String oauthService, String 
rsAddress) throws Exception {
+        doTestServiceWithJwtTokenAndScope(oauthService, rsAddress, 
"consumer-id");
+    }
+
+    private void doTestServiceWithJwtTokenAndScope(String oauthService,
+                                                   String rsAddress,
+                                                   String clientId) throws 
Exception {
         final AuthorizationMetadata authorizationMetadata = 
OAuthClientUtils.getAuthorizationMetadata(oauthService);
 
         final String scope = "create_book";
 
         final URI authorizationURI = OAuthClientUtils.getAuthorizationURI(
             authorizationMetadata.getAuthorizationEndpoint().toString(),
-            "consumer-id", null, null, scope);
+            clientId, null, null, scope);
 
         // Get Authorization Code
         WebClient oauthClient = WebClient.create(authorizationURI.toString(), 
OAuth2TestUtils.setupProviders(),
@@ -123,7 +175,7 @@ public class OAuth2JwtFiltersTest extends 
AbstractBusClientServerTestBase {
         // Now get the access token
         final ClientAccessToken accessToken = OAuthClientUtils.getAccessToken(
             authorizationMetadata.getTokenEndpoint().toString(),
-            new Consumer("consumer-id", "this-is-a-secret"),
+            new Consumer(clientId, "this-is-a-secret"),
             new AuthorizationCodeGrant(code),
             true);
         assertNotNull(accessToken.getTokenKey());
@@ -133,7 +185,7 @@ public class OAuth2JwtFiltersTest extends 
AbstractBusClientServerTestBase {
             "org/apache/cxf/systest/jaxrs/security/alice.rs.properties", null);
         assertTrue(jwtConsumer.verifySignatureWith(verifier));
         JwtClaims claims = jwtConsumer.getJwtClaims();
-        assertEquals("consumer-id", 
claims.getStringProperty(OAuthConstants.CLIENT_ID));
+        assertEquals(clientId, 
claims.getStringProperty(OAuthConstants.CLIENT_ID));
         assertEquals("alice", claims.getStringProperty("username"));
         
assertTrue(claims.getStringProperty(OAuthConstants.SCOPE).contains(scope));
         // Now invoke on the service with the access token

Reply via email to