diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdatePassword.java b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdatePassword.java
index f88cdc8cbf..1521efa328 100755
--- a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdatePassword.java
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdatePassword.java
@@ -5,14 +5,23 @@ import org.keycloak.Config;
import org.keycloak.authentication.RequiredActionContext;
import org.keycloak.authentication.RequiredActionFactory;
import org.keycloak.authentication.RequiredActionProvider;
+import org.keycloak.events.EventBuilder;
+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.ModelException;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.services.managers.ClientSessionCode;
+import org.keycloak.services.messages.Messages;
+import org.keycloak.services.validation.Validation;
import org.keycloak.util.Time;
+import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import java.util.concurrent.TimeUnit;
@@ -49,17 +58,48 @@ public class UpdatePassword implements RequiredActionProvider, RequiredActionFac
@Override
public void requiredActionChallenge(RequiredActionContext context) {
- LoginFormsProvider loginFormsProvider = context.getSession()
- .getProvider(LoginFormsProvider.class)
- .setClientSessionCode(context.generateAccessCode(UserModel.RequiredAction.UPDATE_PASSWORD.name()))
- .setUser(context.getUser());
- Response challenge = loginFormsProvider.createResponse(UserModel.RequiredAction.UPDATE_PASSWORD);
+ Response challenge = context.form().createForm("login-update-password.ftl");
context.challenge(challenge);
}
@Override
public void processAction(RequiredActionContext context) {
- context.failure();
+ EventBuilder event = context.getEvent();
+ MultivaluedMap formData = context.getHttpRequest().getDecodedFormParameters();
+ event.event(EventType.UPDATE_PASSWORD);
+ String passwordNew = formData.getFirst("password-new");
+ String passwordConfirm = formData.getFirst("password-confirm");
+
+ if (Validation.isBlank(passwordNew)) {
+ Response challenge = context.form()
+ .setError(Messages.MISSING_PASSWORD)
+ .createForm("login-update-password.ftl");
+ context.challenge(challenge);
+ return;
+ } else if (!passwordNew.equals(passwordConfirm)) {
+ Response challenge = context.form()
+ .setError(Messages.NOTMATCH_PASSWORD)
+ .createForm("login-update-password.ftl");
+ context.challenge(challenge);
+ return;
+ }
+
+ try {
+ context.getSession().users().updateCredential(context.getRealm(), context.getUser(), UserCredentialModel.password(passwordNew));
+ context.success();
+ } catch (ModelException me) {
+ Response challenge = context.form()
+ .setError(me.getMessage(), me.getParameters())
+ .createForm("login-update-password.ftl");
+ context.challenge(challenge);
+ return;
+ } catch (Exception ape) {
+ Response challenge = context.form()
+ .setError(ape.getMessage())
+ .createForm("login-update-password.ftl");
+ context.challenge(challenge);
+ return;
+ }
}
@Override
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 52cca074fb..70f10e74b1 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -32,6 +32,7 @@ import org.keycloak.protocol.LoginProtocol;
import org.keycloak.protocol.RestartLoginCookie;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.representations.AccessToken;
+import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.IdentityBrokerService;
import org.keycloak.services.resources.RealmsResource;
import org.keycloak.services.Urls;
@@ -57,6 +58,7 @@ import java.util.Set;
* @version $Revision: 1 $
*/
public class AuthenticationManager {
+ public static final String END_AFTER_REQUIRED_ACTIONS = "END_AFTER_REQUIRED_ACTIONS";
protected static Logger logger = Logger.getLogger(AuthenticationManager.class);
public static final String FORM_USERNAME = "username";
// used for auth login
@@ -409,6 +411,15 @@ public class AuthenticationManager {
HttpRequest request, UriInfo uriInfo, EventBuilder event) {
Response requiredAction = actionRequired(session, userSession, clientSession, clientConnection, request, uriInfo, event);
if (requiredAction != null) return requiredAction;
+ if (clientSession.getNote(END_AFTER_REQUIRED_ACTIONS) != null) {
+ Response response = session.getProvider(LoginFormsProvider.class)
+ .setAttribute("skipLink", true)
+ .setSuccess(Messages.ACCOUNT_UPDATED)
+ .createInfoPage();
+ session.sessions().removeUserSession(session.getContext().getRealm(), userSession);
+ return response;
+
+ }
event.success();
RealmModel realm = clientSession.getRealm();
return redirectAfterSuccessfulFlow(session, realm , userSession, clientSession, request, uriInfo, clientConnection);
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 dd2c90daf7..eea24990ea 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -628,67 +628,6 @@ public class LoginActionsService {
return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
}
- @Path("password")
- @POST
- @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
- public Response updatePassword(@QueryParam("code") String code,
- final MultivaluedMap formData) {
- event.event(EventType.UPDATE_PASSWORD);
- Checks checks = new Checks();
- if (!checks.verifyCode(code, ClientSessionModel.Action.UPDATE_PASSWORD.name(), ClientSessionModel.Action.RECOVER_PASSWORD.name())) {
- return checks.response;
- }
- ClientSessionCode accessCode = checks.clientCode;
- ClientSessionModel clientSession = accessCode.getClientSession();
- UserSessionModel userSession = clientSession.getUserSession();
- UserModel user = userSession.getUser();
-
- initEvent(clientSession);
-
- String passwordNew = formData.getFirst("password-new");
- String passwordConfirm = formData.getFirst("password-confirm");
-
- LoginFormsProvider loginForms = session.getProvider(LoginFormsProvider.class)
- .setUser(user);
- if (Validation.isBlank(passwordNew)) {
- return loginForms.setError(Messages.MISSING_PASSWORD)
- .setClientSessionCode(accessCode.getCode())
- .createResponse(RequiredAction.UPDATE_PASSWORD);
- } else if (!passwordNew.equals(passwordConfirm)) {
- return loginForms.setError(Messages.NOTMATCH_PASSWORD)
- .setClientSessionCode(accessCode.getCode())
- .createResponse(RequiredAction.UPDATE_PASSWORD);
- }
-
- try {
- session.users().updateCredential(realm, user, UserCredentialModel.password(passwordNew));
- } catch (ModelException me) {
- return loginForms.setError(me.getMessage(), me.getParameters())
- .setClientSessionCode(accessCode.getCode())
- .createResponse(RequiredAction.UPDATE_PASSWORD);
- } catch (Exception ape) {
- return loginForms.setError(ape.getMessage())
- .setClientSessionCode(accessCode.getCode())
- .createResponse(RequiredAction.UPDATE_PASSWORD);
- }
-
- user.removeRequiredAction(RequiredAction.UPDATE_PASSWORD);
-
- event.event(EventType.UPDATE_PASSWORD).success();
-
- if (clientSession.getAction().equals(ClientSessionModel.Action.RECOVER_PASSWORD.name())) {
- session.sessions().removeClientSession(realm, clientSession);
- return session.getProvider(LoginFormsProvider.class)
- .setSuccess(Messages.ACCOUNT_PASSWORD_UPDATED)
- .createInfoPage();
- }
-
- event = event.clone().event(EventType.LOGIN);
-
- return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
- }
-
-
@Path("email-verification")
@GET
public Response emailVerification(@QueryParam("code") String code, @QueryParam("key") String key) {
@@ -752,13 +691,11 @@ public class LoginActionsService {
if (key != null) {
Checks checks = new Checks();
if (!checks.verifyCode(key, ClientSessionModel.Action.RECOVER_PASSWORD.name())) {
- event.error(Errors.RESET_CREDENTIAL_DISABLED);
- return ErrorPage.error(session, Messages.INVALID_CODE);
+ return checks.response;
}
- ClientSessionCode accessCode = checks.clientCode;
- return session.getProvider(LoginFormsProvider.class)
- .setClientSessionCode(accessCode.getCode())
- .createResponse(RequiredAction.UPDATE_PASSWORD);
+ ClientSessionModel clientSession = checks.clientCode.getClientSession();
+ clientSession.setNote("END_AFTER_REQUIRED_ACTIONS", "true");
+ return AuthenticationManager.nextActionAfterAuthentication(session, clientSession.getUserSession(), clientSession, clientConnection, request, uriInfo, event);
} else {
event.error(Errors.RESET_CREDENTIAL_DISABLED);
return ErrorPage.error(session, Messages.INVALID_CODE);
@@ -843,6 +780,7 @@ public class LoginActionsService {
}
initEvent(clientSession);
+ event.event(EventType.CUSTOM_REQUIRED_ACTION);
RequiredActionContextResult context = new RequiredActionContextResult(clientSession.getUserSession(), clientSession, realm, event, session, request, clientSession.getUserSession().getUser(), factory) {
@@ -865,9 +803,9 @@ public class LoginActionsService {
};
provider.processAction(context);
if (context.getStatus() == RequiredActionContext.Status.SUCCESS) {
- event.clone().event(EventType.CUSTOM_REQUIRED_ACTION)
- .detail(Details.CUSTOM_REQUIRED_ACTION, action).success();
+ event.clone().success();
clientSession.getUserSession().getUser().removeRequiredAction(factory.getId());
+ event.event(EventType.LOGIN);
return AuthenticationManager.nextActionAfterAuthentication(session, clientSession.getUserSession(), clientSession, clientConnection, request, uriInfo, event);
}
if (context.getStatus() == RequiredActionContext.Status.CHALLENGE) {
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
index 066ce30817..6d55bc2405 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
@@ -854,6 +854,7 @@ public class UsersResource {
accessCode.setAction(ClientSessionModel.Action.RECOVER_PASSWORD.name());
try {
+ user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
UriBuilder builder = Urls.recoverPasswordBuilder(uriInfo.getBaseUri());
builder.queryParam("key", accessCode.getCode());
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/TermsAndConditionsTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/TermsAndConditionsTest.java
index a1636c8686..f758900d3b 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/TermsAndConditionsTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/TermsAndConditionsTest.java
@@ -108,7 +108,7 @@ public class TermsAndConditionsTest {
termsPage.declineTerms();
- events.expectLogin().detail(Details.CUSTOM_REQUIRED_ACTION, TermsAndConditions.PROVIDER_ID)
+ events.expectLogin().event(EventType.CUSTOM_REQUIRED_ACTION_ERROR).detail(Details.CUSTOM_REQUIRED_ACTION, TermsAndConditions.PROVIDER_ID)
.error(Errors.REJECTED_BY_USER)
.removeDetail(Details.CONSENT)
.assertEvent();