diff --git a/wp-includes/default-filters.php b/wp-includes/default-filters.php
index dc0c8f8..9d77240 100644
--- a/wp-includes/default-filters.php
+++ b/wp-includes/default-filters.php
@@ -302,6 +302,7 @@ add_filter( 'heartbeat_nopriv_send', 'wp_auth_check' );
 
 // Default authentication filters
 add_filter( 'authenticate', 'wp_authenticate_username_password',  20, 3 );
+add_filter( 'authenticate', 'wp_authenticate_email_password',     20, 3 );
 add_filter( 'authenticate', 'wp_authenticate_spam_check',         99    );
 add_filter( 'determine_current_user', 'wp_validate_auth_cookie'          );
 add_filter( 'determine_current_user', 'wp_validate_logged_in_cookie', 20 );
diff --git a/wp-includes/pluggable.php b/wp-includes/pluggable.php
index 0c87138..3f88ee3 100644
--- a/wp-includes/pluggable.php
+++ b/wp-includes/pluggable.php
@@ -533,14 +533,13 @@ if ( !function_exists('wp_authenticate') ) :
  *
  * @since 2.5.0
  *
- * @param string $username User's username
- * @param string $password User's password
+ * @param string $user_identifier User's username or email address.
+ * @param string $password        User's password.
  * @return WP_User|WP_Error WP_User object if login successful, otherwise WP_Error object.
  */
-function wp_authenticate($username, $password) {
-	$username = sanitize_user($username);
+function wp_authenticate($user_identifier, $password) {
+	$user_identifier = sanitize_user($user_identifier);
 	$password = trim($password);
-
 	/**
 	 * Filter the user to authenticate.
 	 *
@@ -549,16 +548,16 @@ function wp_authenticate($username, $password) {
 	 *
 	 * @since 2.8.0
 	 *
-	 * @param null|WP_User $user     User to authenticate.
-	 * @param string       $username User login.
-	 * @param string       $password User password
+	 * @param null|WP_User $user            User to authenticate.
+	 * @param string       $user_identifier User login.
+	 * @param string       $password        User password.
 	 */
-	$user = apply_filters( 'authenticate', null, $username, $password );
+	$user = apply_filters( 'authenticate', null, $user_identifier, $password );
 
 	if ( $user == null ) {
 		// TODO what should the error message be? (Or would these even happen?)
 		// Only needed if all authentication handlers fail to return anything.
-		$user = new WP_Error('authentication_failed', __('<strong>ERROR</strong>: Invalid username or incorrect password.'));
+		$user = new WP_Error('authentication_failed', __('<strong>ERROR</strong>: Invalid username/email address or incorrect password.'));
 	}
 
 	$ignore_codes = array('empty_username', 'empty_password');
diff --git a/wp-includes/user.php b/wp-includes/user.php
index 17fe9fc..757d0f7 100644
--- a/wp-includes/user.php
+++ b/wp-includes/user.php
@@ -116,12 +116,17 @@ function wp_authenticate_username_password($user, $username, $password) {
 		return $user;
 	}
 
+	// Coallate errors found in previous authentication callbacks.
+	if ( is_wp_error( $user ) ) {
+		$error = $user;
+	} else {
+		$error = new WP_Error();
+	}
+
 	if ( empty($username) || empty($password) ) {
 		if ( is_wp_error( $user ) )
 			return $user;
 
-		$error = new WP_Error();
-
 		if ( empty($username) )
 			$error->add('empty_username', __('<strong>ERROR</strong>: The username field is empty.'));
 
@@ -133,8 +138,10 @@ function wp_authenticate_username_password($user, $username, $password) {
 
 	$user = get_user_by('login', $username);
 
-	if ( !$user )
-		return new WP_Error( 'invalid_username', sprintf( __( '<strong>ERROR</strong>: Invalid username. <a href="%s">Lost your password</a>?' ), wp_lostpassword_url() ) );
+	if ( !$user ) {
+		$error->add( 'invalid_username', sprintf( __( '<strong>ERROR</strong>: Invalid username. <a href="%s">Lost your password</a>?' ), wp_lostpassword_url() ) );
+		return $error;
+	}
 
 	/**
 	 * Filter whether the given user can be authenticated with the provided $password.
@@ -149,9 +156,82 @@ function wp_authenticate_username_password($user, $username, $password) {
 	if ( is_wp_error($user) )
 		return $user;
 
-	if ( !wp_check_password($password, $user->user_pass, $user->ID) )
-		return new WP_Error( 'incorrect_password', sprintf( __( '<strong>ERROR</strong>: The password you entered for the username <strong>%1$s</strong> is incorrect. <a href="%2$s">Lost your password</a>?' ),
+	if ( !wp_check_password($password, $user->user_pass, $user->ID) ) {
+		$error->add( 'incorrect_password', sprintf( __( '<strong>ERROR</strong>: The password you entered for the username <strong>%1$s</strong> is incorrect. <a href="%2$s">Lost your password</a>?' ),
 		$username, wp_lostpassword_url() ) );
+		return $error;
+	}
+
+	return $user;
+}
+
+/**
+ * Authenticate the user using the email and password.
+ *
+ * @since 4.2.0
+ *
+ * @param WP_User|WP_Error|null $user     WP_User or WP_Error object from a previous callback. Default null.
+ * @param string                $username User email for authentication.
+ * @param string                $password Password for authentication.
+ * @return WP_User|WP_Error WP_User on success, WP_Error on failure.
+ */
+function wp_authenticate_email_password( $user, $email, $password ) {
+	if ( is_a( $user, 'WP_User' ) ) {
+		return $user;
+	}
+
+	if ( ! is_email( $email ) ) {
+		return $user;
+	}
+
+	// Coallate errors found in previous authentication callbacks.
+	if ( is_wp_error( $user ) ) {
+		$error = $user;
+	} else {
+		$error = new WP_Error();
+	}
+
+	if ( empty($email) || empty($password) ) {
+		if ( is_wp_error( $user ) ) {
+			return $user;
+		}
+
+		if ( empty($email) ) {
+			$error->add('empty_email', __('<strong>ERROR</strong>: The email field is empty.'));
+		}
+
+		if ( empty($password) ) {
+			$error->add('empty_password', __('<strong>ERROR</strong>: The password field is empty.'));
+		}
+
+		return $error;
+	}
+
+	$user = get_user_by( 'email', $email );
+
+	if ( ! $user ) {
+		$error->add( 'invalid_email', __( '<strong>ERROR</strong>: Invalid email address.' ) );
+		return $error;
+	}
+
+	/**
+	 * Filter whether the given user can be authenticated with the provided $password.
+	 *
+	 * @since 2.5.0
+	 *
+	 * @param WP_User|WP_Error $user     WP_User or WP_Error object if a previous
+	 *                                   callback failed authentication.
+	 * @param string           $password Password to check against the user.
+	 */
+	$user = apply_filters( 'wp_authenticate_user', $user, $password );
+	if ( is_wp_error($user) ) {
+		return $user;
+	}
+
+	if ( !wp_check_password($password, $user->user_pass, $user->ID) ) {
+		return new WP_Error( 'incorrect_password', sprintf( __( '<strong>ERROR</strong>: The password you entered for the email <strong>%1$s</strong> is incorrect. <a href="%2$s">Lost your password</a>?' ),
+		$email, wp_lostpassword_url() ) );
+	}
 
 	return $user;
 }
diff --git a/wp-login.php b/wp-login.php
index 8290f1a..01ce41f 100644
--- a/wp-login.php
+++ b/wp-login.php
@@ -874,7 +874,7 @@ default:
 
 <form name="loginform" id="loginform" action="<?php echo esc_url( site_url( 'wp-login.php', 'login_post' ) ); ?>" method="post">
 	<p>
-		<label for="user_login"><?php _e('Username') ?><br />
+		<label for="user_login"><?php _e('Username or email') ?><br />
 		<input type="text" name="log" id="user_login" class="input" value="<?php echo esc_attr($user_login); ?>" size="20" /></label>
 	</p>
 	<p>
