Skip to content

Commit

Permalink
Verify if token is revoked when validating bearer tokens (#16389)
Browse files Browse the repository at this point in the history
  • Loading branch information
pedroigor committed Jan 11, 2023
1 parent 6c0e66a commit 803ae18
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1335,7 +1335,7 @@ public static NotBeforeCheck forModel(KeycloakSession session, RealmModel realmM
/**
* 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;

Expand All @@ -1344,7 +1344,7 @@ public TokenRevocationCheck(KeycloakSession session) {
}

@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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@
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;
Expand Down Expand Up @@ -94,7 +94,7 @@ public static TokenVerification verifyToken(KeycloakSession session, RealmModel
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,13 @@ void authNoAccess() {
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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
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;
Expand All @@ -42,6 +43,7 @@
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;
Expand Down Expand Up @@ -171,6 +173,41 @@ public void registerClientAsAdminWithNoAccess() throws ClientRegistrationExcepti
}
}

@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();
Expand Down

0 comments on commit 803ae18

Please sign in to comment.