diff --git a/events/api/src/main/java/org/keycloak/events/Details.java b/events/api/src/main/java/org/keycloak/events/Details.java index 1a9c479b35..4a0a1ad007 100755 --- a/events/api/src/main/java/org/keycloak/events/Details.java +++ b/events/api/src/main/java/org/keycloak/events/Details.java @@ -27,4 +27,9 @@ public interface Details { String CLIENT_SESSION_STATE = "client_session_state"; String CLIENT_SESSION_HOST = "client_session_host"; + String CONSENT = "consent"; + String CONSENT_VALUE_NO_CONSENT_REQUIRED = "no_consent_required"; // No consent is required by client + String CONSENT_VALUE_CONSENT_GRANTED = "consent_granted"; // Consent granted by user + String CONSENT_VALUE_PERSISTED_CONSENT = "persistent_consent"; // Persistent consent used (was already granted by user before) + } diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java index e76ab3aee9..f200130794 100755 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java @@ -465,7 +465,6 @@ public class AuthenticationManager { } if (client.isConsentRequired()) { - accessCode.setAction(ClientSessionModel.Action.OAUTH_GRANT); UserConsentModel grantedConsent = user.getConsentByClient(client.getId()); @@ -496,11 +495,18 @@ public class AuthenticationManager { // Skip grant screen if everything was already approved by this user if (realmRoles.size() > 0 || resourceRoles.size() > 0 || protocolMappers.size() > 0) { + accessCode.setAction(ClientSessionModel.Action.OAUTH_GRANT); + return session.getProvider(LoginFormsProvider.class) .setClientSessionCode(accessCode.getCode()) .setAccessRequest(realmRoles, resourceRoles, protocolMappers) .createOAuthGrant(clientSession); + } else { + String consentDetail = (grantedConsent != null) ? Details.CONSENT_VALUE_PERSISTED_CONSENT : Details.CONSENT_VALUE_NO_CONSENT_REQUIRED; + event.detail(Details.CONSENT, consentDetail); } + } else { + event.detail(Details.CONSENT, Details.CONSENT_VALUE_NO_CONSENT_REQUIRED); } event.success(); diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java index c2571d9e8c..08a05c9d5e 100755 --- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java +++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java @@ -316,7 +316,7 @@ public class LoginActionsService { return ErrorPage.error(session, Messages.UNKNOWN_LOGIN_REQUESTER); } if (!client.isEnabled()) { - event.error(Errors.CLIENT_NOT_FOUND); + event.error(Errors.CLIENT_DISABLED); return ErrorPage.error(session, Messages.LOGIN_REQUESTER_NOT_ENABLED); } @@ -443,7 +443,7 @@ public class LoginActionsService { return ErrorPage.error(session, Messages.UNKNOWN_LOGIN_REQUESTER); } if (!client.isEnabled()) { - event.error(Errors.CLIENT_NOT_FOUND); + event.error(Errors.CLIENT_DISABLED); return ErrorPage.error(session, Messages.LOGIN_REQUESTER_NOT_ENABLED); } @@ -741,6 +741,7 @@ public class LoginActionsService { } user.updateConsent(grantedConsent); + event.detail(Details.CONSENT, Details.CONSENT_VALUE_CONSENT_GRANTED); event.success(); return authManager.redirectAfterSuccessfulFlow(session, realm, userSession, clientSession, request, uriInfo, clientConnection); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java index ce805271c6..21e51907e6 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java @@ -113,7 +113,7 @@ public class AssertEvents implements TestRule, EventListenerProviderFactory { } public ExpectedEvent expectRequiredAction(EventType event) { - return expectLogin().event(event).session(isUUID()); + return expectLogin().event(event).removeDetail(Details.CONSENT).session(isUUID()); } public ExpectedEvent expectLogin() { @@ -123,6 +123,7 @@ public class AssertEvents implements TestRule, EventListenerProviderFactory { .detail(Details.RESPONSE_TYPE, "code") .detail(Details.AUTH_METHOD, "form") .detail(Details.REDIRECT_URI, DEFAULT_REDIRECT_URI) + .detail(Details.CONSENT, Details.CONSENT_VALUE_NO_CONSENT_REQUIRED) .session(isUUID()); } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java index 0f1eba124e..4fafbb300f 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java @@ -242,7 +242,9 @@ public class AccountTest { Assert.assertEquals("Invalid username or password.", loginPage.getError()); - events.expectLogin().session((String) null).error("invalid_user_credentials").assertEvent(); + events.expectLogin().session((String) null).error("invalid_user_credentials") + .removeDetail(Details.CONSENT) + .assertEvent(); loginPage.open(); loginPage.login("test-user@localhost", "new-password"); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java index 644fff31bf..03da92f0b8 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java @@ -127,7 +127,10 @@ public class LoginTest { Assert.assertEquals("Invalid username or password.", loginPage.getError()); - events.expectLogin().user(userId).session((String) null).error("invalid_user_credentials").detail(Details.USERNAME, "login-test").assertEvent(); + events.expectLogin().user(userId).session((String) null).error("invalid_user_credentials") + .detail(Details.USERNAME, "login-test") + .removeDetail(Details.CONSENT) + .assertEvent(); } @Test @@ -147,7 +150,10 @@ public class LoginTest { Assert.assertEquals("Invalid username or password.", loginPage.getError()); - events.expectLogin().user(userId).session((String) null).error("invalid_user_credentials").detail(Details.USERNAME, "login-test").assertEvent(); + events.expectLogin().user(userId).session((String) null).error("invalid_user_credentials") + .detail(Details.USERNAME, "login-test") + .removeDetail(Details.CONSENT) + .assertEvent(); } finally { keycloakRule.configure(new KeycloakRule.KeycloakSetup() { @Override @@ -175,7 +181,10 @@ public class LoginTest { Assert.assertEquals("Account is disabled, contact admin.", loginPage.getError()); - events.expectLogin().user(userId).session((String) null).error("user_disabled").detail(Details.USERNAME, "login-test").assertEvent(); + events.expectLogin().user(userId).session((String) null).error("user_disabled") + .detail(Details.USERNAME, "login-test") + .removeDetail(Details.CONSENT) + .assertEvent(); } finally { keycloakRule.configure(new KeycloakRule.KeycloakSetup() { @Override @@ -195,7 +204,10 @@ public class LoginTest { Assert.assertEquals("Invalid username or password.", loginPage.getError()); - events.expectLogin().user((String) null).session((String) null).error("user_not_found").detail(Details.USERNAME, "invalid").assertEvent(); + events.expectLogin().user((String) null).session((String) null).error("user_not_found") + .detail(Details.USERNAME, "invalid") + .removeDetail(Details.CONSENT) + .assertEvent(); } @Test @@ -413,7 +425,10 @@ public class LoginTest { Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType()); Assert.assertEquals("access_denied", oauth.getCurrentQuery().get(OAuth2Constants.ERROR)); - events.expectLogin().error("rejected_by_user").user((String) null).session((String) null).removeDetail(Details.USERNAME).assertEvent(); + events.expectLogin().error("rejected_by_user").user((String) null).session((String) null) + .removeDetail(Details.USERNAME) + .removeDetail(Details.CONSENT) + .assertEvent(); } // KEYCLOAK-1037 @@ -427,7 +442,10 @@ public class LoginTest { loginPage.assertCurrent(); Assert.assertEquals("Login timeout. Please login again.", loginPage.getError()); - events.expectLogin().user((String) null).session((String) null).error("expired_code").clearDetails().detail(Details.CODE_ID, AssertEvents.isCodeId()).assertEvent(); + events.expectLogin().user((String) null).session((String) null).error("expired_code").clearDetails() + .detail(Details.CODE_ID, AssertEvents.isCodeId()) + .removeDetail(Details.CONSENT) + .assertEvent(); } finally { Time.setOffset(0); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java index 45795dac81..8eea530409 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java @@ -114,7 +114,9 @@ public class LoginTotpTest { loginPage.assertCurrent(); Assert.assertEquals("Invalid username or password.", loginPage.getError()); - events.expectLogin().error("invalid_user_credentials").session((String) null).assertEvent(); + events.expectLogin().error("invalid_user_credentials").session((String) null) + .removeDetail(Details.CONSENT) + .assertEvent(); } @Test @@ -140,7 +142,9 @@ public class LoginTotpTest { Assert.assertEquals("Invalid username or password.", loginPage.getError()); - events.expectLogin().error("invalid_user_credentials").session((String) null).assertEvent(); + events.expectLogin().error("invalid_user_credentials").session((String) null) + .removeDetail(Details.CONSENT) + .assertEvent(); } @Test @@ -159,7 +163,8 @@ public class LoginTotpTest { Assert.assertEquals("Invalid username or password.", loginPage.getError()); AssertEvents.ExpectedEvent expectedEvent = events.expectLogin().error("invalid_user_credentials") - .session((String) null); + .session((String) null) + .removeDetail(Details.CONSENT); expectedEvent.assertEvent(); } finally { Time.setOffset(0); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java index 0a70da0ef9..784318f5f4 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java @@ -141,6 +141,7 @@ public class AuthorizationCodeTest { events.expectLogin().error("rejected_by_user").user((String) null).session((String) null) .removeDetail(Details.USERNAME) + .removeDetail(Details.CONSENT) .detail(Details.REDIRECT_URI, "http://localhost:8081/auth/realms/test/protocol/openid-connect/oauth/oob") .assertEvent().getDetails().get(Details.CODE_ID); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java index 939231f031..bfff4bda6a 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java @@ -36,7 +36,6 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; import org.keycloak.protocol.oidc.OIDCLoginProtocol; -import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory; import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper; import org.keycloak.representations.AccessToken; import org.keycloak.services.managers.RealmManager; @@ -51,7 +50,6 @@ import org.keycloak.testsuite.rule.WebResource; import org.keycloak.testsuite.rule.WebRule; import org.openqa.selenium.WebDriver; -import java.io.IOException; import java.util.Map; import static org.junit.Assert.assertEquals; @@ -104,7 +102,10 @@ public class OAuthGrantTest { Assert.assertTrue(oauth.getCurrentQuery().containsKey(OAuth2Constants.CODE)); - Event loginEvent = events.expectLogin().client("third-party").assertEvent(); + Event loginEvent = events.expectLogin() + .client("third-party") + .detail(Details.CONSENT, Details.CONSENT_VALUE_CONSENT_GRANTED) + .assertEvent(); String codeId = loginEvent.getDetails().get(Details.CODE_ID); String sessionId = loginEvent.getSessionId(); @@ -147,7 +148,11 @@ public class OAuthGrantTest { Assert.assertTrue(oauth.getCurrentQuery().containsKey(OAuth2Constants.ERROR)); assertEquals("access_denied", oauth.getCurrentQuery().get(OAuth2Constants.ERROR)); - events.expectLogin().client("third-party").error("rejected_by_user").assertEvent(); + events.expectLogin() + .client("third-party") + .error("rejected_by_user") + .removeDetail(Details.CONSENT) + .assertEvent(); } @Test @@ -159,7 +164,10 @@ public class OAuthGrantTest { grantPage.assertCurrent(); grantPage.accept(); - events.expectLogin().client("third-party").assertEvent(); + events.expectLogin() + .client("third-party") + .detail(Details.CONSENT, Details.CONSENT_VALUE_CONSENT_GRANTED) + .assertEvent(); // Assert permissions granted on Account mgmt. applications page accountAppsPage.open(); @@ -172,7 +180,11 @@ public class OAuthGrantTest { // Open login form and assert grantPage not shown oauth.openLoginForm(); appPage.assertCurrent(); - events.expectLogin().detail(Details.AUTH_METHOD, "sso").removeDetail(Details.USERNAME).client("third-party").assertEvent(); + events.expectLogin() + .detail(Details.AUTH_METHOD, "sso") + .detail(Details.CONSENT, Details.CONSENT_VALUE_PERSISTED_CONSENT) + .removeDetail(Details.USERNAME) + .client("third-party").assertEvent(); // Revoke grant in account mgmt. accountAppsPage.open(); @@ -219,7 +231,10 @@ public class OAuthGrantTest { // Confirm grant page grantPage.assertCurrent(); grantPage.accept(); - events.expectLogin().client("third-party").assertEvent(); + events.expectLogin() + .client("third-party") + .detail(Details.CONSENT, Details.CONSENT_VALUE_CONSENT_GRANTED) + .assertEvent(); // Assert new role and protocol mapper not in account mgmt. accountAppsPage.open(); @@ -235,7 +250,10 @@ public class OAuthGrantTest { Assert.assertTrue(driver.getPageSource().contains("new-role")); Assert.assertTrue(driver.getPageSource().contains(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME)); grantPage.accept(); - events.expectLogin().client("third-party").assertEvent(); + events.expectLogin() + .client("third-party") + .detail(Details.CONSENT, Details.CONSENT_VALUE_CONSENT_GRANTED) + .assertEvent(); // Go to account mgmt. Everything is granted now accountAppsPage.open(); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ResourceOwnerPasswordCredentialsGrantTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ResourceOwnerPasswordCredentialsGrantTest.java index 59ba8aabd7..23d2e5e907 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ResourceOwnerPasswordCredentialsGrantTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ResourceOwnerPasswordCredentialsGrantTest.java @@ -93,6 +93,7 @@ public class ResourceOwnerPasswordCredentialsGrantTest { .detail(Details.USERNAME, login) .removeDetail(Details.CODE_ID) .removeDetail(Details.REDIRECT_URI) + .removeDetail(Details.CONSENT) .assertEvent(); assertEquals(accessToken.getSessionState(), refreshToken.getSessionState()); @@ -128,6 +129,7 @@ public class ResourceOwnerPasswordCredentialsGrantTest { .detail(Details.REFRESH_TOKEN_ID, refreshToken.getId()) .removeDetail(Details.CODE_ID) .removeDetail(Details.REDIRECT_URI) + .removeDetail(Details.CONSENT) .assertEvent(); HttpResponse logoutResponse = oauth.doLogout(response.getRefreshToken(), "secret"); @@ -180,6 +182,7 @@ public class ResourceOwnerPasswordCredentialsGrantTest { .detail(Details.RESPONSE_TYPE, "token") .removeDetail(Details.CODE_ID) .removeDetail(Details.REDIRECT_URI) + .removeDetail(Details.CONSENT) .error(Errors.INVALID_USER_CREDENTIALS) .assertEvent(); } @@ -203,6 +206,7 @@ public class ResourceOwnerPasswordCredentialsGrantTest { .detail(Details.USERNAME, "invalid") .removeDetail(Details.CODE_ID) .removeDetail(Details.REDIRECT_URI) + .removeDetail(Details.CONSENT) .error(Errors.INVALID_USER_CREDENTIALS) .assertEvent(); }