Skip to content

Commit

Permalink
KEYCLOAK-13682 NPE when refreshing token after enabling consent
Browse files Browse the repository at this point in the history
  • Loading branch information
martin-kanis authored and stianst committed Apr 30, 2020
1 parent a878bec commit aa309b9
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ public static boolean verifyConsentStillAvailable(KeycloakSession session, UserM
continue;
}

if (!grantedConsent.getGrantedClientScopes().contains(requestedScope)) {
if (grantedConsent == null || !grantedConsent.getGrantedClientScopes().contains(requestedScope)) {
logger.debugf("Client '%s' no longer has requested consent from user '%s' for client scope '%s'",
client.getClientId(), user.getUsername(), requestedScope.getName());
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,26 @@
import org.jboss.arquillian.graphene.page.Page;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.OAuthErrorException;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.UserSessionRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.ConsentPage;
import org.keycloak.testsuite.pages.ErrorPage;
import org.keycloak.testsuite.pages.LoginPage;
Expand All @@ -41,11 +49,15 @@
import java.util.Map;

import static org.junit.Assert.assertEquals;
import static org.keycloak.testsuite.AbstractTestRealmKeycloakTest.TEST_REALM_NAME;
import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
import static org.keycloak.testsuite.admin.ApiUtil.createUserWithAdminClient;
import static org.keycloak.testsuite.admin.ApiUtil.findClientByClientId;
import static org.keycloak.testsuite.admin.ApiUtil.resetUserPassword;
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
import org.keycloak.testsuite.util.OAuthClient.AccessTokenResponse;
import org.keycloak.testsuite.util.OAuthClient.AuthorizationEndpointResponse;

/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
Expand Down Expand Up @@ -149,23 +161,30 @@ protected String getIDPAlias() {
return IDP_OIDC_ALIAS;
}

@Rule
public AssertEvents events = new AssertEvents(this);

@Page
protected LoginPage accountLoginPage;

@Page
protected ConsentPage consentPage;

@Page
protected AppPage appPage;

@Page
protected ErrorPage errorPage;

@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
RealmRepresentation providerRealm = createProviderRealm();
RealmRepresentation consumerRealm = createConsumerRealm();
RealmRepresentation realmRepresentation = loadJson(getClass().getResourceAsStream("/testrealm.json"), RealmRepresentation.class);

testRealms.add(providerRealm);
testRealms.add(consumerRealm);
testRealms.add(realmRepresentation);
}

@Before
Expand Down Expand Up @@ -347,6 +366,38 @@ public void testConsentCancel() {
accountPage.assertCurrent();
}

@Test
public void clientConsentRequiredAfterLogin() {
oauth.realm(TEST_REALM_NAME).clientId("test-app");
AuthorizationEndpointResponse response = oauth.doLogin("test-user@localhost", "password");
AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(response.getCode(), "password");

Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));

EventRepresentation loginEvent = events.expectLogin().detail(Details.USERNAME, "test-user@localhost").assertEvent();
String sessionId = loginEvent.getSessionId();

ClientRepresentation clientRepresentation = adminClient.realm(TEST_REALM_NAME).clients().findByClientId("test-app").get(0);
try {
clientRepresentation.setConsentRequired(true);
adminClient.realm(TEST_REALM_NAME).clients().get(clientRepresentation.getId()).update(clientRepresentation);

events.clear();

// try to refresh the token
// this fails as client no longer has requested consent from user
AccessTokenResponse refreshTokenResponse = oauth.doRefreshTokenRequest(accessTokenResponse.getRefreshToken(), "password");
Assert.assertEquals(OAuthErrorException.INVALID_SCOPE, refreshTokenResponse.getError());
Assert.assertEquals("Client no longer has requested consent from user", refreshTokenResponse.getErrorDescription());

events.expectRefresh(accessTokenResponse.getRefreshToken(), sessionId).clearDetails().error(Errors.INVALID_TOKEN).assertEvent();
} finally {
clientRepresentation.setConsentRequired(false);
adminClient.realm(TEST_REALM_NAME).clients().get(clientRepresentation.getId()).update(clientRepresentation);
}
}

private String getAccountUrl(String realmName) {
return getAuthRoot() + "/auth/realms/" + realmName + "/account";
}
Expand Down

0 comments on commit aa309b9

Please sign in to comment.