diff --git a/CHANGELOG b/CHANGELOG
index 2837b321d5404a8f36dcc462241bfb39c927e789..087a339f3f66a23caac706b30b95853cff918450 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -29,6 +29,7 @@ v 8.8.0 (unreleased)
   - Display informative message when new milestone is created
   - Sanitize milestones and labels titles
   - Support multi-line tag messages. !3833 (Calin Seciu)
+  - Force users to reset their password after an admin changes it
   - Allow "NEWS" and "CHANGES" as alternative names for CHANGELOG. !3768 (Connor Shea)
   - Added button to toggle whitespaces changes on diff view
   - Backport GitHub Enterprise import support from EE
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index f2f654c7bcdeb37c77454da89c266a3c850266e4..6908a3bf946e94a16bec1411b02d8db70113af45 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -119,6 +119,7 @@ class Admin::UsersController < Admin::ApplicationController
       user_params_with_pass.merge!(
         password: params[:user][:password],
         password_confirmation: params[:user][:password_confirmation],
+        password_expires_at: Time.now
       )
     end
 
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
index ce2a62ae1fd3319d7b03ac62d7c579a1eca07264..6caf37ddc2c3057b71ca0fc4ab4e060ea80926a5 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -114,6 +114,82 @@ describe Admin::UsersController do
     end
   end
 
+  describe 'POST update' do
+    context 'when the password has changed' do
+      def update_password(user, password, password_confirmation = nil)
+        params = {
+          id: user.to_param,
+          user: {
+            password: password,
+            password_confirmation: password_confirmation || password
+          }
+        }
+
+        post :update, params
+      end
+
+      context 'when the new password is valid' do
+        it 'redirects to the user' do
+          update_password(user, 'AValidPassword1')
+
+          expect(response).to redirect_to(admin_user_path(user))
+        end
+
+        it 'updates the password' do
+          update_password(user, 'AValidPassword1')
+
+          expect { user.reload }.to change { user.encrypted_password }
+        end
+
+        it 'sets the new password to expire immediately' do
+          update_password(user, 'AValidPassword1')
+
+          expect { user.reload }.to change { user.password_expires_at }.to(a_value <= Time.now)
+        end
+      end
+
+      context 'when the new password is invalid' do
+        it 'shows the edit page again' do
+          update_password(user, 'invalid')
+
+          expect(response).to render_template(:edit)
+        end
+
+        it 'returns the error message' do
+          update_password(user, 'invalid')
+
+          expect(assigns[:user].errors).to contain_exactly(a_string_matching(/too short/))
+        end
+
+        it 'does not update the password' do
+          update_password(user, 'invalid')
+
+          expect { user.reload }.not_to change { user.encrypted_password }
+        end
+      end
+
+      context 'when the new password does not match the password confirmation' do
+        it 'shows the edit page again' do
+          update_password(user, 'AValidPassword1', 'AValidPassword2')
+
+          expect(response).to render_template(:edit)
+        end
+
+        it 'returns the error message' do
+          update_password(user, 'AValidPassword1', 'AValidPassword2')
+
+          expect(assigns[:user].errors).to contain_exactly(a_string_matching(/doesn't match/))
+        end
+
+        it 'does not update the password' do
+          update_password(user, 'AValidPassword1', 'AValidPassword2')
+
+          expect { user.reload }.not_to change { user.encrypted_password }
+        end
+      end
+    end
+  end
+
   describe "POST impersonate" do
     context "when the user is blocked" do
       before do
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index 4570e4091284cea19d32166a21f45192814babef..6dee0cd8d47d63c479255c1e7f791e358f86d19d 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -210,6 +210,8 @@ describe "Admin::Users", feature: true  do
       before do
         fill_in "user_name", with: "Big Bang"
         fill_in "user_email", with: "bigbang@mail.com"
+        fill_in "user_password", with: "AValidPassword1"
+        fill_in "user_password_confirmation", with: "AValidPassword1"
         check "user_admin"
         click_button "Save changes"
       end
@@ -223,6 +225,7 @@ describe "Admin::Users", feature: true  do
         @simple_user.reload
         expect(@simple_user.name).to eq('Big Bang')
         expect(@simple_user.is_admin?).to be_truthy
+        expect(@simple_user.password_expires_at).to be <= Time.now
       end
     end
   end