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
