diff --git a/wp-content/plugins/wds-citytech/assets/js/passwords-admin.js b/wp-content/plugins/wds-citytech/assets/js/passwords-admin.js
new file mode 100644
index 0000000000..916af39db5
--- /dev/null
+++ b/wp-content/plugins/wds-citytech/assets/js/passwords-admin.js
@@ -0,0 +1,22 @@
+/* global jQuery */
+(function($){
+ $(document).ready(function(){
+ const $passwordExpiration = $('#password-expiration');
+
+ $( '#set-password-expiration' ).click(function(e){
+ e.preventDefault();
+
+ const now = new Date();
+ const formattedDate = now.toISOString().slice( 0, 16 );
+
+ $passwordExpiration.val( formattedDate );
+ });
+
+ $( '#clear-password-expiration' ).click(function(e){
+ e.preventDefault();
+
+ $passwordExpiration.val( '' );
+ });
+
+ });
+})(jQuery);
diff --git a/wp-content/plugins/wds-citytech/includes/passwords.php b/wp-content/plugins/wds-citytech/includes/passwords.php
new file mode 100644
index 0000000000..e94a0fe275
--- /dev/null
+++ b/wp-content/plugins/wds-citytech/includes/passwords.php
@@ -0,0 +1,196 @@
+ID );
+
+ ?>
+
+
Password Expiration
+
+
+ This user's password does not currently have an expiration date.
+ time() ) : ?>
+ This user's password will expire on at (UTC).
+
+ This user's password expired on at (UTC).
+
+
+
+
+ ID );
+
+ if ( ! $expiration ) {
+ return $user;
+ }
+
+ if ( $expiration < time() ) {
+ return new \WP_Error(
+ 'password_expired',
+ 'Your password has expired. Please reset it.' ,
+ [
+ 'username' => $username,
+ ]
+ );
+ }
+
+ return $user;
+}
+add_filter( 'authenticate', __NAMESPACE__ . '\check_password_expiration', 999, 3 );
+
+/**
+ * Perform a redirect when a user's password has expired.
+ *
+ * We use the 'login_redirect' filter because it occurs after the user has been authenticated.
+ *
+ * @param string $redirect_to The redirect URL.
+ * @param string $requested_redirect_to The requested redirect URL.
+ * @param \WP_User|\WP_Error $user The WP_User object.
+ * @return string The redirect URL.
+ */
+function login_redirect( $redirect_to, $requested_redirect_to, $user ) {
+ if ( \is_wp_error( $user ) && 'password_expired' === $user->get_error_code() ) {
+ $redirect_url = add_query_arg(
+ [
+ 'action' => 'lostpassword',
+ 'password_expired' => 'true',
+ 'user_login' => $user->get_error_data()['username'],
+ ],
+ wp_login_url()
+ );
+
+ wp_safe_redirect( $redirect_url );
+ die;
+ }
+
+ return $redirect_to;
+}
+add_filter( 'login_redirect', __NAMESPACE__ . '\login_redirect', 50, 3 );
+
+/**
+ * Filters the message to display when a user's password has expired.
+ *
+ * @param string $message The message.
+ * @return string The message.
+ */
+function password_expired_message( $message ) {
+ if ( empty( $_GET['action'] ) || 'lostpassword' !== $_GET['action'] ) {
+ return $message;
+ }
+
+ if ( empty( $_GET['password_expired'] ) || 'true' !== $_GET['password_expired'] ) {
+ return $message;
+ }
+
+ $message = wp_get_admin_notice(
+ 'Your password has expired. To reset it, please enter your username or email address below. You will receive an email with instructions on how to proceed.',
+ [
+ 'type' => 'error',
+ 'additional_classes' => [ 'password-expired' ],
+ ]
+ );
+
+ return $message;
+}
+add_filter( 'login_message', __NAMESPACE__ . '\password_expired_message' );
diff --git a/wp-content/plugins/wds-citytech/wds-citytech.php b/wp-content/plugins/wds-citytech/wds-citytech.php
index 19eb5a1455..2a38e6494c 100755
--- a/wp-content/plugins/wds-citytech/wds-citytech.php
+++ b/wp-content/plugins/wds-citytech/wds-citytech.php
@@ -23,6 +23,7 @@
require 'includes/user-moderation.php';
require 'includes/block-widgets.php';
require 'includes/cbox-polyfills/index.php';
+require 'includes/passwords.php';
// Conditionally load Easy TOC modifications.
add_action( 'plugins_loaded', function() {