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 2019/11/05 17:34:54 UTC

[cxf] branch 3.3.x-fixes updated: Make sure that only the client associated with a token can revoke it

This is an automated email from the ASF dual-hosted git repository.

coheigea pushed a commit to branch 3.3.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git


The following commit(s) were added to refs/heads/3.3.x-fixes by this push:
     new 3aa9f80  Make sure that only the client associated with a token can revoke it
3aa9f80 is described below

commit 3aa9f8011b54b9a1540a61abc24dac75c0b68381
Author: Colm O hEigeartaigh <co...@apache.org>
AuthorDate: Tue Nov 5 16:20:07 2019 +0000

    Make sure that only the client associated with a token can revoke it
    
    (cherry picked from commit a9a7302896c4d52bace3af0ac2e252bf6e4d6ba1)
---
 .../oauth2/provider/AbstractOAuthDataProvider.java | 37 +++++++------
 .../oauth2/grants/RevocationServiceTest.java       | 60 ++++++++++++++++++++++
 2 files changed, 82 insertions(+), 15 deletions(-)

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 3413976..3681b86 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
@@ -199,18 +199,18 @@ public abstract class AbstractOAuthDataProvider implements OAuthDataProvider, Cl
     public ServerAccessToken refreshAccessToken(Client client, String refreshTokenKey,
                                                 List<String> restrictedScopes) throws OAuthServiceException {
         RefreshToken currentRefreshToken = recycleRefreshTokens
-            ? revokeRefreshToken(refreshTokenKey) : getRefreshToken(refreshTokenKey);
+            ? revokeRefreshToken(client, refreshTokenKey) : getRefreshToken(refreshTokenKey);
         if (currentRefreshToken == null) {
             throw new OAuthServiceException(OAuthConstants.ACCESS_DENIED);
         }
         if (OAuthUtils.isExpired(currentRefreshToken.getIssuedAt(), currentRefreshToken.getExpiresIn())) {
             if (!recycleRefreshTokens) {
-                revokeRefreshToken(refreshTokenKey);
+                revokeRefreshToken(client, refreshTokenKey);
             }
             throw new OAuthServiceException(OAuthConstants.ACCESS_DENIED);
         }
         if (recycleRefreshTokens) {
-            revokeAccessTokens(currentRefreshToken);
+            revokeAccessTokens(client, currentRefreshToken);
         }
 
         ServerAccessToken at = doRefreshAccessToken(client, currentRefreshToken, restrictedScopes);
@@ -227,16 +227,17 @@ public abstract class AbstractOAuthDataProvider implements OAuthDataProvider, Cl
     public void revokeToken(Client client, String tokenKey, String tokenTypeHint) throws OAuthServiceException {
         ServerAccessToken accessToken = null;
         if (!OAuthConstants.REFRESH_TOKEN.equals(tokenTypeHint)) {
-            accessToken = revokeAccessToken(tokenKey);
+            accessToken = revokeAccessToken(client, tokenKey);
         }
         if (accessToken != null) {
-            handleLinkedRefreshToken(accessToken);
+            handleLinkedRefreshToken(client, accessToken);
         } else if (!OAuthConstants.ACCESS_TOKEN.equals(tokenTypeHint)) {
-            RefreshToken currentRefreshToken = revokeRefreshToken(tokenKey);
-            revokeAccessTokens(currentRefreshToken);
+            RefreshToken currentRefreshToken = revokeRefreshToken(client, tokenKey);
+            revokeAccessTokens(client, currentRefreshToken);
         }
     }
-    protected void handleLinkedRefreshToken(ServerAccessToken accessToken) {
+
+    protected void handleLinkedRefreshToken(Client client, ServerAccessToken accessToken) {
         if (accessToken != null && accessToken.getRefreshToken() != null) {
             RefreshToken rt = getRefreshToken(accessToken.getRefreshToken());
             if (rt == null) {
@@ -245,7 +246,7 @@ public abstract class AbstractOAuthDataProvider implements OAuthDataProvider, Cl
 
             unlinkRefreshAccessToken(rt, accessToken.getTokenKey());
             if (rt.getAccessTokens().isEmpty()) {
-                revokeRefreshToken(rt.getTokenKey());
+                revokeRefreshToken(client, rt.getTokenKey());
             } else {
                 saveRefreshToken(rt);
             }
@@ -253,10 +254,10 @@ public abstract class AbstractOAuthDataProvider implements OAuthDataProvider, Cl
 
     }
 
-    protected void revokeAccessTokens(RefreshToken currentRefreshToken) {
+    protected void revokeAccessTokens(Client client, RefreshToken currentRefreshToken) {
         if (currentRefreshToken != null) {
             for (String accessTokenKey : currentRefreshToken.getAccessTokens()) {
-                revokeAccessToken(accessTokenKey);
+                revokeAccessToken(client, accessTokenKey);
             }
         }
     }
@@ -495,13 +496,13 @@ public abstract class AbstractOAuthDataProvider implements OAuthDataProvider, Cl
         List<RefreshToken> refreshTokens = getRefreshTokens(c, null);
         if (refreshTokens != null) {
             for (RefreshToken rt : refreshTokens) {
-                revokeRefreshToken(rt.getTokenKey());
+                revokeRefreshToken(c, rt.getTokenKey());
             }
         }
         List<ServerAccessToken> accessTokens = getAccessTokens(c, null);
         if (accessTokens != null) {
             for (ServerAccessToken at : accessTokens) {
-                revokeAccessToken(at.getTokenKey());
+                revokeAccessToken(c, at.getTokenKey());
             }
         }
     }
@@ -549,16 +550,22 @@ public abstract class AbstractOAuthDataProvider implements OAuthDataProvider, Cl
         return null;
     }
 
-    protected ServerAccessToken revokeAccessToken(String accessTokenKey) {
+    protected ServerAccessToken revokeAccessToken(Client client, String accessTokenKey) {
         ServerAccessToken at = getAccessToken(accessTokenKey);
         if (at != null) {
+            if (!at.getClient().getClientId().equals(client.getClientId())) {
+                throw new OAuthServiceException(OAuthConstants.INVALID_GRANT);
+            }
             doRevokeAccessToken(at);
         }
         return at;
     }
-    protected RefreshToken revokeRefreshToken(String refreshTokenKey) {
+    protected RefreshToken revokeRefreshToken(Client client, String refreshTokenKey) {
         RefreshToken refreshToken = getRefreshToken(refreshTokenKey);
         if (refreshToken != null) {
+            if (!refreshToken.getClient().getClientId().equals(client.getClientId())) {
+                throw new OAuthServiceException(OAuthConstants.INVALID_GRANT);
+            }
             doRevokeRefreshToken(refreshToken);
         }
         return refreshToken;
diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/grants/RevocationServiceTest.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/grants/RevocationServiceTest.java
index 057eda6..7847f7a 100644
--- a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/grants/RevocationServiceTest.java
+++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/grants/RevocationServiceTest.java
@@ -201,6 +201,66 @@ public class RevocationServiceTest extends AbstractBusClientServerTestBase {
         assertEquals(400, response.getStatus());
     }
 
+    @org.junit.Test
+    public void testAccessTokenRevocationWrongClient() throws Exception {
+        URL busFile = RevocationServiceTest.class.getResource("client.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
+        String code = OAuth2TestUtils.getAuthorizationCode(client);
+        assertNotNull(code);
+
+        // Now get the access token
+        client = WebClient.create(address, OAuth2TestUtils.setupProviders(),
+                                  "consumer-id", "this-is-a-secret", busFile.toString());
+        // Save the Cookie for the second request...
+        WebClient.getConfig(client).getRequestContext().put(
+            org.apache.cxf.message.Message.MAINTAIN_SESSION, Boolean.TRUE);
+
+        ClientAccessToken accessToken = OAuth2TestUtils.getAccessTokenWithAuthorizationCode(client, code);
+        assertNotNull(accessToken.getTokenKey());
+
+        // Now query the token introspection service to make sure the token is valid
+        client = WebClient.create(address, OAuth2TestUtils.setupProviders(),
+                                  "consumer-id", "this-is-a-secret", busFile.toString());
+        client.accept("application/json").type("application/x-www-form-urlencoded");
+        Form form = new Form();
+        form.param("token", accessToken.getTokenKey());
+        client.path("introspect/");
+        Response response = client.post(form);
+
+        TokenIntrospection tokenIntrospection = response.readEntity(TokenIntrospection.class);
+        assertTrue(tokenIntrospection.isActive());
+
+        // Now try to revoke the token as a different client (this should fail, but no exception is thrown)
+        client = WebClient.create(address, OAuth2TestUtils.setupProviders(),
+                                  "consumer-id-aud", "this-is-a-secret", busFile.toString());
+        client.accept("application/json").type("application/x-www-form-urlencoded");
+        form = new Form();
+        form.param("token", accessToken.getTokenKey());
+        client.path("revoke/");
+        response = client.post(form);
+
+        // Now check the token introspection service again to make sure the token is still valid
+        // (the token revocation call failed)
+        client = WebClient.create(address, OAuth2TestUtils.setupProviders(),
+                                  "consumer-id", "this-is-a-secret", busFile.toString());
+        client.accept("application/json").type("application/x-www-form-urlencoded");
+        form = new Form();
+        form.param("token", accessToken.getTokenKey());
+        client.path("introspect/");
+        response = client.post(form);
+
+        tokenIntrospection = response.readEntity(TokenIntrospection.class);
+        assertTrue(tokenIntrospection.isActive());
+    }
+
     //
     // Server implementations
     //