keycloak-aplcache

Details

diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
index 17c02af..b3123cb 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
@@ -637,23 +637,9 @@ public class AuthenticationProcessor {
     }
 
     public Response authenticate() throws AuthenticationFlowException {
-        checkClientSession();
         logger.debug("AUTHENTICATE");
-        event.client(clientSession.getClient().getClientId())
-                .detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
-                .detail(Details.AUTH_METHOD, clientSession.getAuthMethod());
-        String authType = clientSession.getNote(Details.AUTH_TYPE);
-        if (authType != null) {
-            event.detail(Details.AUTH_TYPE, authType);
-        }
-        UserModel authUser = clientSession.getAuthenticatedUser();
-        validateUser(authUser);
-        AuthenticationFlow authenticationFlow = createFlowExecution(this.flowId, null);
-        Response challenge = authenticationFlow.processFlow();
+        Response challenge = authenticateOnly();
         if (challenge != null) return challenge;
-        if (clientSession.getAuthenticatedUser() == null) {
-            throw new AuthenticationFlowException(AuthenticationFlowError.UNKNOWN_USER);
-        }
         return authenticationComplete();
     }
 
@@ -668,18 +654,7 @@ public class AuthenticationProcessor {
         }
     }
 
-    public Response createSuccessRedirect() {
-        // redirect to non-action url so browser refresh button works without reposting past data
-        String code = generateCode();
-
-        URI redirect = LoginActionsService.loginActionsBaseUrl(getUriInfo())
-                .path(flowPath)
-                .queryParam(OAuth2Constants.CODE, code).build(getRealm().getName());
-        return Response.status(302).location(redirect).build();
-
-    }
-
-    public static Response createRequiredActionRedirect(RealmModel realm, ClientSessionModel clientSession, UriInfo uriInfo) {
+    public static Response redirectToRequiredActions(RealmModel realm, ClientSessionModel clientSession, UriInfo uriInfo) {
 
         // redirect to non-action url so browser refresh button works without reposting past data
         ClientSessionCode accessCode = new ClientSessionCode(realm, clientSession);
@@ -722,8 +697,8 @@ public class AuthenticationProcessor {
         String current = clientSession.getNote(CURRENT_AUTHENTICATION_EXECUTION);
         if (!execution.equals(current)) {
             logger.debug("Current execution does not equal executed execution.  Might be a page refresh");
-            logFailure();
-            resetFlow(clientSession);
+            //logFailure();
+            //resetFlow(clientSession);
             return authenticate();
         }
         UserModel authUser = clientSession.getAuthenticatedUser();
@@ -766,7 +741,7 @@ public class AuthenticationProcessor {
 
     public Response authenticateOnly() throws AuthenticationFlowException {
         logger.debug("AUTHENTICATE ONLY");
-       checkClientSession();
+        checkClientSession();
         event.client(clientSession.getClient().getClientId())
                 .detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
                 .detail(Details.AUTH_METHOD, clientSession.getAuthMethod());
@@ -778,14 +753,13 @@ public class AuthenticationProcessor {
         validateUser(authUser);
         AuthenticationFlow authenticationFlow = createFlowExecution(this.flowId, null);
         Response challenge = authenticationFlow.processFlow();
+        if (challenge != null) return challenge;
+        if (clientSession.getAuthenticatedUser() == null) {
+            throw new AuthenticationFlowException(AuthenticationFlowError.UNKNOWN_USER);
+        }
         return challenge;
     }
 
-    public Response attachSessionExecutionRequiredActions() {
-        attachSession();
-        return AuthenticationManager.actionRequired(session, userSession, clientSession, connection, request, uriInfo, event);
-    }
-
     public void attachSession() {
         String username = clientSession.getAuthenticatedUser().getUsername();
         String attemptedUsername = clientSession.getNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME);
@@ -829,9 +803,16 @@ public class AuthenticationProcessor {
 
     protected Response authenticationComplete() {
         attachSession();
-        return createRequiredActionRedirect(realm, clientSession, uriInfo);
-        //return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, connection, request, uriInfo, event);
+        if (isActionRequired()) {
+            return redirectToRequiredActions(realm, clientSession, uriInfo);
+        } else {
+            event.detail(Details.CODE_ID, clientSession.getId());  // todo This should be set elsewhere.  find out why tests fail.  Don't know where this is supposed to be set
+            return AuthenticationManager.finishedRequiredActions(session,  userSession, clientSession, connection, request, uriInfo, event);
+        }
+    }
 
+    public boolean isActionRequired() {
+        return AuthenticationManager.isActionRequired(session, userSession, clientSession, connection, request, uriInfo, event);
     }
 
     public AuthenticationProcessor.Result createAuthenticatorContext(AuthenticationExecutionModel model, Authenticator authenticator, List<AuthenticationExecutionModel> executions) {
diff --git a/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java b/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
index 9a3a8c2..6cc22f2 100755
--- a/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
+++ b/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
@@ -6,17 +6,16 @@ import org.keycloak.models.AuthenticationFlowModel;
 import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.UserModel;
 
-import static org.keycloak.authentication.FlowStatus.*;
-
 import javax.ws.rs.core.Response;
-
 import java.util.Iterator;
 import java.util.List;
 
+import static org.keycloak.authentication.FlowStatus.SUCCESS;
+
 /**
-* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
-* @version $Revision: 1 $
-*/
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
 public class DefaultAuthenticationFlow implements AuthenticationFlow {
     protected static Logger logger = Logger.getLogger(DefaultAuthenticationFlow.class);
     Response alternativeChallenge = null;
@@ -70,14 +69,9 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
                 authenticator.action(result);
                 Response response = processResult(result);
                 if (response == null) {
-                    if (result.status == SUCCESS && processor.isBrowserFlow()) {
-                         // redirect to a non-action URL so browser refresh works without reposting.
-                         return processor.createSuccessRedirect();
-                    } else {
-                        return processFlow();
-                    }
-                }
-                else return response;
+                    processor.getClientSession().removeNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION);
+                    return processFlow();
+                } else return response;
             }
         }
         throw new AuthenticationFlowException("action is not in current execution", AuthenticationFlowError.INTERNAL_ERROR);
diff --git a/services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java b/services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java
index 5ee8bbe..2913dcf 100755
--- a/services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java
+++ b/services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java
@@ -220,9 +220,8 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
 
         }
         processor.getClientSession().setExecutionStatus(actionExecution, ClientSessionModel.ExecutionStatus.SUCCESS);
-
-        // redirect to no execution so browser refresh button works without reposting past data
-        return processor.createSuccessRedirect();
+        processor.getClientSession().removeNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION);
+        return null;
     }
 
     public URI getActionUrl(String executionId, String code) {
diff --git a/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java b/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java
old mode 100644
new mode 100755
index befd364..9b11535
--- a/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java
+++ b/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java
@@ -11,6 +11,7 @@ import org.jboss.logging.Logger;
 import org.jboss.resteasy.spi.HttpRequest;
 import org.keycloak.authentication.AuthenticationProcessor;
 import org.keycloak.common.ClientConnection;
+import org.keycloak.events.Details;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.models.AuthenticationFlowModel;
 import org.keycloak.models.ClientSessionModel;
@@ -90,33 +91,29 @@ public abstract class AuthorizationEndpointBase {
         AuthenticationFlowModel flow = getAuthenticationFlow();
         String flowId = flow.getId();
         AuthenticationProcessor processor = createProcessor(clientSession, flowId, LoginActionsService.AUTHENTICATE_PATH);
-
+        event.detail(Details.CODE_ID, clientSession.getId());
         if (isPassive) {
             // OIDC prompt == NONE or SAML 2 IsPassive flag
             // This means that client is just checking if the user is already completely logged in.
             // We cancel login if any authentication action or required action is required
-            Response challenge = null;
-            Response challenge2 = null;
             try {
-                challenge = processor.authenticateOnly();
-                if (challenge == null) {
-                    challenge2 = processor.attachSessionExecutionRequiredActions();
+                if (processor.authenticateOnly() == null) {
+                    processor.attachSession();
+                } else {
+                    Response response = protocol.sendError(clientSession, Error.PASSIVE_LOGIN_REQUIRED);
+                    session.sessions().removeClientSession(realm, clientSession);
+                    return response;
                 }
-            } catch (Exception e) {
-                return processor.handleBrowserException(e);
-            }
+                if (processor.isActionRequired()) {
+                    Response response = protocol.sendError(clientSession, Error.PASSIVE_INTERACTION_REQUIRED);
+                    session.sessions().removeClientSession(realm, clientSession);
+                    return response;
 
-            if (challenge != null || challenge2 != null) {
-                if (processor.isUserSessionCreated()) {
-                    session.sessions().removeUserSession(realm, processor.getUserSession());
                 }
-                if (challenge != null)
-                    return protocol.sendError(clientSession, Error.PASSIVE_LOGIN_REQUIRED);
-                else
-                    return protocol.sendError(clientSession, Error.PASSIVE_INTERACTION_REQUIRED);
-            } else {
-                return processor.finishAuthentication(protocol);
+            } catch (Exception e) {
+                return processor.handleBrowserException(e);
             }
+            return processor.finishAuthentication(protocol);
         } else {
             try {
                 RestartLoginCookie.setRestartCookie(realm, clientConnection, uriInfo, clientSession);
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 d034d75..5d045e4 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -421,12 +421,16 @@ public class AuthenticationManager {
         return protocol.authenticated(userSession, new ClientSessionCode(realm, clientSession));
 
     }
-
     public static Response nextActionAfterAuthentication(KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession,
                                                   ClientConnection clientConnection,
                                                   HttpRequest request, UriInfo uriInfo, EventBuilder event) {
         Response requiredAction = actionRequired(session, userSession, clientSession, clientConnection, request, uriInfo, event);
         if (requiredAction != null) return requiredAction;
+        return finishedRequiredActions(session, userSession, clientSession, clientConnection, request, uriInfo, event);
+
+    }
+
+    public static Response finishedRequiredActions(KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession, ClientConnection clientConnection, HttpRequest request, UriInfo uriInfo, EventBuilder event) {
         if (clientSession.getNote(END_AFTER_REQUIRED_ACTIONS) != null) {
             Response response = session.getProvider(LoginFormsProvider.class)
                     .setAttribute("skipLink", true)
@@ -439,9 +443,50 @@ public class AuthenticationManager {
         event.success();
         RealmModel realm = clientSession.getRealm();
         return redirectAfterSuccessfulFlow(session, realm , userSession, clientSession, request, uriInfo, clientConnection, event);
+    }
+
+    public static boolean isActionRequired(final KeycloakSession session, final UserSessionModel userSession, final ClientSessionModel clientSession,
+                                           final ClientConnection clientConnection,
+                                           final HttpRequest request, final UriInfo uriInfo, final EventBuilder event) {
+        final RealmModel realm = clientSession.getRealm();
+        final UserModel user = userSession.getUser();
+        final ClientModel client = clientSession.getClient();
+
+        evaluateRequiredActionTriggers(session, userSession, clientSession, clientConnection, request, uriInfo, event, realm, user);
+
+        if (!user.getRequiredActions().isEmpty() || !clientSession.getRequiredActions().isEmpty()) return true;
+
+        if (client.isConsentRequired()) {
+
+            UserConsentModel grantedConsent = user.getConsentByClient(client.getId());
+
+            ClientSessionCode accessCode = new ClientSessionCode(realm, clientSession);
+            for (RoleModel r : accessCode.getRequestedRoles()) {
+
+                // Consent already granted by user
+                if (grantedConsent != null && grantedConsent.isRoleGranted(r)) {
+                    continue;
+                }
+                return true;
+             }
+
+            for (ProtocolMapperModel protocolMapper : accessCode.getRequestedProtocolMappers()) {
+                if (protocolMapper.isConsentRequired() && protocolMapper.getConsentText() != null) {
+                    if (grantedConsent == null || !grantedConsent.isProtocolMapperGranted(protocolMapper)) {
+                        return true;
+                    }
+                }
+            }
+            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);
+        }
+        return false;
 
     }
 
+
     public static Response actionRequired(final KeycloakSession session, final UserSessionModel userSession, final ClientSessionModel clientSession,
                                                          final ClientConnection clientConnection,
                                                          final HttpRequest request, final UriInfo uriInfo, final EventBuilder event) {
diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
index eb3f816..aa6d3fe 100755
--- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
+++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
@@ -523,7 +523,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
             LOGGER.debugf("Performing local authentication for user [%s].", federatedUser);
         }
 
-        return AuthenticationProcessor.createRequiredActionRedirect(realmModel, clientSession, uriInfo);
+        return AuthenticationProcessor.redirectToRequiredActions(realmModel, clientSession, uriInfo);
     }
 
 
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 75dd87e..d015224 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -697,7 +697,7 @@ public class LoginActionsService {
 
             event = event.clone().removeDetail(Details.EMAIL).event(EventType.LOGIN);
 
-            return AuthenticationProcessor.createRequiredActionRedirect(realm, clientSession, uriInfo);
+            return AuthenticationProcessor.redirectToRequiredActions(realm, clientSession, uriInfo);
         } else {
             Checks checks = new Checks();
             if (!checks.verifyCode(code, ClientSessionModel.Action.REQUIRED_ACTIONS.name(), ClientSessionCode.ActionType.USER)) {
@@ -740,7 +740,7 @@ public class LoginActionsService {
             clientSession.getUserSession().getUser().setEmailVerified(true);
             clientSession.setNote(AuthenticationManager.END_AFTER_REQUIRED_ACTIONS, "true");
             clientSession.setNote(ClientSessionModel.Action.EXECUTE_ACTIONS.name(), "true");
-            return AuthenticationProcessor.createRequiredActionRedirect(realm, clientSession, uriInfo);
+            return AuthenticationProcessor.redirectToRequiredActions(realm, clientSession, uriInfo);
         } else {
             event.error(Errors.INVALID_CODE);
             return ErrorPage.error(session, Messages.INVALID_CODE);
@@ -825,9 +825,9 @@ public class LoginActionsService {
         }
 
         if (!action.equals(clientSession.getNote(AuthenticationManager.CURRENT_REQUIRED_ACTION))) {
-            logger.error("required action doesn't match current required action");
-            event.error(Errors.INVALID_CODE);
-            throw new WebApplicationException(ErrorPage.error(session, Messages.INVALID_CODE));
+            logger.debug("required action doesn't match current required action");
+            clientSession.removeNote(AuthenticationManager.CURRENT_REQUIRED_ACTION);
+            redirectToRequiredActions(code);
         }
 
         RequiredActionFactory factory = (RequiredActionFactory)session.getKeycloakSessionFactory().getProviderFactory(RequiredActionProvider.class, action);
@@ -850,15 +850,17 @@ public class LoginActionsService {
         };
         provider.processAction(context);
         if (context.getStatus() == RequiredActionContext.Status.SUCCESS) {
-            event.success();
+            event.clone().success();
             // do both
             clientSession.removeRequiredAction(factory.getId());
             clientSession.getUserSession().getUser().removeRequiredAction(factory.getId());
+            clientSession.removeNote(AuthenticationManager.CURRENT_REQUIRED_ACTION);
             // redirect to a generic code URI so that browser refresh will work
-            URI redirect = LoginActionsService.loginActionsBaseUrl(uriInfo)
-                    .path(LoginActionsService.REQUIRED_ACTION)
-                    .queryParam(OAuth2Constants.CODE, code).build(realm.getName());
-            return Response.status(302).location(redirect).build();
+            //return redirectToRequiredActions(code);
+            event.removeDetail(Details.CUSTOM_REQUIRED_ACTION);
+            initEvent(clientSession);
+            event.event(EventType.LOGIN);
+            return AuthenticationManager.nextActionAfterAuthentication(session, clientSession.getUserSession(), clientSession, clientConnection, request, uriInfo, event);
        }
         if (context.getStatus() == RequiredActionContext.Status.CHALLENGE) {
             return context.getChallenge();
@@ -880,4 +882,11 @@ public class LoginActionsService {
         throw new RuntimeException("Unreachable");
     }
 
+    public Response redirectToRequiredActions(String code) {
+        URI redirect = LoginActionsService.loginActionsBaseUrl(uriInfo)
+                .path(LoginActionsService.REQUIRED_ACTION)
+                .queryParam(OAuth2Constants.CODE, code).build(realm.getName());
+        return Response.status(302).location(redirect).build();
+    }
+
 }