keycloak-aplcache
Changes
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCPairwiseClientRegistrationTest.java 116(+116 -0)
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();