keycloak-uncached

Details

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 0db650c..b484d5f 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -40,6 +40,7 @@ import org.keycloak.models.utils.SessionTimeoutHelper;
 import org.keycloak.models.utils.SystemClientUtil;
 import org.keycloak.protocol.LoginProtocol;
 import org.keycloak.protocol.LoginProtocol.Error;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.protocol.oidc.TokenManager;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.services.ServicesLogger;
@@ -53,6 +54,7 @@ import org.keycloak.services.util.P3PHelper;
 import org.keycloak.sessions.AuthenticationSessionModel;
 import org.keycloak.sessions.CommonClientSessionModel;
 import org.keycloak.sessions.RootAuthenticationSessionModel;
+import org.keycloak.util.TokenUtil;
 
 import javax.crypto.SecretKey;
 import javax.ws.rs.core.Cookie;
@@ -863,7 +865,7 @@ public class AuthenticationManager {
 
         if (client.isConsentRequired()) {
 
-            UserConsentModel grantedConsent = session.users().getConsentByClient(realm, user.getId(), client.getId());
+            UserConsentModel grantedConsent = getEffectiveGrantedConsent(session, authSession);
 
             // See if any clientScopes need to be approved on consent screen
             List<ClientScopeModel> clientScopesToApprove = getClientScopesToApproveOnConsentScreen(realm, grantedConsent, authSession);
@@ -881,6 +883,21 @@ public class AuthenticationManager {
     }
 
 
+    private static UserConsentModel getEffectiveGrantedConsent(KeycloakSession session, AuthenticationSessionModel authSession) {
+        // If prompt=consent, we ignore existing persistent consent
+        String prompt = authSession.getClientNote(OIDCLoginProtocol.PROMPT_PARAM);
+        if (TokenUtil.hasPrompt(prompt, OIDCLoginProtocol.PROMPT_VALUE_CONSENT)) {
+            return null;
+        } else {
+            final RealmModel realm = authSession.getRealm();
+            final UserModel user = authSession.getAuthenticatedUser();
+            final ClientModel client = authSession.getClient();
+
+            return session.users().getConsentByClient(realm, user.getId(), client.getId());
+        }
+    }
+
+
     public static Response actionRequired(final KeycloakSession session, final AuthenticationSessionModel authSession,
                                                          final ClientConnection clientConnection,
                                                          final HttpRequest request, final UriInfo uriInfo, final EventBuilder event) {
@@ -906,7 +923,7 @@ public class AuthenticationManager {
 
         if (client.isConsentRequired()) {
 
-            UserConsentModel grantedConsent = session.users().getConsentByClient(realm, user.getId(), client.getId());
+            UserConsentModel grantedConsent = getEffectiveGrantedConsent(session, authSession);
 
             List<ClientScopeModel> clientScopesToApprove = getClientScopesToApproveOnConsentScreen(realm, grantedConsent, authSession);
 
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 a7abd4e..90a4ecb 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -834,16 +834,24 @@ public class LoginActionsService {
             session.users().addConsent(realm, user.getId(), grantedConsent);
         }
 
+        // Update may not be required if all clientScopes were already granted (May happen for example with prompt=consent)
+        boolean updateConsentRequired = false;
+
         for (String clientScopeId : authSession.getClientScopes()) {
             ClientScopeModel clientScope = KeycloakModelUtils.findClientScopeById(realm, clientScopeId);
             if (clientScope != null) {
-                grantedConsent.addGrantedClientScope(clientScope);
+                if (!grantedConsent.isClientScopeGranted(clientScope)) {
+                    grantedConsent.addGrantedClientScope(clientScope);
+                    updateConsentRequired = true;
+                }
             } else {
-                logger.warn("Client scope with ID '%s' not found");
+                logger.warnf("Client scope with ID '%s' not found", clientScopeId);
             }
         }
 
-        session.users().updateConsent(realm, user.getId(), grantedConsent);
+        if (updateConsentRequired) {
+            session.users().updateConsent(realm, user.getId(), grantedConsent);
+        }
 
         event.detail(Details.CONSENT, Details.CONSENT_VALUE_CONSENT_GRANTED);
         event.success();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCAdvancedRequestParamsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCAdvancedRequestParamsTest.java
index a3f7e66..65d3897 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCAdvancedRequestParamsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCAdvancedRequestParamsTest.java
@@ -26,6 +26,7 @@ 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.UserResource;
 import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator;
 import org.keycloak.common.util.Time;
 import org.keycloak.events.Details;
@@ -67,6 +68,8 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import javax.ws.rs.core.UriBuilder;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -288,6 +291,10 @@ public class OIDCAdvancedRequestParamsTest extends AbstractTestRealmKeycloakTest
                     .assertEvent();
 
         } finally {
+            // Revert consent
+            UserResource user = ApiUtil.findUserByUsernameId(adminClient.realm("test"), "test-user@localhost");
+            user.revokeConsent("test-app");
+
             //  revert require consent
             ClientManager.realm(adminClient.realm("test")).clientId("test-app").consentRequired(false);
         }
@@ -363,6 +370,68 @@ public class OIDCAdvancedRequestParamsTest extends AbstractTestRealmKeycloakTest
         Assert.assertTrue(errorPage.getError().startsWith("You are already authenticated as different user"));
     }
 
+
+    // prompt=consent
+    @Test
+    public void promptConsent() {
+        // Require consent
+        ClientManager.realm(adminClient.realm("test")).clientId("test-app").consentRequired(true);
+
+        try {
+            // Login user
+            loginPage.open();
+            loginPage.login("test-user@localhost", "password");
+
+            // Grant consent
+            grantPage.assertCurrent();
+            grantPage.accept();
+
+            appPage.assertCurrent();
+            Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
+
+            events.expectLogin()
+                    .detail(Details.USERNAME, "test-user@localhost")
+                    .detail(Details.CONSENT, Details.CONSENT_VALUE_CONSENT_GRANTED)
+                    .assertEvent();
+
+
+            // Re-login without prompt=consent. The previous persistent consent was used
+            driver.navigate().to(oauth.getLoginFormUrl());
+            Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
+            events.expectLogin()
+                    .detail(Details.USERNAME, "test-user@localhost")
+                    .detail(Details.CONSENT, Details.CONSENT_VALUE_PERSISTED_CONSENT)
+                    .assertEvent();
+
+            // Re-login with prompt=consent.
+            String loginFormUri = UriBuilder.fromUri(oauth.getLoginFormUrl())
+                    .queryParam(OIDCLoginProtocol.PROMPT_PARAM, OIDCLoginProtocol.PROMPT_VALUE_CONSENT)
+                    .build().toString();
+            driver.navigate().to(loginFormUri);
+
+            // Assert grant page displayed again. Will need to grant consent again
+            grantPage.assertCurrent();
+            grantPage.accept();
+
+            appPage.assertCurrent();
+            Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
+
+            events.expectLogin()
+                    .detail(Details.USERNAME, "test-user@localhost")
+                    .detail(Details.CONSENT, Details.CONSENT_VALUE_CONSENT_GRANTED)
+                    .assertEvent();
+
+        } finally {
+            // Revert consent
+            UserResource user = ApiUtil.findUserByUsernameId(adminClient.realm("test"), "test-user@localhost");
+            user.revokeConsent("test-app");
+
+            //  revert require consent
+            ClientManager.realm(adminClient.realm("test")).clientId("test-app").consentRequired(false);
+        }
+    }
+
+
     // DISPLAY & OTHERS
 
     @Test