Index: src/wp-includes/user.php
===================================================================
--- src/wp-includes/user.php	(revision 42873)
+++ src/wp-includes/user.php	(working copy)
@@ -2815,20 +2815,28 @@
  *
  * @since 5.0.0
  *
- * @param string $action_name        Name of the action that is being confirmed.
- * @param string $action_description User facing description of the action they will be confirming.
  * @param string $email              User email address. This can be the address of a registered or non-registered user. Defaults to logged in user email address.
- *
- * @return WP_ERROR|bool Will return true/false based on the success of sending the email, or a WP_Error object.
+ * @param string $action_name        Name of the action that is being confirmed. Defaults to 'confirm_email'.
+ * @param string $action_description User facing description of the action they will be confirming. Defaults to "confirm your email address".
+ * @param array  $request_data       Misc data you want to send with the verification request and pass to the actions once the request is confirmed.
+ * @return WP_Error|bool Will return true/false based on the success of sending the email, or a WP_Error object.
  */
-function send_confirm_account_action_email( $action_name, $action_description = '', $email = '' ) {
+function wp_send_account_verification_key( $email = '', $action_name = '', $action_description = '', $request_data = array() ) {
+	if ( ! function_exists( 'wp_get_current_user' ) ) {
+		return new WP_Error( 'invalid', __( 'This function cannot be used before init.' ) );
+	}
+
 	$action_name        = sanitize_key( $action_name );
 	$action_description = wp_kses_post( $action_description );
 
 	if ( empty( $action_name ) ) {
-		return new WP_Error( 'invalid_action', __( 'Invalid action' ) );
+		$action_name = 'confirm_email';
 	}
 
+	if ( empty( $action_description ) ) {
+		$action_description = __( 'Confirm your email address.' );
+	}
+
 	if ( empty( $email ) ) {
 		$user  = wp_get_current_user();
 		$email = $user->ID ? $user->user_email : '';
@@ -2846,18 +2854,20 @@
 		$user = get_user_by( 'email', $email );
 	}
 
-	// We could be dealing with a registered user account, or a visitor.
-	$is_registered_user = $user && ! is_wp_error( $user );
-	$uid                = $is_registered_user ? $user->ID : hash( 'sha256', $email );
-	$confirm_key        = get_confirm_account_action_key( $action_name, $email );
+	$confirm_key = wp_get_account_verification_key( $email, $action_name, $request_data );
 
 	if ( is_wp_error( $confirm_key ) ) {
 		return $confirm_key;
 	}
 
-	// Prepare the email content.
-	if ( ! $action_description ) {
-		$action_description = $action_name;
+	// We could be dealing with a registered user account, or a visitor.
+	$is_registered_user = $user && ! is_wp_error( $user );
+
+	if ( $is_registered_user ) {
+		$uid = $user->ID;
+	} else {
+		// Generate a UID for this email address so we don't send the actual email in the query string. Hash is not supported on all systems.
+		$uid = function_exists( 'hash' ) ? hash( 'sha256', $email ) : sha1( $email );
 	}
 
 	/* translators: Do not translate DESCRIPTION, CONFIRM_URL, EMAIL, SITENAME, SITEURL: those are placeholders. */
@@ -2864,12 +2874,11 @@
 	$email_text = __(
 		'Howdy,
 
-An account linked to your email address has requested to perform
-the following action:
+A request has been made to perform the following action on your account:
 
      ###DESCRIPTION###
 
-To confirm this action, please click on the following link:
+To confirm this, please click on the following link:
 ###CONFIRM_URL###
 
 You can safely ignore and delete this email if you do not want to
@@ -2887,7 +2896,7 @@
 		'email'       => $email,
 		'description' => $action_description,
 		'confirm_url' => add_query_arg( array(
-			'action'         => 'emailconfirm',
+			'action'         => 'verifyaccount',
 			'confirm_action' => $action_name,
 			'uid'            => $uid,
 			'confirm_key'    => $confirm_key,
@@ -2907,6 +2916,8 @@
 	 * ###SITENAME###           The name of the site.
 	 * ###SITEURL###            The URL to the site.
 	 *
+	 * @since 5.0.0
+	 *
 	 * @param string $email_text     Text in the email.
 	 * @param array  $email_data {
 	 *     Data relating to the account action email.
@@ -2919,7 +2930,7 @@
 	 *     @type string $siteurl     The site URL sending the mail.
 	 * }
 	 */
-	$content = apply_filters( 'confirm_account_action_email_content', $email_text, $email_data );
+	$content = apply_filters( 'account_verification_email_content', $email_text, $email_data );
 
 	$content = str_replace( '###DESCRIPTION###', $email_data['description'], $content );
 	$content = str_replace( '###CONFIRM_URL###', esc_url_raw( $email_data['confirm_url'] ), $content );
@@ -2928,7 +2939,7 @@
 	$content = str_replace( '###SITEURL###', esc_url_raw( $email_data['siteurl'] ), $content );
 
 	/* translators: %s Site name. */
-	return wp_mail( $email_data['email'], sprintf( __( '[%s] Confirm Account Action' ), wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ) ), $content );
+	return wp_mail( $email_data['email'], sprintf( __( '[%s] Confirm Action' ), wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ) ), $content );
 }
 
 /**
@@ -2936,12 +2947,12 @@
  *
  * @since 5.0.0
  *
- * @param string $action_name Name of the action this key is being generated for.
- * @param string $email       User email address. This can be the address of a registered or non-registered user.
- *
+ * @param string $email        User email address. This can be the address of a registered or non-registered user.
+ * @param string $action_name  Name of the action this key is being generated for.
+ * @param array  $request_data Misc data you want to send with the verification request and pass to the actions once the request is confirmed.
  * @return string|WP_Error Confirmation key on success. WP_Error on error.
  */
-function get_confirm_account_action_key( $action_name, $email ) {
+function wp_get_account_verification_key( $email, $action_name, $request_data = array() ) {
 	global $wp_hasher;
 
 	if ( ! is_email( $email ) ) {
@@ -2967,15 +2978,22 @@
 	}
 
 	$hashed_key = $wp_hasher->HashPassword( $key );
+	$value      = array(
+		'time'         => time(),
+		'hash'         => $hashed_key,
+		'email'        => $email,
+		'request_data' => $request_data,
+	);
 
 	if ( $is_registered_user ) {
-		$key_saved = (bool) update_user_meta( $user->ID, '_account_action_' . $action_name, implode( ':', array( time(), $hashed_key ) ) );
+		$key_saved = (bool) update_user_meta( $user->ID, '_verify_' . $action_name, wp_json_encode( $value ) );
 	} else {
-		$key_saved = (bool) update_site_option( '_account_action_' . hash( 'sha256', $email ) . '_' . $action_name, implode( ':', array( time(), $hashed_key, $email ) ) );
+		$uid       = function_exists( 'hash' ) ? hash( 'sha256', $email ) : sha1( $email );
+		$key_saved = (bool) update_site_option( '_verify_' . $action_name . '_' . $uid, wp_json_encode( $value ) );
 	}
 
 	if ( false === $key_saved ) {
-		return new WP_Error( 'no_confirm_account_action_key_update', __( 'Could not save confirm account action key to database.' ) );
+		return new WP_Error( 'no_account_verification_key_update', __( 'Could not save confirm account action key to database.' ) );
 	}
 
 	return $key;
@@ -2986,81 +3004,95 @@
  *
  * @since 5.0.0
  *
- * @param string $action_name Name of the action this key is being generated for.
  * @param string $key         Key to confirm.
  * @param string $uid         Email hash or user ID.
- *
+ * @param string $action_name Name of the action this key is being generated for.
  * @return array|WP_Error WP_Error on failure, action name and user email address on success.
  */
-function check_confirm_account_action_key( $action_name, $key, $uid ) {
+function wp_check_account_verification_key( $key, $uid, $action_name ) {
 	global $wp_hasher;
 
-	if ( ! empty( $action_name ) && ! empty( $key ) && ! empty( $uid ) ) {
-		$user = false;
+	if ( empty( $action_name ) || empty( $key ) || empty( $uid ) ) {
+		return new WP_Error( 'invalid_key', __( 'Invalid key' ) );
+	}
 
-		if ( is_numeric( $uid ) ) {
-			$user = get_user_by( 'id', absint( $uid ) );
-		}
+	$user = false;
 
-		// We could be dealing with a registered user account, or a visitor.
-		$is_registered_user = $user && ! is_wp_error( $user );
-		$key_request_time = '';
-		$saved_key        = '';
-		$email            = '';
+	if ( is_numeric( $uid ) ) {
+		$user = get_user_by( 'id', absint( $uid ) );
+	}
 
-		if ( empty( $wp_hasher ) ) {
-			require_once ABSPATH . WPINC . '/class-phpass.php';
-			$wp_hasher = new PasswordHash( 8, true );
-		}
+	// We could be dealing with a registered user account, or a visitor.
+	$is_registered_user = ( $user && ! is_wp_error( $user ) );
+	$key_request_time   = '';
+	$saved_key          = '';
+	$email              = '';
 
-		// Get the saved key from the database.
-		if ( $is_registered_user ) {
-			$confirm_action_data = get_user_meta( $user->ID, '_account_action_' . $action_name, true );
-			$email               = $user->user_email;
+	if ( empty( $wp_hasher ) ) {
+		require_once ABSPATH . WPINC . '/class-phpass.php';
+		$wp_hasher = new PasswordHash( 8, true );
+	}
 
-			if ( false !== strpos( $confirm_action_data, ':' ) ) {
-				list( $key_request_time, $saved_key ) = explode( ':', $confirm_action_data, 2 );
-			}
-		} else {
-			$confirm_action_data = get_site_option( '_account_action_' . $uid . '_' . $action_name, '' );
+	// Get the saved key from the database.
+	if ( $is_registered_user ) {
+		$raw_data = get_user_meta( $user->ID, '_verify_' . $action_name, true );
+		$email               = $user->user_email;
 
-			if ( false !== strpos( $confirm_action_data, ':' ) ) {
-				list( $key_request_time, $saved_key, $email ) = explode( ':', $confirm_action_data, 3 );
-			}
+		if ( false !== strpos( $confirm_action_data, ':' ) ) {
+			list( $key_request_time, $saved_key ) = explode( ':', $confirm_action_data, 2 );
 		}
+	} else {
+		$raw_data = get_site_option( '_verify_' . $action_name . '_' . $uid, '' );
 
-		if ( ! $saved_key ) {
-			return new WP_Error( 'invalid_key', __( 'Invalid key' ) );
+		if ( false !== strpos( $confirm_action_data, ':' ) ) {
+			list( $key_request_time, $saved_key, $email ) = explode( ':', $confirm_action_data, 3 );
 		}
+	}
 
-		/**
-		 * Filters the expiration time of confirm keys.
-		 *
-		 * @param int $expiration The expiration time in seconds.
-		 */
-		$expiration_duration = apply_filters( 'account_action_expiration', DAY_IN_SECONDS );
-		$expiration_time     = $key_request_time + $expiration_duration;
+	$data             = json_decode( $raw_data, true );
+	$key_request_time = (int) isset( $data['time'] ) ? $data['time'] : 0;
+	$saved_key        = isset( $data['hash'] ) ? $data['hash'] : '';
+	$email            = sanitize_email( isset( $data['email'] ) ? $data['email'] : '' );
+	$request_data     = isset( $data['request_data'] ) ? $data['request_data'] : array();
 
-		if ( $wp_hasher->CheckPassword( $key, $saved_key ) ) {
-			if ( $expiration_time && time() < $expiration_time ) {
-				$return = array(
-					'action' => $action_name,
-					'email'  => $email,
-				);
-			} else {
-				$return = new WP_Error( 'expired_key', __( 'The confirmation email has expired.' ) );
-			}
+	if ( ! $saved_key ) {
+		return new WP_Error( 'invalid_key', __( 'Invalid key' ) );
+	}
 
-			// Clean up stored keys.
-			if ( $is_registered_user ) {
-				delete_user_meta( $user->ID, '_account_action_' . $action_name );
-			} else {
-				delete_site_option( '_account_action_' . $uid . '_' . $action_name );
-			}
+	if ( ! $key_request_time || ! $email ) {
+		return new WP_Error( 'invalid_key', __( 'Invalid action' ) );
+	}
 
-			return $return;
-		}
+	/**
+	 * Filters the expiration time of confirm keys.
+	 *
+	 * @since 5.0.0
+	 *
+	 * @param int $expiration The expiration time in seconds.
+	 */
+	$expiration_duration = apply_filters( 'account_verification_expiration', DAY_IN_SECONDS );
+	$expiration_time     = $key_request_time + $expiration_duration;
+
+	if ( ! $wp_hasher->CheckPassword( $key, $saved_key ) ) {
+		return new WP_Error( 'invalid_key', __( 'Invalid key' ) );
 	}
 
-	return new WP_Error( 'invalid_key', __( 'Invalid key' ) );
+	if ( $expiration_time && time() < $expiration_time ) {
+		$return = array(
+			'action'       => $action_name,
+			'email'        => $email,
+			'request_data' => $request_data,
+		);
+	} else {
+		$return = new WP_Error( 'expired_key', __( 'The confirmation email has expired.' ) );
+	}
+
+	// Clean up stored keys.
+	if ( $is_registered_user ) {
+		delete_user_meta( $user->ID, '_verify_' . $action_name );
+	} else {
+		delete_site_option( '_verify_' . $action_name . '_' . $uid );
+	}
+
+	return $return;
 }
Index: src/wp-login.php
===================================================================
--- src/wp-login.php	(revision 42873)
+++ src/wp-login.php	(working copy)
@@ -427,7 +427,7 @@
 }
 
 // validate action so as to default to the login screen
-if ( ! in_array( $action, array( 'postpass', 'logout', 'lostpassword', 'retrievepassword', 'resetpass', 'rp', 'register', 'login', 'emailconfirm' ), true ) && false === has_filter( 'login_form_' . $action ) ) {
+if ( ! in_array( $action, array( 'postpass', 'logout', 'lostpassword', 'retrievepassword', 'resetpass', 'rp', 'register', 'login', 'verifyaccount' ), true ) && false === has_filter( 'login_form_' . $action ) ) {
 	$action = 'login';
 }
 
@@ -858,12 +858,12 @@
 
 		break;
 
-	case 'emailconfirm' :
+	case 'verifyaccount' :
 		if ( isset( $_GET['confirm_action'], $_GET['confirm_key'], $_GET['uid'] ) ) {
-			$action_name = sanitize_key( wp_unslash( $_GET['confirm_action'] ) );
 			$key         = sanitize_text_field( wp_unslash( $_GET['confirm_key'] ) );
 			$uid         = sanitize_text_field( wp_unslash( $_GET['uid'] ) );
-			$result      = check_confirm_account_action_key( $action_name, $key, $uid );
+			$action_name = sanitize_key( wp_unslash( $_GET['confirm_action'] ) );
+			$result      = wp_check_account_verification_key( $key, $uid, $action_name );
 		} else {
 			$result = new WP_Error( 'invalid_key', __( 'Invalid key' ) );
 		}
@@ -874,6 +874,8 @@
 			 *
 			 * After running this action hook the page will die.
 			 *
+			 * @since 5.0.0
+			 *
 			 * @param WP_Error $result Error object.
 			 */
 			do_action( 'account_action_failed', $result );
@@ -890,6 +892,8 @@
 		 * After firing this action hook the page will redirect to wp-login a callback
 		 * redirects or exits first.
 		 *
+		 * @since 5.0.0
+		 *
 		 * @param array $result {
 		 *     Data about the action which was confirmed.
 		 *
