From c6dfdcc95228ff4ba5a904f166d3df0feca506ae Mon Sep 17 00:00:00 2001 From: PRASAD KARMALKAR Date: Mon, 8 Sep 2025 18:29:06 +0530 Subject: [PATCH 1/6] Send email notification when an application password is added --- .../class-wp-application-passwords.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/wp-includes/class-wp-application-passwords.php b/src/wp-includes/class-wp-application-passwords.php index 6e84e0a2b2d0b..1b907c93362ee 100644 --- a/src/wp-includes/class-wp-application-passwords.php +++ b/src/wp-includes/class-wp-application-passwords.php @@ -149,6 +149,23 @@ public static function create_new_application_password( $user_id, $args = array( */ do_action( 'wp_create_application_password', $user_id, $new_item, $new_password, $args ); + // Send email notification to user when a new application password is created. + $user = get_userdata( $user_id ); + if ( $user && $user->user_email ) { + $site_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); + $subject = sprintf( __( '[%s] New application password added to your account' ), $site_name ); + $message = sprintf( + __( "Hello %s,\n\nA new application password named '%s' was added to your account on %s. This password allows access to your account via the REST API.\n\nIf you did not expect this, please review your account security and revoke the password immediately.\n\nApplication password name: %s\nSite: %s\n\nYou can manage your application passwords in your account settings.\n\nRegards,\n%s" ), + $user->display_name, + $new_item['name'], + $site_name, + $new_item['name'], + home_url(), + $site_name + ); + wp_mail( $user->user_email, $subject, $message ); + } + return array( $new_password, $new_item ); } From 48428045cf0ef51680ff1a2440246e2e0152075f Mon Sep 17 00:00:00 2001 From: PRASAD KARMALKAR Date: Mon, 8 Sep 2025 18:49:17 +0530 Subject: [PATCH 2/6] Fix phpcs issues --- src/wp-includes/class-wp-application-passwords.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/class-wp-application-passwords.php b/src/wp-includes/class-wp-application-passwords.php index 1b907c93362ee..f61098e5d8cb8 100644 --- a/src/wp-includes/class-wp-application-passwords.php +++ b/src/wp-includes/class-wp-application-passwords.php @@ -153,9 +153,10 @@ public static function create_new_application_password( $user_id, $args = array( $user = get_userdata( $user_id ); if ( $user && $user->user_email ) { $site_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); + // translators: 1: Site name. $subject = sprintf( __( '[%s] New application password added to your account' ), $site_name ); $message = sprintf( - __( "Hello %s,\n\nA new application password named '%s' was added to your account on %s. This password allows access to your account via the REST API.\n\nIf you did not expect this, please review your account security and revoke the password immediately.\n\nApplication password name: %s\nSite: %s\n\nYou can manage your application passwords in your account settings.\n\nRegards,\n%s" ), + __( "Hello %1\$s,\n\nA new application password named '%2\$s' was added to your account on %3\$s. This password allows access to your account via the REST API.\n\nIf you did not expect this, please review your account security and revoke the password immediately.\n\nApplication password name: %4\$s\nSite: %5\$s\n\nYou can manage your application passwords in your account settings.\n\nRegards,\n%6\$s" ), $user->display_name, $new_item['name'], $site_name, From 0c296665f38d43164ad17ffbbe4e2261107224b1 Mon Sep 17 00:00:00 2001 From: PRASAD KARMALKAR Date: Tue, 9 Sep 2025 17:37:58 +0530 Subject: [PATCH 3/6] Add email notification for newly created application passwords --- .../class-wp-application-passwords.php | 18 --- src/wp-includes/default-filters.php | 3 + src/wp-includes/functions.php | 109 ++++++++++++++++++ 3 files changed, 112 insertions(+), 18 deletions(-) diff --git a/src/wp-includes/class-wp-application-passwords.php b/src/wp-includes/class-wp-application-passwords.php index f61098e5d8cb8..6e84e0a2b2d0b 100644 --- a/src/wp-includes/class-wp-application-passwords.php +++ b/src/wp-includes/class-wp-application-passwords.php @@ -149,24 +149,6 @@ public static function create_new_application_password( $user_id, $args = array( */ do_action( 'wp_create_application_password', $user_id, $new_item, $new_password, $args ); - // Send email notification to user when a new application password is created. - $user = get_userdata( $user_id ); - if ( $user && $user->user_email ) { - $site_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); - // translators: 1: Site name. - $subject = sprintf( __( '[%s] New application password added to your account' ), $site_name ); - $message = sprintf( - __( "Hello %1\$s,\n\nA new application password named '%2\$s' was added to your account on %3\$s. This password allows access to your account via the REST API.\n\nIf you did not expect this, please review your account security and revoke the password immediately.\n\nApplication password name: %4\$s\nSite: %5\$s\n\nYou can manage your application passwords in your account settings.\n\nRegards,\n%6\$s" ), - $user->display_name, - $new_item['name'], - $site_name, - $new_item['name'], - home_url(), - $site_name - ); - wp_mail( $user->user_email, $subject, $message ); - } - return array( $new_password, $new_item ); } diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index 936bbb6a8673f..afb1a03c90e53 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -339,6 +339,9 @@ add_filter( 'rest_authentication_errors', 'rest_application_password_check_errors', 90 ); add_filter( 'rest_authentication_errors', 'rest_cookie_check_errors', 100 ); +// Application password notifications. +add_action( 'wp_create_application_password', 'wp_application_password_created_notification', 10, 2 ); + // Actions. add_action( 'wp_head', '_wp_render_title_tag', 1 ); add_action( 'wp_head', 'wp_enqueue_scripts', 1 ); diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index cab6f5e88aa7f..c21641197ce28 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -9218,3 +9218,112 @@ function wp_verify_fast_hash( return hash_equals( $hash, wp_fast_hash( $message ) ); } + +/** + * Sends an email to the user when a new application password is created. + * + * @since n.e.x.t + * + * @param int $user_id The user ID. + * @param array $new_item The created application password details. + */ +function wp_application_password_created_notification( $user_id, $new_item ) { + $send = true; + + // Get current user data. + $user = get_userdata( $user_id ); + + if ( ! $user ) { + return; + } + + if ( ! is_email( $user->user_email ) ) { + return; + } + + // Validate that the application password has a name. + if ( empty( $new_item['name'] ) ) { + return; + } + + /** + * Filters whether to send the application password created notification email. + * + * @since 5.6.0 + * + * @param bool $send Whether to send the email notification. + * @param WP_User $user The user object. + * @param array $new_item The created application password details. + */ + $send = apply_filters( 'wp_send_application_password_created_email', $send, $user, $new_item ); + + if ( ! $send ) { + return; + } + + /* translators: Do not translate USER_DISPLAY_NAME, APPLICATION_PASSWORD_NAME, SITENAME, SITEURL: those are placeholders. */ + $application_password_create_text = __( + 'Hello ###USER_DISPLAY_NAME###, + +A new application password named "###APPLICATION_PASSWORD_NAME###" was added to your account on ###SITENAME###. This password allows access to your account via the REST API. + +If you did not expect this, please review your account security and revoke the password immediately. + +Application password name: ###APPLICATION_PASSWORD_NAME### +Site: ###SITEURL### + +You can manage your application passwords in your account settings. + +Regards, +###SITENAME###' + ); + + $application_password_create_email = array( + 'to' => $user->user_email, + /* translators: Application password creation email subject. %s: Site title. */ + 'subject' => __( '[%s] Application Password Created' ), + 'message' => $application_password_create_text, + 'headers' => '', + ); + + // Get site name. + $site_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); + + /** + * Filters the contents of the email notification sent when a new application password is created. + * + * @since n.e.x.t + * + * @param array $application_password_create_email { + * Used to build wp_mail(). + * + * @type string $to The intended recipient. + * @type string $subject The subject of the email. + * @type string $message The content of the email. + * The following strings have a special meaning and will get replaced dynamically: + * - `###USER_DISPLAY_NAME###` The user's display name. + * - `###APPLICATION_PASSWORD_NAME###` The name of the application password. + * - `###SITENAME###` The name of the site. + * - `###SITEURL###` The URL to the site. + * @type string $headers Headers. + * } + * @param WP_User $user The user object. + * @param array $new_item The created application password details. + */ + $application_password_create_email = apply_filters( 'wp_application_password_created_email', $application_password_create_email, $user, $new_item ); + + $application_password_create_email['message'] = str_replace( '###USER_DISPLAY_NAME###', $user->display_name, $application_password_create_email['message'] ); + $application_password_create_email['message'] = str_replace( '###APPLICATION_PASSWORD_NAME###', $new_item['name'], $application_password_create_email['message'] ); + $application_password_create_email['message'] = str_replace( '###SITENAME###', $site_name, $application_password_create_email['message'] ); + $application_password_create_email['message'] = str_replace( '###SITEURL###', home_url(), $application_password_create_email['message'] ); + + wp_mail( + $application_password_create_email['to'], + sprintf( + $application_password_create_email['subject'], + $site_name + ), + $application_password_create_email['message'], + $application_password_create_email['headers'] + ); +} From 3478c81e2b4c50ec5586fb3ef01fcff5883c9111 Mon Sep 17 00:00:00 2001 From: John Blackbourn Date: Mon, 31 Aug 2026 23:39:44 +0200 Subject: [PATCH 4/6] Boop. --- src/wp-includes/functions.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index ca75833a4b06f..44a3621eb7682 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -9431,7 +9431,7 @@ function wp_verify_fast_hash( /** * Sends an email to the user when a new application password is created. * - * @since n.e.x.t + * @since 7.2.0 * * @param int $user_id The user ID. * @param array $new_item The created application password details. @@ -9458,7 +9458,7 @@ function wp_application_password_created_notification( $user_id, $new_item ) { /** * Filters whether to send the application password created notification email. * - * @since 5.6.0 + * @since 7.2.0 * * @param bool $send Whether to send the email notification. * @param WP_User $user The user object. @@ -9501,7 +9501,7 @@ function wp_application_password_created_notification( $user_id, $new_item ) { /** * Filters the contents of the email notification sent when a new application password is created. * - * @since n.e.x.t + * @since 7.2.0 * * @param array $application_password_create_email { * Used to build wp_mail(). From d23a15f84a281b4d88215d6fb2aa4a4aa7a0ff8d Mon Sep 17 00:00:00 2001 From: John Blackbourn Date: Mon, 31 Aug 2026 23:45:17 +0200 Subject: [PATCH 5/6] Tidying up. --- src/wp-includes/functions.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index 44a3621eb7682..0086bc7f774f1 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -9487,7 +9487,7 @@ function wp_application_password_created_notification( $user_id, $new_item ) { ###SITENAME###' ); - $application_password_create_email = array( + $email = array( 'to' => $user->user_email, /* translators: Application password creation email subject. %s: Site title. */ 'subject' => __( '[%s] Application Password Created' ), @@ -9503,7 +9503,7 @@ function wp_application_password_created_notification( $user_id, $new_item ) { * * @since 7.2.0 * - * @param array $application_password_create_email { + * @param array $email { * Used to build wp_mail(). * * @type string $to The intended recipient. @@ -9519,20 +9519,20 @@ function wp_application_password_created_notification( $user_id, $new_item ) { * @param WP_User $user The user object. * @param array $new_item The created application password details. */ - $application_password_create_email = apply_filters( 'wp_application_password_created_email', $application_password_create_email, $user, $new_item ); + $email = apply_filters( 'wp_application_password_created_email', $email, $user, $new_item ); - $application_password_create_email['message'] = str_replace( '###USER_DISPLAY_NAME###', $user->display_name, $application_password_create_email['message'] ); - $application_password_create_email['message'] = str_replace( '###APPLICATION_PASSWORD_NAME###', $new_item['name'], $application_password_create_email['message'] ); - $application_password_create_email['message'] = str_replace( '###SITENAME###', $site_name, $application_password_create_email['message'] ); - $application_password_create_email['message'] = str_replace( '###SITEURL###', home_url(), $application_password_create_email['message'] ); + $email['message'] = str_replace( '###USER_DISPLAY_NAME###', $user->display_name, $email['message'] ); + $email['message'] = str_replace( '###APPLICATION_PASSWORD_NAME###', $new_item['name'], $email['message'] ); + $email['message'] = str_replace( '###SITENAME###', $site_name, $email['message'] ); + $email['message'] = str_replace( '###SITEURL###', home_url(), $email['message'] ); wp_mail( - $application_password_create_email['to'], + $email['to'], sprintf( - $application_password_create_email['subject'], + $email['subject'], $site_name ), - $application_password_create_email['message'], - $application_password_create_email['headers'] + $email['message'], + $email['headers'] ); } From e72cff73fde38661401607795066f5072426576d Mon Sep 17 00:00:00 2001 From: John Blackbourn Date: Tue, 1 Sep 2026 13:39:31 +0200 Subject: [PATCH 6/6] Adjustments to the email body text. --- src/wp-includes/functions.php | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index 0086bc7f774f1..ed17d22f9b42c 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -9434,7 +9434,7 @@ function wp_verify_fast_hash( * @since 7.2.0 * * @param int $user_id The user ID. - * @param array $new_item The created application password details. + * @param array $new_item The application password details. */ function wp_application_password_created_notification( $user_id, $new_item ) { $send = true; @@ -9462,7 +9462,7 @@ function wp_application_password_created_notification( $user_id, $new_item ) { * * @param bool $send Whether to send the email notification. * @param WP_User $user The user object. - * @param array $new_item The created application password details. + * @param array $new_item The application password details. */ $send = apply_filters( 'wp_send_application_password_created_email', $send, $user, $new_item ); @@ -9470,21 +9470,25 @@ function wp_application_password_created_notification( $user_id, $new_item ) { return; } - /* translators: Do not translate USER_DISPLAY_NAME, APPLICATION_PASSWORD_NAME, SITENAME, SITEURL: those are placeholders. */ + /* translators: Do not translate USERNAME, APPLICATION_PASSWORD_NAME, SITENAME, SITEURL, EMAIL: those are placeholders. */ $application_password_create_text = __( - 'Hello ###USER_DISPLAY_NAME###, + 'Hi ###USERNAME###, -A new application password named "###APPLICATION_PASSWORD_NAME###" was added to your account on ###SITENAME###. This password allows access to your account via the REST API. +A new application password was added to your account on ###SITENAME###. This password allows access to your account via the REST API. -If you did not expect this, please review your account security and revoke the password immediately. +If you did not expect this, please contact the Site Administrator at +###ADMIN_EMAIL### Application password name: ###APPLICATION_PASSWORD_NAME### Site: ###SITEURL### You can manage your application passwords in your account settings. +This email has been sent to ###EMAIL### + Regards, -###SITENAME###' +All at ###SITENAME### +###SITEURL###' ); $email = array( @@ -9499,30 +9503,32 @@ function wp_application_password_created_notification( $user_id, $new_item ) { $site_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); /** - * Filters the contents of the email notification sent when a new application password is created. + * Filters the contents of the email notification sent to a user when a new application password is created. * * @since 7.2.0 * * @param array $email { * Used to build wp_mail(). * - * @type string $to The intended recipient. + * @type string $to The email address of the intended recipient. * @type string $subject The subject of the email. * @type string $message The content of the email. * The following strings have a special meaning and will get replaced dynamically: - * - `###USER_DISPLAY_NAME###` The user's display name. + * - `###USERNAME###` The user's display name. * - `###APPLICATION_PASSWORD_NAME###` The name of the application password. - * - `###SITENAME###` The name of the site. - * - `###SITEURL###` The URL to the site. + * - `###EMAIL###` The user's email address. + * - `###SITENAME###` The name of the site. + * - `###SITEURL###` The URL to the site. * @type string $headers Headers. * } * @param WP_User $user The user object. - * @param array $new_item The created application password details. + * @param array $new_item The application password details. */ $email = apply_filters( 'wp_application_password_created_email', $email, $user, $new_item ); - $email['message'] = str_replace( '###USER_DISPLAY_NAME###', $user->display_name, $email['message'] ); + $email['message'] = str_replace( '###USERNAME###', $user->display_name, $email['message'] ); $email['message'] = str_replace( '###APPLICATION_PASSWORD_NAME###', $new_item['name'], $email['message'] ); + $email['message'] = str_replace( '###EMAIL###', $user->user_email, $email['message'] ); $email['message'] = str_replace( '###SITENAME###', $site_name, $email['message'] ); $email['message'] = str_replace( '###SITEURL###', home_url(), $email['message'] );