Verify if token is revoked when validating bearer tokens (#16394)

Closes #16388
This commit is contained in:
Pedro Igor 2023-01-11 05:42:29 -08:00 committed by GitHub
parent 3d62dc4254
commit 9945135861
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 47 additions and 6 deletions

View file

@ -1332,7 +1332,7 @@ public class TokenManager {
/**
* Check if access token was revoked with OAuth revocation endpoint
*/
public static class TokenRevocationCheck implements TokenVerifier.Predicate<AccessToken> {
public static class TokenRevocationCheck implements TokenVerifier.Predicate<JsonWebToken> {
private final KeycloakSession session;
@ -1341,7 +1341,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);
}

View file

@ -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<JsonWebToken> 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);

View file

@ -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);
}

View file

@ -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.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
@ -46,6 +47,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;
@ -176,6 +178,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();