diff --git a/services/src/main/java/org/keycloak/authentication/RequiredActionFactory.java b/services/src/main/java/org/keycloak/authentication/RequiredActionFactory.java
new file mode 100755
index 0000000000..9acfcd12b4
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/RequiredActionFactory.java
@@ -0,0 +1,10 @@
+package org.keycloak.authentication;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public interface RequiredActionFactory extends ProviderFactory {
+}
diff --git a/services/src/main/java/org/keycloak/authentication/RequiredActionProvider.java b/services/src/main/java/org/keycloak/authentication/RequiredActionProvider.java
index b5cc21aedc..e6ff4882b3 100755
--- a/services/src/main/java/org/keycloak/authentication/RequiredActionProvider.java
+++ b/services/src/main/java/org/keycloak/authentication/RequiredActionProvider.java
@@ -9,6 +9,7 @@ import javax.ws.rs.core.Response;
* @version $Revision: 1 $
*/
public interface RequiredActionProvider extends Provider {
+ void evaluateTriggers(RequiredActionContext context);
Response invokeRequiredAction(RequiredActionContext context);
- Object jaxrsService();
+ Object jaxrsService(RequiredActionContext context);
}
diff --git a/services/src/main/java/org/keycloak/authentication/RequiredActionSpi.java b/services/src/main/java/org/keycloak/authentication/RequiredActionSpi.java
new file mode 100755
index 0000000000..65370e5bca
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/RequiredActionSpi.java
@@ -0,0 +1,32 @@
+package org.keycloak.authentication;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author Stian Thorgersen
+ */
+public class RequiredActionSpi implements Spi {
+
+ @Override
+ public boolean isInternal() {
+ return false;
+ }
+
+ @Override
+ public String getName() {
+ return "required-action";
+ }
+
+ @Override
+ public Class extends Provider> getProviderClass() {
+ return RequiredActionProvider.class;
+ }
+
+ @Override
+ public Class extends ProviderFactory> getProviderFactoryClass() {
+ return RequiredActionFactory.class;
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/authentication/actions/UpdatePassword.java b/services/src/main/java/org/keycloak/authentication/actions/UpdatePassword.java
new file mode 100755
index 0000000000..23d5a93b1f
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/actions/UpdatePassword.java
@@ -0,0 +1,93 @@
+package org.keycloak.authentication.actions;
+
+import org.jboss.logging.Logger;
+import org.keycloak.Config;
+import org.keycloak.authentication.RequiredActionContext;
+import org.keycloak.authentication.RequiredActionFactory;
+import org.keycloak.authentication.RequiredActionProvider;
+import org.keycloak.login.LoginFormsProvider;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserCredentialValueModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.services.managers.ClientSessionCode;
+import org.keycloak.util.Time;
+
+import javax.ws.rs.core.Response;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class UpdatePassword implements RequiredActionProvider, RequiredActionFactory {
+ protected static Logger logger = Logger.getLogger(UpdatePassword.class);
+ @Override
+ public void evaluateTriggers(RequiredActionContext context) {
+ int daysToExpirePassword = context.getRealm().getPasswordPolicy().getDaysToExpirePassword();
+ if(daysToExpirePassword != -1) {
+ for (UserCredentialValueModel entity : context.getUser().getCredentialsDirectly()) {
+ if (entity.getType().equals(UserCredentialModel.PASSWORD)) {
+
+ if(entity.getCreatedDate() == null) {
+ context.getUser().addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
+ logger.debug("User is required to update password");
+ } else {
+ long timeElapsed = Time.toMillis(Time.currentTime()) - entity.getCreatedDate();
+ long timeToExpire = TimeUnit.DAYS.toMillis(daysToExpirePassword);
+
+ if(timeElapsed > timeToExpire) {
+ context.getUser().addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
+ logger.debug("User is required to update password");
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public Response invokeRequiredAction(RequiredActionContext context) {
+ ClientSessionCode accessCode = new ClientSessionCode(context.getRealm(), context.getClientSession());
+ accessCode.setAction(ClientSessionModel.Action.UPDATE_PASSWORD.name());
+
+ LoginFormsProvider loginFormsProvider = context.getSession().getProvider(LoginFormsProvider.class).setClientSessionCode(accessCode.getCode())
+ .setUser(context.getUser());
+ return loginFormsProvider.createResponse(UserModel.RequiredAction.UPDATE_PASSWORD);
+ }
+
+ @Override
+ public Object jaxrsService(RequiredActionContext context) {
+ // this is handled by LoginActionsService at the moment
+ return null;
+ }
+
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public RequiredActionProvider create(KeycloakSession session) {
+ return this;
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+
+ }
+
+ @Override
+ public String getId() {
+ return UserModel.RequiredAction.UPDATE_PASSWORD.name();
+ }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/actions/UpdateProfile.java b/services/src/main/java/org/keycloak/authentication/actions/UpdateProfile.java
new file mode 100755
index 0000000000..8ee36e94fb
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/actions/UpdateProfile.java
@@ -0,0 +1,75 @@
+package org.keycloak.authentication.actions;
+
+import org.jboss.logging.Logger;
+import org.keycloak.Config;
+import org.keycloak.authentication.RequiredActionContext;
+import org.keycloak.authentication.RequiredActionFactory;
+import org.keycloak.authentication.RequiredActionProvider;
+import org.keycloak.login.LoginFormsProvider;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RequiredCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.services.managers.ClientSessionCode;
+
+import javax.ws.rs.core.Response;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class UpdateProfile implements RequiredActionProvider, RequiredActionFactory {
+ protected static Logger logger = Logger.getLogger(UpdateProfile.class);
+ @Override
+ public void evaluateTriggers(RequiredActionContext context) {
+ if (context.getRealm().isVerifyEmail() && !context.getUser().isEmailVerified()) {
+ context.getUser().addRequiredAction(UserModel.RequiredAction.VERIFY_EMAIL);
+ logger.debug("User is required to verify email");
+ }
+ }
+
+ @Override
+ public Response invokeRequiredAction(RequiredActionContext context) {
+ ClientSessionCode accessCode = new ClientSessionCode(context.getRealm(), context.getClientSession());
+ accessCode.setAction(ClientSessionModel.Action.UPDATE_PROFILE.name());
+
+ LoginFormsProvider loginFormsProvider = context.getSession().getProvider(LoginFormsProvider.class).setClientSessionCode(accessCode.getCode())
+ .setUser(context.getUser());
+ return loginFormsProvider.createResponse(UserModel.RequiredAction.UPDATE_PROFILE);
+ }
+
+ @Override
+ public Object jaxrsService(RequiredActionContext context) {
+ // this is handled by LoginActionsService at the moment
+ // todo should be refactored to contain it here
+ return null;
+ }
+
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public RequiredActionProvider create(KeycloakSession session) {
+ return this;
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+
+ }
+
+ @Override
+ public String getId() {
+ return UserModel.RequiredAction.UPDATE_PROFILE.name();
+ }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/actions/UpdateTotp.java b/services/src/main/java/org/keycloak/authentication/actions/UpdateTotp.java
new file mode 100755
index 0000000000..7f2228abc0
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/actions/UpdateTotp.java
@@ -0,0 +1,84 @@
+package org.keycloak.authentication.actions;
+
+import org.jboss.logging.Logger;
+import org.keycloak.Config;
+import org.keycloak.authentication.RequiredActionContext;
+import org.keycloak.authentication.RequiredActionFactory;
+import org.keycloak.authentication.RequiredActionProvider;
+import org.keycloak.login.LoginFormsProvider;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RequiredCredentialModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserCredentialValueModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.services.managers.ClientSessionCode;
+import org.keycloak.util.Time;
+
+import javax.ws.rs.core.Response;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class UpdateTotp implements RequiredActionProvider, RequiredActionFactory {
+ protected static Logger logger = Logger.getLogger(UpdateTotp.class);
+ @Override
+ public void evaluateTriggers(RequiredActionContext context) {
+ // I don't think we need this check here. AuthenticationProcessor should be setting the required action
+ // if OTP changes from required from optional or disabled
+ for (RequiredCredentialModel c : context.getRealm().getRequiredCredentials()) {
+ if (c.getType().equals(CredentialRepresentation.TOTP) && !context.getUser().isTotp()) {
+ context.getUser().addRequiredAction(UserModel.RequiredAction.CONFIGURE_TOTP);
+ logger.debug("User is required to configure totp");
+ }
+ }
+ }
+
+ @Override
+ public Response invokeRequiredAction(RequiredActionContext context) {
+ ClientSessionCode accessCode = new ClientSessionCode(context.getRealm(), context.getClientSession());
+ accessCode.setAction(ClientSessionModel.Action.CONFIGURE_TOTP.name());
+
+ LoginFormsProvider loginFormsProvider = context.getSession().getProvider(LoginFormsProvider.class).setClientSessionCode(accessCode.getCode())
+ .setUser(context.getUser());
+ return loginFormsProvider.createResponse(UserModel.RequiredAction.CONFIGURE_TOTP);
+ }
+
+ @Override
+ public Object jaxrsService(RequiredActionContext context) {
+ // this is handled by LoginActionsService at the moment
+ // todo should be refactored to contain it here
+ return null;
+ }
+
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public RequiredActionProvider create(KeycloakSession session) {
+ return this;
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+
+ }
+
+ @Override
+ public String getId() {
+ return UserModel.RequiredAction.CONFIGURE_TOTP.name();
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/authentication/actions/VerifyEmail.java b/services/src/main/java/org/keycloak/authentication/actions/VerifyEmail.java
new file mode 100755
index 0000000000..1be48a02c6
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/actions/VerifyEmail.java
@@ -0,0 +1,104 @@
+package org.keycloak.authentication.actions;
+
+import org.jboss.logging.Logger;
+import org.keycloak.Config;
+import org.keycloak.authentication.RequiredActionContext;
+import org.keycloak.authentication.RequiredActionFactory;
+import org.keycloak.authentication.RequiredActionProvider;
+import org.keycloak.events.Details;
+import org.keycloak.events.EventType;
+import org.keycloak.login.LoginFormsProvider;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserCredentialValueModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.services.managers.ClientSessionCode;
+import org.keycloak.services.resources.LoginActionsService;
+import org.keycloak.services.validation.Validation;
+import org.keycloak.util.Time;
+
+import javax.ws.rs.core.Response;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class VerifyEmail implements RequiredActionProvider, RequiredActionFactory {
+ protected static Logger logger = Logger.getLogger(VerifyEmail.class);
+ @Override
+ public void evaluateTriggers(RequiredActionContext context) {
+ int daysToExpirePassword = context.getRealm().getPasswordPolicy().getDaysToExpirePassword();
+ if(daysToExpirePassword != -1) {
+ for (UserCredentialValueModel entity : context.getUser().getCredentialsDirectly()) {
+ if (entity.getType().equals(UserCredentialModel.PASSWORD)) {
+
+ if(entity.getCreatedDate() == null) {
+ context.getUser().addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
+ logger.debug("User is required to update password");
+ } else {
+ long timeElapsed = Time.toMillis(Time.currentTime()) - entity.getCreatedDate();
+ long timeToExpire = TimeUnit.DAYS.toMillis(daysToExpirePassword);
+
+ if(timeElapsed > timeToExpire) {
+ context.getUser().addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
+ logger.debug("User is required to update password");
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public Response invokeRequiredAction(RequiredActionContext context) {
+ if (Validation.isBlank(context.getUser().getEmail())) {
+ return null;
+ }
+
+ ClientSessionCode accessCode = new ClientSessionCode(context.getRealm(), context.getClientSession());
+ accessCode.setAction(ClientSessionModel.Action.VERIFY_EMAIL.name());
+ context.getEvent().clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, context.getUser().getEmail()).success();
+ LoginActionsService.createActionCookie(context.getRealm(), context.getUriInfo(), context.getConnection(), context.getUserSession().getId());
+
+ LoginFormsProvider loginFormsProvider = context.getSession().getProvider(LoginFormsProvider.class).setClientSessionCode(accessCode.getCode())
+ .setUser(context.getUser());
+ return loginFormsProvider.createResponse(UserModel.RequiredAction.VERIFY_EMAIL);
+ }
+
+ @Override
+ public Object jaxrsService(RequiredActionContext context) {
+ // this is handled by LoginActionsService at the moment
+ return null;
+ }
+
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public RequiredActionProvider create(KeycloakSession session) {
+ return this;
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+
+ }
+
+ @Override
+ public String getId() {
+ return UserModel.RequiredAction.VERIFY_EMAIL.name();
+ }
+
+}
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 f445bfe7b3..8d1995054c 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -6,6 +6,9 @@ import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.ClientConnection;
import org.keycloak.RSATokenVerifier;
import org.keycloak.VerificationException;
+import org.keycloak.authentication.RequiredActionContext;
+import org.keycloak.authentication.RequiredActionFactory;
+import org.keycloak.authentication.RequiredActionProvider;
import org.keycloak.broker.provider.IdentityProvider;
import org.keycloak.events.Details;
import org.keycloak.events.EventBuilder;
@@ -28,6 +31,7 @@ import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.LoginProtocol;
import org.keycloak.protocol.oidc.TokenManager;
+import org.keycloak.provider.ProviderFactory;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.resources.IdentityBrokerService;
@@ -433,15 +437,70 @@ public class AuthenticationManager {
}
- public static Response actionRequired(KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession,
- ClientConnection clientConnection,
- HttpRequest request, UriInfo uriInfo, EventBuilder event) {
- RealmModel realm = clientSession.getRealm();
- UserModel user = userSession.getUser();
+ 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) {
+ final RealmModel realm = clientSession.getRealm();
+ final UserModel user = userSession.getUser();
+ /*
isForcePasswordUpdateRequired(realm, user);
isTotpConfigurationRequired(realm, user);
isEmailVerificationRequired(realm, user);
- ClientModel client = clientSession.getClient();
+ */
+ final ClientModel client = clientSession.getClient();
+
+ RequiredActionContext context = new RequiredActionContext() {
+ @Override
+ public EventBuilder getEvent() {
+ return event;
+ }
+
+ @Override
+ public UserModel getUser() {
+ return user;
+ }
+
+ @Override
+ public RealmModel getRealm() {
+ return realm;
+ }
+
+ @Override
+ public ClientSessionModel getClientSession() {
+ return clientSession;
+ }
+
+ @Override
+ public UserSessionModel getUserSession() {
+ return userSession;
+ }
+
+ @Override
+ public ClientConnection getConnection() {
+ return clientConnection;
+ }
+
+ @Override
+ public UriInfo getUriInfo() {
+ return uriInfo;
+ }
+
+ @Override
+ public KeycloakSession getSession() {
+ return session;
+ }
+
+ @Override
+ public HttpRequest getHttpRequest() {
+ return request;
+ }
+ };
+
+ // see if any required actions need triggering, i.e. an expired password
+ for (ProviderFactory factory : session.getKeycloakSessionFactory().getProviderFactories(RequiredActionProvider.class)) {
+ RequiredActionProvider provider = ((RequiredActionFactory)factory).create(session);
+ provider.evaluateTriggers(context);
+ }
ClientSessionCode accessCode = new ClientSessionCode(realm, clientSession);
@@ -450,38 +509,19 @@ public class AuthenticationManager {
event.detail(Details.CODE_ID, clientSession.getId());
Set requiredActions = user.getRequiredActions();
- if (!requiredActions.isEmpty()) {
- Iterator i = user.getRequiredActions().iterator();
- String action = i.next();
-
- if (action.equals(UserModel.RequiredAction.VERIFY_EMAIL.name()) && Validation.isBlank(user.getEmail())) {
- if (i.hasNext())
- action = i.next();
- else
- action = null;
- }
+ for (String action : requiredActions) {
+ RequiredActionProvider actionProvider = session.getProvider(RequiredActionProvider.class, action);
+ Response challenge = actionProvider.invokeRequiredAction(context);
+ if (challenge != null) return challenge;
- if (action != null) {
- accessCode.setRequiredAction(RequiredAction.valueOf(action));
-
- LoginFormsProvider loginFormsProvider = session.getProvider(LoginFormsProvider.class).setClientSessionCode(accessCode.getCode())
- .setUser(user);
- if (action.equals(UserModel.RequiredAction.VERIFY_EMAIL.name())) {
- event.clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, user.getEmail()).success();
- LoginActionsService.createActionCookie(realm, uriInfo, clientConnection, userSession.getId());
- }
-
- return loginFormsProvider.createResponse(RequiredAction.valueOf(action));
- }
}
-
if (client.isConsentRequired()) {
accessCode.setAction(ClientSessionModel.Action.OAUTH_GRANT.name());
UserConsentModel grantedConsent = user.getConsentByClient(client.getId());
- List realmRoles = new LinkedList();
- MultivaluedMap resourceRoles = new MultivaluedMapImpl();
+ List realmRoles = new LinkedList<>();
+ MultivaluedMap resourceRoles = new MultivaluedMapImpl<>();
for (RoleModel r : accessCode.getRequestedRoles()) {
// Consent already granted by user
@@ -496,7 +536,7 @@ public class AuthenticationManager {
}
}
- List protocolMappers = new LinkedList();
+ List protocolMappers = new LinkedList<>();
for (ProtocolMapperModel protocolMapper : accessCode.getRequestedProtocolMappers()) {
if (protocolMapper.isConsentRequired() && protocolMapper.getConsentText() != null) {
if (grantedConsent == null || !grantedConsent.isProtocolMapperGranted(protocolMapper)) {
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 a81327ee19..08327b96a6 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -773,7 +773,7 @@ public class LoginActionsService {
event.clone().event(EventType.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, oldEmail).detail(Details.UPDATED_EMAIL, email).success();
}
- return redirectOauth(user, accessCode, clientSession, userSession);
+ return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
}
@Path("totp")
@@ -818,7 +818,7 @@ public class LoginActionsService {
event.clone().event(EventType.UPDATE_TOTP).success();
- return redirectOauth(user, accessCode, clientSession, userSession);
+ return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
}
@Path("password")
@@ -880,7 +880,7 @@ public class LoginActionsService {
event = event.clone().event(EventType.LOGIN);
- return redirectOauth(user, accessCode, clientSession, userSession);
+ return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
}
@@ -913,7 +913,7 @@ public class LoginActionsService {
event = event.clone().removeDetail(Details.EMAIL).event(EventType.LOGIN);
- return redirectOauth(user, accessCode, clientSession, userSession);
+ return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
} else {
Checks checks = new Checks();
if (!checks.check(code, ClientSessionModel.Action.VERIFY_EMAIL.name())) {
@@ -1057,10 +1057,6 @@ public class LoginActionsService {
CookieHelper.addCookie(ACTION_COOKIE, sessionId, AuthenticationManager.getRealmCookiePath(realm, uriInfo), null, null, -1, realm.getSslRequired().isRequired(clientConnection), true);
}
- private Response redirectOauth(UserModel user, ClientSessionCode accessCode, ClientSessionModel clientSession, UserSessionModel userSession) {
- return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
- }
-
private void initEvent(ClientSessionModel clientSession) {
event.event(EventType.LOGIN).client(clientSession.getClient())
.user(clientSession.getUserSession().getUser())
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.authentication.RequiredActionFactory b/services/src/main/resources/META-INF/services/org.keycloak.authentication.RequiredActionFactory
new file mode 100755
index 0000000000..fc74174d2c
--- /dev/null
+++ b/services/src/main/resources/META-INF/services/org.keycloak.authentication.RequiredActionFactory
@@ -0,0 +1,4 @@
+org.keycloak.authentication.actions.UpdatePassword
+org.keycloak.authentication.actions.UpdateProfile
+org.keycloak.authentication.actions.UpdateTotp
+org.keycloak.authentication.actions.VerifyEmail
\ No newline at end of file
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index d2b5ca78ff..050fef24d6 100755
--- a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -3,4 +3,5 @@ org.keycloak.protocol.ProtocolMapperSpi
org.keycloak.exportimport.ClientImportSpi
org.keycloak.wellknown.WellKnownSpi
org.keycloak.messages.MessagesSpi
-org.keycloak.authentication.AuthenticatorSpi
\ No newline at end of file
+org.keycloak.authentication.AuthenticatorSpi
+org.keycloak.authentication.RequiredActionSpi
\ No newline at end of file