keycloak-aplcache

refactor recover password

8/31/2015 11:13:42 AM

Changes

forms/common-themes/src/main/resources/theme/keycloak/email/text/changePassword.ftl 1(+0 -1)

Details

diff --git a/events/api/src/main/java/org/keycloak/events/EventType.java b/events/api/src/main/java/org/keycloak/events/EventType.java
index 250ac9f..eacce62 100755
--- a/events/api/src/main/java/org/keycloak/events/EventType.java
+++ b/events/api/src/main/java/org/keycloak/events/EventType.java
@@ -68,7 +68,8 @@ public enum EventType {
     IDENTITY_PROVIDER_ACCCOUNT_LINKING_ERROR(false),
     IMPERSONATE(true),
     CUSTOM_REQUIRED_ACTION(true),
-    CUSTOM_REQUIRED_ACTION_ERROR(true);
+    CUSTOM_REQUIRED_ACTION_ERROR(true),
+    EXECUTE_ACTIONS(true);
 
     private boolean saveByDefault;
 
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
index b693827..12c0eb4 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
@@ -304,7 +304,7 @@ module.controller('UserTabCtrl', function($scope, $location, Dialog, Notificatio
     };
 });
 
-module.controller('UserDetailCtrl', function($scope, realm, user, BruteForceUser, User, UserFederationInstances, UserImpersonation, RequiredActions, $location, Dialog, Notifications) {
+module.controller('UserDetailCtrl', function($scope, realm, user, BruteForceUser, User, UserExecuteActionsEmail, UserFederationInstances, UserImpersonation, RequiredActions, $location, Dialog, Notifications) {
     $scope.realm = realm;
     $scope.create = !user.id;
     $scope.editUsername = $scope.create || $scope.realm.editUsernameAllowed;
@@ -389,6 +389,21 @@ module.controller('UserDetailCtrl', function($scope, realm, user, BruteForceUser
         }
     }, true);
 
+    $scope.sendExecuteActionsEmail = function() {
+        if ($scope.changed) {
+            Dialog.message("Cannot send email", "You must save your current changes before you can send an email");
+            return;
+        }
+        Dialog.confirm('Send Email', 'Are you sure you want to send email to user?', function() {
+            UserExecuteActionsEmail.update({ realm: realm.realm, userId: user.id }, { }, function() {
+                Notifications.success("Email sent to user");
+            }, function() {
+                Notifications.error("Failed to send email to user");
+            });
+        });
+    };
+
+
     $scope.save = function() {
         convertAttributeValuesToLists();
 
@@ -513,15 +528,6 @@ module.controller('UserCredentialsCtrl', function($scope, realm, user, User, Use
         });
     };
 
-    $scope.resetPasswordEmail = function() {
-        Dialog.confirm('Reset password email', 'Are you sure you want to send password reset email to user?', function() {
-            UserCredentials.resetPasswordEmail({ realm: realm.realm, userId: user.id }, { }, function() {
-                Notifications.success("Password reset email sent to user");
-            }, function() {
-                Notifications.error("Failed to send password reset mail to user");
-            });
-        });
-    };
 
     $scope.$watch('user', function() {
         if (!angular.equals($scope.user, user)) {
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
index c8ba5f5..845ac2f 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
@@ -5,7 +5,7 @@ var module = angular.module('keycloak.services', [ 'ngResource', 'ngRoute' ]);
 module.service('Dialog', function($modal) {
 	var dialog = {};
 
-    var openDialog = function(title, message, btns) {
+    var openDialog = function(title, message, btns, template) {
         var controller = function($scope, $modalInstance, title, message, btns) {
             $scope.title = title;
             $scope.message = message;
@@ -20,7 +20,7 @@ module.service('Dialog', function($modal) {
         };
 
         return $modal.open({
-            templateUrl: resourceUrl + '/templates/kc-modal.html',
+            templateUrl: resourceUrl + template,
             controller: controller,
             resolve: {
                 title: function() {
@@ -56,7 +56,7 @@ module.service('Dialog', function($modal) {
             }
         }
 
-        openDialog(title, msg, btns).then(success);
+        openDialog(title, msg, btns, '/templates/kc-modal.html').then(success);
 	}
 
     dialog.confirmGenerateKeys = function(name, type, success) {
@@ -73,7 +73,7 @@ module.service('Dialog', function($modal) {
             }
         }
 
-        openDialog(title, msg, btns).then(success);
+        openDialog(title, msg, btns, '/templates/kc-modal.html').then(success);
     }
 
     dialog.confirm = function(title, message, success, cancel) {
@@ -88,10 +88,21 @@ module.service('Dialog', function($modal) {
             }
         }
 
-        openDialog(title, message, btns).then(success, cancel);
+        openDialog(title, message, btns, '/templates/kc-modal.html').then(success, cancel);
     }
 
-	return dialog
+    dialog.message = function(title, message, success, cancel) {
+        var btns = {
+            ok: {
+                label: "Ok",
+                cssClass: 'btn btn-default'
+            }
+        }
+
+        openDialog(title, message, btns, '/templates/kc-modal-message.html').then(success, cancel);
+    }
+
+    return dialog
 });
 
 module.service('CopyDialog', function($modal) {
@@ -427,16 +438,18 @@ module.factory('UserCredentials', function($resource) {
         }
     }).update;
 
-    credentials.resetPasswordEmail = $resource(authUrl + '/admin/realms/:realm/users/:userId/reset-password-email', {
+    return credentials;
+});
+
+module.factory('UserExecuteActionsEmail', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/users/:userId/execute-actions-email', {
         realm : '@realm',
         userId : '@userId'
     }, {
         update : {
             method : 'PUT'
         }
-    }).update;
-
-    return credentials;
+    });
 });
 
 module.factory('RealmRoleMapping', function($resource) {
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-credentials.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-credentials.html
index ffdfd6e..b07371a 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-credentials.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-credentials.html
@@ -37,14 +37,7 @@
                 </div>
             </fieldset>
 
-            <fieldset class="border-top" data-ng-show="user.email || user.totp">
-                <div class="form-group" data-ng-show="user.email">
-                    <label class="col-md-2 control-label" for="password">Reset password email</label>
-                    <div class="col-sm-5">
-                        <button class="btn btn-danger" type="submit" data-ng-click="resetPasswordEmail()" tooltip="Send an email to user with a link to reset their password" tooltip-placement="right">Send Email</button>
-                    </div>
-                </div>
-
+            <fieldset class="border-top" data-ng-show="user.totp">
                 <div class="form-group" data-ng-show="user.totp">
                     <label class="col-md-2 control-label">Remove totp</label>
                     <div class="col-sm-5" data-ng-show="user.totp">
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html
index feedea5..e00d928 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html
@@ -99,6 +99,15 @@
                 </div>
                 <kc-tooltip>Require an action when the user logs in. 'Verify email' sends an email to the user to verify their email address.  'Update profile' requires user to enter in new personal information.  'Update password' requires user to enter in a new password.  'Configure TOTP' requires setup of a mobile password generator.</kc-tooltip>
             </div>
+            <div class="form-group clearfix" data-ng-show="!create && user.email">
+                <label class="col-md-2 control-label" for="reqActionsEmail">Required Actions Email</label>
+
+                <div class="col-md-6">
+                    <button id="reqActionsEmail" class="btn btn-default" data-ng-click="sendExecuteActionsEmail()">Send Email</button>
+                </div>
+                <kc-tooltip>Sends an email to user with an embedded link.  Clicking on link will allow the user to execute all their required actions.  They will not have to login prior to this.  For example, set the required action to update password, click this button, and the user will be able to change their password without logging in.</kc-tooltip>
+            </div>
+
             <div class="form-group clearfix" data-ng-if="realm.internationalizationEnabled">
                 <label class="col-md-2 control-label" for="locale">Locale</label>
                 <div class="col-md-6">
@@ -113,10 +122,10 @@
             </div>
 
             <div class="form-group clearfix">
-                <label class="col-md-2 control-label" for="reqActions">Impersonate user</label>
+                <label class="col-md-2 control-label" for="impersonate">Impersonate user</label>
 
                 <div class="col-md-6">
-                    <button data-ng-show="access.impersonation" class="btn btn-default" data-ng-click="impersonate()">Impersonate</button>
+                    <button id="impersonate" data-ng-show="access.impersonation" class="btn btn-default" data-ng-click="impersonate()">Impersonate</button>
                 </div>
                 <kc-tooltip>Login as this user.  If user is in same realm as you, your current login session will be logged out before you are logged in as this user.</kc-tooltip>
             </div>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-modal-message.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-modal-message.html
new file mode 100755
index 0000000..fc32708
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-modal-message.html
@@ -0,0 +1,10 @@
+<div class="modal-header">
+    <button type="button" class="close" ng-click="cancel()">
+        <span class="pficon pficon-close"></span>
+    </button>
+    <h4 class="modal-title">{{title}}</h4>
+</div>
+<div class="modal-body">{{message}}</div>
+<div class="modal-footer">
+    <button type="button" data-ng-class="btns.ok.cssClass" ng-click="ok()">{{btns.ok.label}}</button>
+</div>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/keycloak/email/messages/messages_de.properties b/forms/common-themes/src/main/resources/theme/keycloak/email/messages/messages_de.properties
index 1777078..ca5c776 100755
--- a/forms/common-themes/src/main/resources/theme/keycloak/email/messages/messages_de.properties
+++ b/forms/common-themes/src/main/resources/theme/keycloak/email/messages/messages_de.properties
@@ -1,10 +1,10 @@
 emailVerificationSubject=E-Mail verifizieren
 passwordResetSubject=Passwort zur\u00FCckzusetzen
-passwordResetBody=Someone just requested to change your {2} account''s password. If this was you, click on the link below to set a new password.\n\n{0}\n\nThis link and code will expire within {1} minutes.\n\nIf you don''t want to reset your password, just ignore this message and nothing will be changed.
-passwordResetBodyHtml=<p>Someone just requested to change your {2} account''s password. If this was you, click on the link below to set a new password.</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you don''t want to reset your password, just ignore this message and nothing will be changed.</p>
-changePasswordSubject=Change password
-changePasswordBody=Your adminstrator has just requested that you change your {2} account''s password. Click on the link below to set a new password\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you don''t want to reset your password, just ignore this message and nothing will be changed.
-changePasswordBodyHtml=<p>Your adminstrator has just requested that you change your {2} account''s password. Click on the link below to set a new password</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you don''t want to reset your password, just ignore this message and nothing will be changed.</p>
+passwordResetBody=Someone just requested to change your {2} account''s credentials. If this was you, click on the link below to reset them.\n\n{0}\n\nThis link and code will expire within {1} minutes.\n\nIf you don''t want to reset your credentials, just ignore this message and nothing will be changed.
+passwordResetBodyHtml=<p>Someone just requested to change your {2} account''s credentials. If this was you, click on the link below to reset them.</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you don''t want to reset your credentials, just ignore this message and nothing will be changed.</p>
+executeActionsSubject=Update Your Account
+executeActionsBody=Your adminstrator has just requested that you update your {2} account. Click on the link below to start this process.\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you are unaware that your admin has requested this, just ignore this message and nothing will be changed.
+executeActionsBodyHtml=<p>Your adminstrator has just requested that you update your {2} account.  Click on the link below to start this process.</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you are unaware that your admin has requested this, just ignore this message and nothing will be changed.</p>
 emailVerificationBody=Jemand hat ein {2} Konto mit dieser E-Mail Adresse erstellt. Fall das Sie waren, dann klicken Sie auf den Link um die E-Mail Adresse zu verifizieren.\n\n{0}\n\nDieser Link wird in {1} Minuten ablaufen.\n\nFalls Sie dieses Konto nicht erstellt haben, dann k\u00F6nnen sie diese Nachricht ignorieren.
 emailVerificationBodyHtml=<p>Jemand hat ein {2} Konto mit dieser E-Mail Adresse erstellt. Fall das Sie waren, dann klicken Sie auf den Link um die E-Mail Adresse zu verifizieren.</p><p><a href="{0}">{0}</a></p><p>Dieser Link wird in {1} Minuten ablaufen.</p><p>Falls Sie dieses Konto nicht erstellt haben, dann k\u00F6nnen sie diese Nachricht ignorieren.</p>
 eventLoginErrorSubject=Fehlgeschlagene Anmeldung
diff --git a/forms/common-themes/src/main/resources/theme/keycloak/email/messages/messages_en.properties b/forms/common-themes/src/main/resources/theme/keycloak/email/messages/messages_en.properties
index dc353e9..fd9d909 100755
--- a/forms/common-themes/src/main/resources/theme/keycloak/email/messages/messages_en.properties
+++ b/forms/common-themes/src/main/resources/theme/keycloak/email/messages/messages_en.properties
@@ -2,11 +2,11 @@ emailVerificationSubject=Verify email
 emailVerificationBody=Someone has created a {2} account with this email address. If this was you, click the link below to verify your email address\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you didn''t create this account, just ignore this message.
 emailVerificationBodyHtml=<p>Someone has created a {2} account with this email address. If this was you, click the link below to verify your email address</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you didn''t create this account, just ignore this message.</p>
 passwordResetSubject=Reset password
-passwordResetBody=Someone just requested to change your {2} account''s password. If this was you, click on the link below to set a new password.\n\n{0}\n\nThis link and code will expire within {1} minutes.\n\nIf you don''t want to reset your password, just ignore this message and nothing will be changed.
-passwordResetBodyHtml=<p>Someone just requested to change your {2} account''s password. If this was you, click on the link below to set a new password.</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you don''t want to reset your password, just ignore this message and nothing will be changed.</p>
-changePasswordSubject=Change password
-changePasswordBody=Your adminstrator has just requested that you change your {2} account''s password. Click on the link below to set a new password\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you don''t want to reset your password, just ignore this message and nothing will be changed.
-changePasswordBodyHtml=<p>Your adminstrator has just requested that you change your {2} account''s password. Click on the link below to set a new password</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you don''t want to reset your password, just ignore this message and nothing will be changed.</p>
+passwordResetBody=Someone just requested to change your {2} account''s credentials. If this was you, click on the link below to reset them.\n\n{0}\n\nThis link and code will expire within {1} minutes.\n\nIf you don''t want to reset your credentials, just ignore this message and nothing will be changed.
+passwordResetBodyHtml=<p>Someone just requested to change your {2} account''s credentials. If this was you, click on the link below to reset them.</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you don''t want to reset your credentials, just ignore this message and nothing will be changed.</p>
+executeActionsSubject=Update Your Account
+executeActionsBody=Your adminstrator has just requested that you update your {2} account. Click on the link below to start this process.\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you are unaware that your admin has requested this, just ignore this message and nothing will be changed.
+executeActionsBodyHtml=<p>Your adminstrator has just requested that you update your {2} account.  Click on the link below to start this process.</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you are unaware that your admin has requested this, just ignore this message and nothing will be changed.</p>
 eventLoginErrorSubject=Login error
 eventLoginErrorBody=A failed login attempt was detected to your account on {0} from {1}. If this was not you, please contact an admin.
 eventLoginErrorBodyHtml=<p>A failed login attempt was detected to your account on {0} from {1}. If this was not you, please contact an admin.</p>
diff --git a/forms/common-themes/src/main/resources/theme/keycloak/email/messages/messages_pt_BR.properties b/forms/common-themes/src/main/resources/theme/keycloak/email/messages/messages_pt_BR.properties
index 91758c3..06b4647 100755
--- a/forms/common-themes/src/main/resources/theme/keycloak/email/messages/messages_pt_BR.properties
+++ b/forms/common-themes/src/main/resources/theme/keycloak/email/messages/messages_pt_BR.properties
@@ -2,11 +2,11 @@ emailVerificationSubject=Verifica\u00E7\u00E3o de e-mail
 emailVerificationBody=Algu\u00E9m criou uma conta {2} com este endere\u00E7o de e-mail. Se foi voc\u00EA, clique no link abaixo para verificar o seu endere\u00E7o de email\n\n{0}\n\nEste link ir\u00E1 expirar dentro de {1} minutos.\n\nSe n\u00E3o foi voc\u00EA que criou esta conta, basta ignorar esta mensagem.
 emailVerificationBodyHtml=<p>Algu\u00E9m criou uma conta {2} com este endere\u00E7o de e-mail. Se foi voc\u00EA, clique no link abaixo para verificar o seu endere\u00E7o de email</p><p><a href="{0}">{0}</a></p><p>Este link ir\u00E1 expirar dentro de {1} minutos.</p><p>Se n\u00E3o foi voc\u00EA que criou esta conta, basta ignorar esta mensagem.</p>
 passwordResetSubject=Redefini\u00E7\u00E3o de senha
-passwordResetBody=Someone just requested to change your {2} account''s password. If this was you, click on the link below to set a new password.\n\n{0}\n\nThis link and code will expire within {1} minutes.\n\nIf you don''t want to reset your password, just ignore this message and nothing will be changed.
-passwordResetBodyHtml=<p>Someone just requested to change your {2} account''s password. If this was you, click on the link below to set a new password.</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you don''t want to reset your password, just ignore this message and nothing will be changed.</p>
-changePasswordSubject=Change password
-changePasswordBody=Your adminstrator has just requested that you change your {2} account''s password. Click on the link below to set a new password\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you don''t want to reset your password, just ignore this message and nothing will be changed.
-changePasswordBodyHtml=<p>Your adminstrator has just requested that you change your {2} account''s password. Click on the link below to set a new password</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you don''t want to reset your password, just ignore this message and nothing will be changed.</p>
+passwordResetBody=Someone just requested to change your {2} account''s credentials. If this was you, click on the link below to reset them.\n\n{0}\n\nThis link and code will expire within {1} minutes.\n\nIf you don''t want to reset your credentials, just ignore this message and nothing will be changed.
+passwordResetBodyHtml=<p>Someone just requested to change your {2} account''s credentials. If this was you, click on the link below to reset them.</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you don''t want to reset your credentials, just ignore this message and nothing will be changed.</p>
+executeActionsSubject=Update Your Account
+executeActionsBody=Your adminstrator has just requested that you update your {2} account. Click on the link below to start this process.\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you are unaware that your admin has requested this, just ignore this message and nothing will be changed.
+executeActionsBodyHtml=<p>Your adminstrator has just requested that you update your {2} account.  Click on the link below to start this process.</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you are unaware that your admin has requested this, just ignore this message and nothing will be changed.</p>
 eventLoginErrorSubject=Erro de login
 eventLoginErrorBody=Uma tentativa de login mal sucedida para a sua conta foi detectada em {0} de {1}. Se n\u00E3o foi voc\u00EA, por favor, entre em contato com um administrador.
 eventLoginErrorBodyHtml=<p>Uma tentativa de login mal sucedida para a sua conta foi detectada em {0} de {1}. Se n\u00E3o foi voc\u00EA, por favor, entre em contato com um administrador.</p>
diff --git a/forms/common-themes/src/main/resources/theme/keycloak/email/text/executeActions.ftl b/forms/common-themes/src/main/resources/theme/keycloak/email/text/executeActions.ftl
new file mode 100755
index 0000000..a33758f
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/keycloak/email/text/executeActions.ftl
@@ -0,0 +1 @@
+${msg("executeActionsBody",link, linkExpiration, realmName)}
\ No newline at end of file
diff --git a/forms/email-api/src/main/java/org/keycloak/email/EmailProvider.java b/forms/email-api/src/main/java/org/keycloak/email/EmailProvider.java
index e860bde..2304ce5 100755
--- a/forms/email-api/src/main/java/org/keycloak/email/EmailProvider.java
+++ b/forms/email-api/src/main/java/org/keycloak/email/EmailProvider.java
@@ -19,7 +19,6 @@ public interface EmailProvider extends Provider {
     /**
      * Reset password sent from forgot password link on login
      *
-     * @param code
      * @param link
      * @param expirationInMinutes
      * @throws EmailException
@@ -33,7 +32,7 @@ public interface EmailProvider extends Provider {
      * @param expirationInMinutes
      * @throws EmailException
      */
-    public void sendChangePassword(String link, long expirationInMinutes) throws EmailException;
+    public void sendExecuteActions(String link, long expirationInMinutes) throws EmailException;
 
     public void sendVerifyEmail(String link, long expirationInMinutes) throws EmailException;
 
diff --git a/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java b/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java
index e2a639c..b620a59 100755
--- a/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java
+++ b/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java
@@ -82,7 +82,7 @@ public class FreeMarkerEmailProvider implements EmailProvider {
     }
 
     @Override
-    public void sendChangePassword(String link, long expirationInMinutes) throws EmailException {
+    public void sendExecuteActions(String link, long expirationInMinutes) throws EmailException {
         Map<String, Object> attributes = new HashMap<String, Object>();
         attributes.put("link", link);
         attributes.put("linkExpiration", expirationInMinutes);
@@ -90,7 +90,7 @@ public class FreeMarkerEmailProvider implements EmailProvider {
         String realmName = realm.getName().substring(0, 1).toUpperCase() + realm.getName().substring(1);
         attributes.put("realmName", realmName);
 
-        send("changePasswordSubject", "changePassword.ftl", attributes);
+        send("executeActionsSubject", "executeActions.ftl", attributes);
 
     }
 
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java
index 7d78d34..1cbf888 100755
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java
@@ -47,12 +47,12 @@ public interface UserResource {
     public void resetPassword(CredentialRepresentation credentialRepresentation);
 
     @PUT
-    @Path("reset-password-email")
-    public void resetPasswordEmail();
+    @Path("execute-actions-email")
+    public void executeActionsEmail();
 
     @PUT
-    @Path("reset-password-email")
-    public void resetPasswordEmail(@QueryParam("client_id") String clientId);
+    @Path("execute-actions-email")
+    public void executeActionsEmail(@QueryParam("client_id") String clientId);
 
     @PUT
     @Path("send-verify-email")
diff --git a/model/api/src/main/java/org/keycloak/models/ClientSessionModel.java b/model/api/src/main/java/org/keycloak/models/ClientSessionModel.java
index 52705a2..c878927 100755
--- a/model/api/src/main/java/org/keycloak/models/ClientSessionModel.java
+++ b/model/api/src/main/java/org/keycloak/models/ClientSessionModel.java
@@ -77,11 +77,12 @@ public interface ClientSessionModel {
         UPDATE_PROFILE,
         CONFIGURE_TOTP,
         UPDATE_PASSWORD,
-        RECOVER_PASSWORD,
+        RECOVER_PASSWORD, // deprecated
         AUTHENTICATE,
         SOCIAL_CALLBACK,
         LOGGED_OUT,
-        RESET_CREDENTIALS
+        RESET_CREDENTIALS,
+        EXECUTE_ACTIONS
     }
 
     public enum ExecutionStatus {
diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/VerifyEmail.java b/services/src/main/java/org/keycloak/authentication/requiredactions/VerifyEmail.java
index f2096cc..11755b2 100755
--- a/services/src/main/java/org/keycloak/authentication/requiredactions/VerifyEmail.java
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/VerifyEmail.java
@@ -8,6 +8,7 @@ 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;
@@ -35,6 +36,13 @@ public class VerifyEmail implements RequiredActionProvider, RequiredActionFactor
     }
     @Override
     public void requiredActionChallenge(RequiredActionContext context) {
+        // if this is EXECUTE_ACTIONS we know that the email sent is valid so we can verify it automatically
+        if (context.getClientSession().getNote(ClientSessionModel.Action.EXECUTE_ACTIONS.name()) != null) {
+            context.getUser().setEmailVerified(true);
+            context.success();
+            return;
+        }
+
         if (Validation.isBlank(context.getUser().getEmail())) {
             context.ignore();
             return;
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 6d55bc2..3158b69 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
@@ -834,10 +834,10 @@ public class UsersResource {
      * @param clientId client id
      * @return
      */
-    @Path("{id}/reset-password-email")
+    @Path("{id}/execute-actions-email")
     @PUT
     @Consumes(MediaType.APPLICATION_JSON)
-    public Response resetPasswordEmail(@PathParam("id") String id, @QueryParam(OIDCLoginProtocol.REDIRECT_URI_PARAM) String redirectUri, @QueryParam(OIDCLoginProtocol.CLIENT_ID_PARAM) String clientId) {
+    public Response executeActionsEmail(@PathParam("id") String id, @QueryParam(OIDCLoginProtocol.REDIRECT_URI_PARAM) String redirectUri, @QueryParam(OIDCLoginProtocol.CLIENT_ID_PARAM) String clientId) {
         auth.requireManage();
 
         UserModel user = session.users().getUserById(id, realm);
@@ -851,17 +851,16 @@ public class UsersResource {
 
         ClientSessionModel clientSession = createClientSession(user, redirectUri, clientId);
         ClientSessionCode accessCode = new ClientSessionCode(realm, clientSession);
-        accessCode.setAction(ClientSessionModel.Action.RECOVER_PASSWORD.name());
+        accessCode.setAction(ClientSessionModel.Action.EXECUTE_ACTIONS.name());
 
         try {
-            user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
-            UriBuilder builder = Urls.recoverPasswordBuilder(uriInfo.getBaseUri());
+            UriBuilder builder = Urls.executeActionsBuilder(uriInfo.getBaseUri());
             builder.queryParam("key", accessCode.getCode());
 
             String link = builder.build(realm.getName()).toString();
             long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction());
 
-            this.session.getProvider(EmailProvider.class).setRealm(realm).setUser(user).sendChangePassword(link, expiration);
+            this.session.getProvider(EmailProvider.class).setRealm(realm).setUser(user).sendExecuteActions(link, expiration);
 
             //audit.user(user).detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, accessCode.getCodeId()).success();
 
@@ -869,8 +868,8 @@ public class UsersResource {
 
             return Response.ok().build();
         } catch (EmailException e) {
-            logger.error("Failed to send password reset email", e);
-            return ErrorResponse.error("Failed to send email", Response.Status.INTERNAL_SERVER_ERROR);
+            logger.error("Failed to send execute actions email", e);
+            return ErrorResponse.error("Failed to send execute actions email", Response.Status.INTERNAL_SERVER_ERROR);
         }
     }
 
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 0a8d3c6..86adcdc 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -274,18 +274,13 @@ public class LoginActionsService {
                                  @QueryParam("execution") String execution) {
         event.event(EventType.LOGIN);
         Checks checks = new Checks();
-        if (!checks.verifyCode(code, ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionModel.Action.RECOVER_PASSWORD.name())) {
+        if (!checks.verifyCode(code, ClientSessionModel.Action.AUTHENTICATE.name())) {
             return checks.response;
         }
         event.detail(Details.CODE_ID, code);
         ClientSessionCode clientSessionCode = checks.clientCode;
         ClientSessionModel clientSession = clientSessionCode.getClientSession();
 
-        if (clientSession.getAction().equals(ClientSessionModel.Action.RECOVER_PASSWORD.name())) {
-            TokenManager.dettachClientSession(session.sessions(), realm, clientSession);
-            clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
-        }
-
         return processAuthentication(execution, clientSession, null);
     }
 
@@ -568,20 +563,21 @@ public class LoginActionsService {
      * @param key
      * @return
      */
-    @Path("recover-password")
+    @Path("execute-actions")
     @GET
-    public Response recoverPassword(@QueryParam("key") String key) {
-        event.event(EventType.RESET_PASSWORD);
+    public Response executeActions(@QueryParam("key") String key) {
+        event.event(EventType.EXECUTE_ACTIONS);
         if (key != null) {
             Checks checks = new Checks();
-            if (!checks.verifyCode(key, ClientSessionModel.Action.RECOVER_PASSWORD.name())) {
+            if (!checks.verifyCode(key, ClientSessionModel.Action.EXECUTE_ACTIONS.name())) {
                 return checks.response;
             }
             ClientSessionModel clientSession = checks.clientCode.getClientSession();
             clientSession.setNote("END_AFTER_REQUIRED_ACTIONS", "true");
+            clientSession.setNote(ClientSessionModel.Action.EXECUTE_ACTIONS.name(), "true");
             return AuthenticationManager.nextActionAfterAuthentication(session, clientSession.getUserSession(), clientSession, clientConnection, request, uriInfo, event);
         } else {
-            event.error(Errors.RESET_CREDENTIAL_DISABLED);
+            event.error(Errors.INVALID_CODE);
             return ErrorPage.error(session, Messages.INVALID_CODE);
         }
     }
diff --git a/services/src/main/java/org/keycloak/services/Urls.java b/services/src/main/java/org/keycloak/services/Urls.java
index 57991b3..cb7292b 100755
--- a/services/src/main/java/org/keycloak/services/Urls.java
+++ b/services/src/main/java/org/keycloak/services/Urls.java
@@ -156,8 +156,8 @@ public class Urls {
         return loginResetCredentialsBuilder(baseUri).build(realmId);
     }
 
-    public static UriBuilder recoverPasswordBuilder(URI baseUri) {
-        return loginActionsBase(baseUri).path(LoginActionsService.class, "recoverPassword");
+    public static UriBuilder executeActionsBuilder(URI baseUri) {
+        return loginActionsBase(baseUri).path(LoginActionsService.class, "executeActions");
     }
 
     public static UriBuilder loginResetCredentialsBuilder(URI baseUri) {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/UserTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/UserTest.java
index aa5c392..25a98a0 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/UserTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/UserTest.java
@@ -361,7 +361,7 @@ public class UserTest extends AbstractClientTest {
         UserResource user = realm.users().get(id);
 
         try {
-            user.resetPasswordEmail();
+            user.executeActionsEmail();
             fail("Expected failure");
         } catch (ClientErrorException e) {
             assertEquals(400, e.getResponse().getStatus());
@@ -374,7 +374,7 @@ public class UserTest extends AbstractClientTest {
             userRep.setEmail("user1@localhost");
             userRep.setEnabled(false);
             user.update(userRep);
-            user.resetPasswordEmail();
+            user.executeActionsEmail();
             fail("Expected failure");
         } catch (ClientErrorException e) {
             assertEquals(400, e.getResponse().getStatus());
@@ -385,7 +385,7 @@ public class UserTest extends AbstractClientTest {
         try {
             userRep.setEnabled(true);
             user.update(userRep);
-            user.resetPasswordEmail("invalidClientId");
+            user.executeActionsEmail("invalidClientId");
             fail("Expected failure");
         } catch (ClientErrorException e) {
             assertEquals(400, e.getResponse().getStatus());