keycloak-aplcache

Details

diff --git a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
index 07aec65..806b173 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
@@ -120,15 +120,6 @@ public class TokenManager {
     }
 
     public TokenValidation validateToken(KeycloakSession session, UriInfo uriInfo, ClientConnection connection, RealmModel realm, AccessToken oldToken, HttpHeaders headers) throws OAuthErrorException {
-        UserModel user = session.users().getUserById(oldToken.getSubject(), realm);
-        if (user == null) {
-            throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid refresh token", "Unknown user");
-        }
-
-        if (!user.isEnabled()) {
-            throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "User disabled", "User disabled");
-        }
-
         UserSessionModel userSession = null;
         if (TokenUtil.TOKEN_TYPE_OFFLINE.equals(oldToken.getType())) {
 
@@ -156,6 +147,15 @@ public class TokenManager {
             throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Offline user session not found", "Offline user session not found");
         }
 
+        UserModel user = userSession.getUser();
+        if (user == null) {
+            throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid refresh token", "Unknown user");
+        }
+
+        if (!user.isEnabled()) {
+            throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "User disabled", "User disabled");
+        }
+
         ClientModel client = session.getContext().getClient();
         AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessions().get(client.getId());
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCPairwiseClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCPairwiseClientRegistrationTest.java
index 8666e04..0601879 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCPairwiseClientRegistrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCPairwiseClientRegistrationTest.java
@@ -28,6 +28,8 @@ import org.keycloak.client.registration.ClientRegistrationException;
 import org.keycloak.client.registration.HttpErrorException;
 import org.keycloak.protocol.oidc.mappers.SHA256PairwiseSubMapper;
 import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.IDToken;
+import org.keycloak.representations.RefreshToken;
 import org.keycloak.representations.UserInfo;
 import org.keycloak.representations.idm.ClientInitialAccessCreatePresentation;
 import org.keycloak.representations.idm.ClientInitialAccessPresentation;
@@ -41,6 +43,7 @@ import org.keycloak.testsuite.client.resources.TestOIDCEndpointsApplicationResou
 import org.keycloak.testsuite.util.ClientManager;
 import org.keycloak.testsuite.util.OAuthClient;
 import org.keycloak.testsuite.util.UserInfoClientUtil;
+import org.keycloak.testsuite.util.UserManager;
 
 import javax.ws.rs.client.Client;
 import javax.ws.rs.core.Response;
@@ -49,6 +52,8 @@ import java.util.Base64;
 import java.util.Collections;
 import java.util.List;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 public class OIDCPairwiseClientRegistrationTest extends AbstractClientRegistrationTest {
@@ -77,6 +82,14 @@ public class OIDCPairwiseClientRegistrationTest extends AbstractClientRegistrati
         return response;
     }
 
+    public OIDCClientRepresentation createPairwise() throws ClientRegistrationException {
+        // Create pairwise client
+        OIDCClientRepresentation clientRep = createRep();
+        clientRep.setSubjectType("pairwise");
+        OIDCClientRepresentation pairwiseClient = reg.oidc().create(clientRep);
+        return pairwiseClient;
+    }
+
 
     private void assertCreateFail(OIDCClientRepresentation client, int expectedStatusCode, String expectedErrorContains) {
         try {
@@ -351,6 +364,109 @@ public class OIDCPairwiseClientRegistrationTest extends AbstractClientRegistrati
         }
     }
 
+    @Test
+    public void refreshPairwiseToken() throws Exception {
+        // Create pairwise client
+        OIDCClientRepresentation pairwiseClient = createPairwise();
+
+        // Login to pairwise client
+        OAuthClient.AccessTokenResponse accessTokenResponse = login(pairwiseClient, "test-user@localhost", "password");
+
+        // Verify tokens
+        oauth.verifyRefreshToken(accessTokenResponse.getAccessToken());
+        IDToken idToken = oauth.verifyIDToken(accessTokenResponse.getIdToken());
+        oauth.verifyRefreshToken(accessTokenResponse.getRefreshToken());
+
+        // Refresh token
+        OAuthClient.AccessTokenResponse refreshTokenResponse = oauth.doRefreshTokenRequest(accessTokenResponse.getRefreshToken(), pairwiseClient.getClientSecret());
+
+        // Verify refreshed tokens
+        oauth.verifyToken(refreshTokenResponse.getAccessToken());
+        RefreshToken refreshedRefreshToken = oauth.verifyRefreshToken(refreshTokenResponse.getRefreshToken());
+        IDToken refreshedIdToken = oauth.verifyIDToken(refreshTokenResponse.getIdToken());
+
+        // If an ID Token is returned as a result of a token refresh request, the following requirements apply:
+        // its iss Claim Value MUST be the same as in the ID Token issued when the original authentication occurred
+        Assert.assertEquals(idToken.getIssuer(), refreshedRefreshToken.getIssuer());
+
+        // its sub Claim Value MUST be the same as in the ID Token issued when the original authentication occurred
+        Assert.assertEquals(idToken.getSubject(), refreshedRefreshToken.getSubject());
+
+        // its iat Claim MUST represent the time that the new ID Token is issued
+        Assert.assertEquals(refreshedIdToken.getIssuedAt(), refreshedRefreshToken.getIssuedAt());
+
+        // its aud Claim Value MUST be the same as in the ID Token issued when the original authentication occurred
+        Assert.assertArrayEquals(idToken.getAudience(), refreshedRefreshToken.getAudience());
+
+        // if the ID Token contains an auth_time Claim, its value MUST represent the time of the original authentication
+        // - not the time that the new ID token is issued
+        Assert.assertEquals(idToken.getAuthTime(), refreshedIdToken.getAuthTime());
+
+        // its azp Claim Value MUST be the same as in the ID Token issued when the original authentication occurred; if
+        // no azp Claim was present in the original ID Token, one MUST NOT be present in the new ID Token
+        Assert.assertEquals(idToken.getIssuedFor(), refreshedIdToken.getIssuedFor());
+    }
+
+    @Test
+    public void refreshPairwiseTokenDeletedUser() throws Exception {
+        String userId = createUser(REALM_NAME, "delete-me@localhost", "password");
+
+        // Create pairwise client
+        OIDCClientRepresentation pairwiseClient = createPairwise();
+
+        // Login to pairwise client
+        oauth.clientId(pairwiseClient.getClientId());
+        oauth.clientId(pairwiseClient.getClientId());
+        OAuthClient.AuthorizationEndpointResponse loginResponse = oauth.doLogin("delete-me@localhost", "password");
+        OAuthClient.AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(loginResponse.getCode(), pairwiseClient.getClientSecret());
+
+        assertEquals(200, accessTokenResponse.getStatusCode());
+
+        // Delete user
+        adminClient.realm(REALM_NAME).users().delete(userId);
+
+        OAuthClient.AccessTokenResponse refreshTokenResponse = oauth.doRefreshTokenRequest(accessTokenResponse.getRefreshToken(), pairwiseClient.getClientSecret());
+        assertEquals(400, refreshTokenResponse.getStatusCode());
+        assertEquals("invalid_grant", refreshTokenResponse.getError());
+        assertNull(refreshTokenResponse.getAccessToken());
+        assertNull(refreshTokenResponse.getIdToken());
+        assertNull(refreshTokenResponse.getRefreshToken());
+    }
+
+    @Test
+    public void refreshPairwiseTokenDisabledUser() throws Exception {
+        createUser(REALM_NAME, "disable-me@localhost", "password");
+
+        // Create pairwise client
+        OIDCClientRepresentation pairwiseClient = createPairwise();
+
+        // Login to pairwise client
+        oauth.clientId(pairwiseClient.getClientId());
+        oauth.clientId(pairwiseClient.getClientId());
+        OAuthClient.AuthorizationEndpointResponse loginResponse = oauth.doLogin("disable-me@localhost", "password");
+        OAuthClient.AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(loginResponse.getCode(), pairwiseClient.getClientSecret());
+        assertEquals(200, accessTokenResponse.getStatusCode());
+
+        try {
+            UserManager.realm(adminClient.realm(REALM_NAME)).username("disable-me@localhost").enabled(false);
+
+            OAuthClient.AccessTokenResponse refreshTokenResponse = oauth.doRefreshTokenRequest(accessTokenResponse.getRefreshToken(), pairwiseClient.getClientSecret());
+            assertEquals(400, refreshTokenResponse.getStatusCode());
+            assertEquals("invalid_grant", refreshTokenResponse.getError());
+            assertNull(refreshTokenResponse.getAccessToken());
+            assertNull(refreshTokenResponse.getIdToken());
+            assertNull(refreshTokenResponse.getRefreshToken());
+        } finally {
+            UserManager.realm(adminClient.realm(REALM_NAME)).username("disable-me@localhost").enabled(true);
+        }
+    }
+
+    private OAuthClient.AccessTokenResponse login(OIDCClientRepresentation client, String username, String password) {
+        oauth.clientId(client.getClientId());
+        OAuthClient.AuthorizationEndpointResponse loginResponse = oauth.doLogin(username, password);
+        return oauth.doAccessTokenRequest(loginResponse.getCode(), client.getClientSecret());
+    }
+
     private String getPayload(String token) {
         String payloadBase64 = token.split("\\.")[1];
         return new String(Base64.getDecoder().decode(payloadBase64));
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java
index 208e6e3..af6cbe8 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java
@@ -27,15 +27,18 @@ import org.keycloak.events.Details;
 import org.keycloak.events.Errors;
 import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
 import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.IDToken;
 import org.keycloak.representations.RefreshToken;
 import org.keycloak.representations.idm.EventRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
 import org.keycloak.testsuite.AbstractKeycloakTest;
 import org.keycloak.testsuite.AssertEvents;
 import org.keycloak.testsuite.util.ClientManager;
 import org.keycloak.testsuite.util.OAuthClient;
 import org.keycloak.testsuite.util.RealmBuilder;
 import org.keycloak.testsuite.util.RealmManager;
+import org.keycloak.testsuite.util.UserManager;
 import org.keycloak.util.BasicAuthHelper;
 
 import javax.ws.rs.client.Client;
@@ -488,6 +491,61 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
 
     }
 
+    @Test
+    public void refreshTokenUserDisabled() throws Exception {
+        oauth.doLogin("test-user@localhost", "password");
+
+        EventRepresentation loginEvent = events.expectLogin().assertEvent();
+
+        String sessionId = loginEvent.getSessionId();
+        String codeId = loginEvent.getDetails().get(Details.CODE_ID);
+
+        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+
+        OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
+        String refreshTokenString = response.getRefreshToken();
+        RefreshToken refreshToken = oauth.verifyRefreshToken(refreshTokenString);
+
+        events.expectCodeToToken(codeId, sessionId).assertEvent();
+
+        try {
+            UserManager.realm(adminClient.realm("test")).username("test-user@localhost").enabled(false);
+            response = oauth.doRefreshTokenRequest(refreshTokenString, "password");
+            assertEquals(400, response.getStatusCode());
+            assertEquals("invalid_grant", response.getError());
+
+            events.expectRefresh(refreshToken.getId(), sessionId).clearDetails().error(Errors.INVALID_TOKEN).assertEvent();
+        } finally {
+            UserManager.realm(adminClient.realm("test")).username("test-user@localhost").enabled(true);
+        }
+    }
+
+    @Test
+    public void refreshTokenUserDeleted() throws Exception {
+        String userId = createUser("test", "temp-user@localhost", "password");
+        oauth.doLogin("temp-user@localhost", "password");
+
+        EventRepresentation loginEvent = events.expectLogin().user(userId).assertEvent();
+
+        String sessionId = loginEvent.getSessionId();
+        String codeId = loginEvent.getDetails().get(Details.CODE_ID);
+
+        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+
+        OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
+        String refreshTokenString = response.getRefreshToken();
+        RefreshToken refreshToken = oauth.verifyRefreshToken(refreshTokenString);
+
+        events.expectCodeToToken(codeId, sessionId).user(userId).assertEvent();
+
+        UserManager.realm(adminClient.realm("test")).username("temp-user@localhost").enabled(false);
+        response = oauth.doRefreshTokenRequest(refreshTokenString, "password");
+        assertEquals(400, response.getStatusCode());
+        assertEquals("invalid_grant", response.getError());
+
+        events.expectRefresh(refreshToken.getId(), sessionId).user(userId).clearDetails().error(Errors.INVALID_TOKEN).assertEvent();
+    }
+
     protected Response executeRefreshToken(WebTarget refreshTarget, String refreshToken) {
         String header = BasicAuthHelper.createHeader("test-app", "password");
         Form form = new Form();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/UserManager.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/UserManager.java
index ed797b5..6bd3789 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/UserManager.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/UserManager.java
@@ -61,6 +61,12 @@ public class UserManager {
             userResource.update(user);
         }
 
+        public void enabled(Boolean enabled) {
+            UserRepresentation user = userResource.toRepresentation();
+            user.setEnabled(enabled);
+            userResource.update(user);
+        }
+
 
         private UserRepresentation initializeRequiredActions() {
             UserRepresentation user = userResource.toRepresentation();