From 803ae181534bb803613fde6119f1bc4bd9478ec6 Mon Sep 17 00:00:00 2001 From: Pedro Igor Date: Wed, 11 Jan 2023 05:42:31 -0800 Subject: [PATCH] Verify if token is revoked when validating bearer tokens (#16389) --- .../keycloak/protocol/oidc/TokenManager.java | 4 +- .../ClientRegistrationTokenUtils.java | 4 +- .../AbstractClientRegistrationTest.java | 8 +++- .../client/ClientRegistrationTest.java | 37 +++++++++++++++++++ 4 files changed, 47 insertions(+), 6 deletions(-) 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 15e6cbe8561..1772e37bdaa 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java @@ -1335,7 +1335,7 @@ public class TokenManager { /** * Check if access token was revoked with OAuth revocation endpoint */ - public static class TokenRevocationCheck implements TokenVerifier.Predicate { + public static class TokenRevocationCheck implements TokenVerifier.Predicate { private final KeycloakSession session; @@ -1344,7 +1344,7 @@ public class TokenManager { } @Override - public boolean test(AccessToken token) { + public boolean test(JsonWebToken token) { SingleUseObjectProvider singleUseStore = session.getProvider(SingleUseObjectProvider.class); return !singleUseStore.contains(token.getId() + SingleUseObjectProvider.REVOKED_KEY); } diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationTokenUtils.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationTokenUtils.java index efcf05b4b6e..c10df835bf6 100755 --- a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationTokenUtils.java +++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationTokenUtils.java @@ -27,10 +27,10 @@ import org.keycloak.crypto.SignatureVerifierContext; import org.keycloak.jose.jws.JWSBuilder; import org.keycloak.models.ClientInitialAccessModel; import org.keycloak.models.ClientModel; -import org.keycloak.models.TokenManager; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.protocol.oidc.TokenManager.TokenRevocationCheck; import org.keycloak.representations.JsonWebToken; import org.keycloak.services.Urls; import org.keycloak.services.clientregistration.policy.RegistrationAuth; @@ -94,7 +94,7 @@ public class ClientRegistrationTokenUtils { JsonWebToken jwt; try { TokenVerifier verifier = TokenVerifier.create(token, JsonWebToken.class) - .withChecks(new TokenVerifier.RealmUrlCheck(getIssuer(session, realm)), TokenVerifier.IS_ACTIVE); + .withChecks(new TokenVerifier.RealmUrlCheck(getIssuer(session, realm)), TokenVerifier.IS_ACTIVE, new TokenRevocationCheck(session)); SignatureVerifierContext verifierContext = session.getProvider(SignatureProvider.class, verifier.getHeader().getAlgorithm().name()).verifier(verifier.getHeader().getKeyId()); verifier.verifierContext(verifierContext); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientRegistrationTest.java index b678a23659e..414b6c3d00e 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientRegistrationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientRegistrationTest.java @@ -128,9 +128,13 @@ public abstract class AbstractClientRegistrationTest extends AbstractKeycloakTes reg.auth(Auth.token(getToken("no-access", "password"))); } - private String getToken(String username, String password) { + protected String getToken(String username, String password) { + return getToken(Constants.ADMIN_CLI_CLIENT_ID, null, username, password); + } + + protected String getToken(String clientId, String clientSecret, String username, String password) { try { - return oauth.doGrantAccessTokenRequest(REALM_NAME, username, password, null, Constants.ADMIN_CLI_CLIENT_ID, null).getAccessToken(); + return oauth.doGrantAccessTokenRequest(REALM_NAME, username, password, null, clientId, clientSecret).getAccessToken(); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java index 6e0e22dc343..5820041639e 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java @@ -32,6 +32,7 @@ import org.keycloak.client.registration.Auth; import org.keycloak.client.registration.ClientRegistration; import org.keycloak.client.registration.ClientRegistrationException; import org.keycloak.client.registration.HttpErrorException; +import org.keycloak.events.Errors; import org.keycloak.models.Constants; import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper; import org.keycloak.representations.idm.ClientRepresentation; @@ -42,6 +43,7 @@ import org.keycloak.testsuite.arquillian.annotation.UncaughtServerErrorExpected; import org.keycloak.util.JsonSerialization; import javax.ws.rs.NotFoundException; +import javax.ws.rs.core.Response; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; @@ -171,6 +173,41 @@ public class ClientRegistrationTest extends AbstractClientRegistrationTest { } } + @Test + public void registerClientUsingRevokedToken() throws Exception { + reg.auth(Auth.token(getToken("manage-clients", "password"))); + + ClientRepresentation myclient = new ClientRepresentation(); + + myclient.setClientId("myclient"); + myclient.setServiceAccountsEnabled(true); + myclient.setSecret("password"); + myclient.setDirectAccessGrantsEnabled(true); + + reg.create(myclient); + + oauth.clientId("myclient"); + String bearerToken = getToken("myclient", "password", "manage-clients", "password"); + try (CloseableHttpResponse response = oauth.doTokenRevoke(bearerToken, "access_token", "password")) { + assertEquals(Response.Status.OK.getStatusCode(), response.getStatusLine().getStatusCode()); + } + + try { + reg.auth(Auth.token(bearerToken)); + + ClientRepresentation clientRep = buildClient(); + clientRep.setServiceAccountsEnabled(true); + + registerClient(clientRep); + } catch (ClientRegistrationException cre) { + HttpErrorException cause = (HttpErrorException) cre.getCause(); + assertEquals(401, cause.getStatusLine().getStatusCode()); + OAuth2ErrorRepresentation error = cause.toErrorRepresentation(); + assertEquals(Errors.INVALID_TOKEN, error.getError()); + assertEquals("Failed decode token", error.getErrorDescription()); + } + } + @Test public void registerClientWithNonAsciiChars() throws ClientRegistrationException { authCreateClients();