diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php
index e0b6858..ade511e 100644
--- a/core/modules/simpletest/src/WebTestBase.php
+++ b/core/modules/simpletest/src/WebTestBase.php
@@ -682,16 +682,18 @@ protected function checkPermissions(array $permissions) {
    *
    * @param \Drupal\Core\Session\AccountInterface $account
    *   User object representing the user to log in.
+   * @param bool $by_email
+   *   Whether to use email for login instead of username.
    *
    * @see drupalCreateUser()
    */
-  protected function drupalLogin(AccountInterface $account) {
+  protected function drupalLogin(AccountInterface $account, $by_email = FALSE) {
     if ($this->loggedInUser) {
       $this->drupalLogout();
     }
 
     $edit = array(
-      'name' => $account->getUsername(),
+      'name' => $by_email ? $account->getEmail() : $account->getUsername(),
       'pass' => $account->pass_raw
     );
     $this->drupalPostForm('user', $edit, t('Log in'));
diff --git a/core/modules/user/config/install/user.settings.yml b/core/modules/user/config/install/user.settings.yml
index fbb0d98..11d7958 100644
--- a/core/modules/user/config/install/user.settings.yml
+++ b/core/modules/user/config/install/user.settings.yml
@@ -1,5 +1,6 @@
 admin_role: ''
 anonymous: Anonymous
+user_login_method: username_only
 verify_mail: true
 notify:
   cancel_confirm: true
diff --git a/core/modules/user/config/schema/user.schema.yml b/core/modules/user/config/schema/user.schema.yml
index 5419c55..59c2a97 100644
--- a/core/modules/user/config/schema/user.schema.yml
+++ b/core/modules/user/config/schema/user.schema.yml
@@ -10,6 +10,9 @@ user.settings:
     anonymous:
       type: label
       label: 'Name'
+    user_login_method:
+      type: string
+      label: 'Login credentials'
     verify_mail:
       type: boolean
       label: 'Require email verification when a visitor creates an account.'
diff --git a/core/modules/user/src/AccountSettingsForm.php b/core/modules/user/src/AccountSettingsForm.php
index 06e1a09..da075d9 100644
--- a/core/modules/user/src/AccountSettingsForm.php
+++ b/core/modules/user/src/AccountSettingsForm.php
@@ -110,6 +110,23 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       $form['language'] += content_translation_enable_widget('user', 'user', $form, $form_state);
     }
 
+  // User login settings.
+  $form['user_login'] = array(
+     '#type' => 'details',
+    '#title' => t('User login'),
+   );
+    $form['user_login']['user_login_method'] = array(
+      '#type' => 'radios',
+      '#title' => t('Login credentials'),
+      '#description' => t('The details users may use to identify themselves.'),
+      '#options' => array(
+        USER_LOGIN_USERNAME_ONLY => t('Username'),
+        USER_LOGIN_EMAIL_ONLY => t('E-mail address'),
+        USER_LOGIN_USERNAME_OR_EMAIL => t('Username or e-mail address'),
+      ),
+      '#default_value' => $config->get('user_login_method'),
+    );
+
     // User registration settings.
     $form['registration_cancellation'] = array(
       '#type' => 'details',
@@ -415,6 +432,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     $this->config('user.settings')
       ->set('anonymous', $form_state['values']['anonymous'])
       ->set('admin_role', $form_state['values']['user_admin_role'])
+      ->set('user_login_method', $form_state['values']['user_login_method'])
       ->set('register', $form_state['values']['user_register'])
       ->set('password_strength', $form_state['values']['user_password_strength'])
       ->set('verify_mail', $form_state['values']['user_email_verification'])
diff --git a/core/modules/user/src/Form/UserLoginForm.php b/core/modules/user/src/Form/UserLoginForm.php
index 8f5f586..3b407ca 100644
--- a/core/modules/user/src/Form/UserLoginForm.php
+++ b/core/modules/user/src/Form/UserLoginForm.php
@@ -78,12 +78,13 @@ public function getFormId() {
    * {@inheritdoc}
    */
   public function buildForm(array $form, FormStateInterface $form_state) {
+    $credentials = $this->config('user.settings')->get('user_login_method');
     // Display login form:
     $form['name'] = array(
       '#type' => 'textfield',
-      '#title' => $this->t('Username'),
+      '#title' => $credentials == USER_LOGIN_USERNAME_ONLY ? $this->t('Username') : ($credentials == USER_LOGIN_EMAIL_ONLY ? $this->t('E-mail address') : $this->t('Username or e-mail address')),
       '#size' => 60,
-      '#maxlength' => USERNAME_MAX_LENGTH,
+      '#maxlength' => $credentials == USER_LOGIN_USERNAME_ONLY ? USERNAME_MAX_LENGTH : EMAIL_MAX_LENGTH,
       '#description' => $this->t('Enter your @s username.', array('@s' => $this->config('system.site')->get('name'))),
       '#required' => TRUE,
       '#attributes' => array(
@@ -133,12 +134,26 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
   }
 
   /**
-   * Sets an error if supplied username has been blocked.
+   * Depending on user_login_method, delegates to valid_email_address(). Sets an
+   * error if username is blocked.
    */
   public function validateName(array &$form, FormStateInterface $form_state) {
-    if (!empty($form_state['values']['name']) && user_is_blocked($form_state['values']['name'])) {
-      // Blocked in user administration.
-      $form_state->setErrorByName('name', $this->t('The username %name has not been activated or is blocked.', array('%name' => $form_state['values']['name'])));
+    if (isset($form_state['values']['name'])) {
+      $name = $form_state['values']['name'];
+      $credentials = $this->config('user.settings')->get('user_login_method');
+      if ($credentials != USER_LOGIN_USERNAME_ONLY) {
+        if ($accounts = $this->userStorage->loadByProperties(array('mail' => $name))) {
+          $name = reset($accounts)->getUsername();
+        }
+        // If can't find account, check if valid email address.
+        elseif ($credentials == USER_LOGIN_EMAIL_ONLY && !valid_email_address($name)) {
+          $form_state->setErrorByName('name', $this->t('The e-mail address %email is not valid.', array('%email' => $form_state['values']['name'])));
+        }
+      }
+      // If can find account, check if user is blocked.
+      if ($name && user_is_blocked($name)) {
+        $form_state->setErrorByName('name', $this->t('The username %name has not been activated or is blocked.', array('%name' => $form_state['values']['name'])));
+      }
     }
   }
 
@@ -160,8 +175,18 @@ public function validateAuthentication(array &$form, FormStateInterface $form_st
         $form_state['flood_control_triggered'] = 'ip';
         return;
       }
-      $accounts = $this->userStorage->loadByProperties(array('name' => $form_state['values']['name'], 'status' => 1));
-      $account = reset($accounts);
+
+      $account = FALSE;
+      $credentials = $this->config('user.settings')->get('user_login_method');
+      if ($credentials != USER_LOGIN_USERNAME_ONLY && valid_email_address($form_state['values']['name'])) {
+        $accounts = $this->userStorage->loadByProperties(array('mail' => $form_state['values']['name'], 'status' => 1));
+        $account = reset($accounts);
+      }
+      if (!$account && $credentials != USER_LOGIN_EMAIL_ONLY) {
+        $accounts = $this->userStorage->loadByProperties(array('name' => $form_state['values']['name'], 'status' => 1));
+        $account = reset($accounts);
+      }
+
       if ($account) {
         if ($flood_config->get('uid_only')) {
           // Register flood events based on the uid only, so they apply for any
@@ -182,10 +207,14 @@ public function validateAuthentication(array &$form, FormStateInterface $form_st
           $form_state['flood_control_triggered'] = 'user';
           return;
         }
+
+        // We are not limited by flood control, so try to authenticate.
+        // Set $form_state['uid'] as a flag for self::validateFinal().
+        $form_state['uid'] = $this->userAuth->authenticate($account->getUsername(), $password);
+      }
+      else {
+        $form_state['uid'] = FALSE;
       }
-      // We are not limited by flood control, so try to authenticate.
-      // Set $form_state['uid'] as a flag for self::validateFinal().
-      $form_state['uid'] = $this->userAuth->authenticate($form_state['values']['name'], $password);
     }
   }
 
@@ -214,7 +243,19 @@ public function validateFinal(array &$form, FormStateInterface $form_state) {
         }
       }
       else {
-        $form_state->setErrorByName('name', $this->t('Sorry, unrecognized username or password. <a href="@password">Have you forgotten your password?</a>', array('@password' => url('user/password', array('query' => array('name' => $form_state['values']['name']))))));
+        switch ($this->config('user.settings')->get('user_login_method')) {
+          case USER_LOGIN_USERNAME_ONLY:
+            $form_state->setErrorByName('name', $this->t('Sorry, unrecognized username or password. <a href="@password">Have you forgotten your password?</a>', array('@password' => url('user/password', array('query' => array('name' => $form_state['values']['name']))))));
+            break;
+
+          case USER_LOGIN_EMAIL_ONLY:
+            $form_state->setErrorByName('name', $this->t('Sorry, unrecognized e-mail address or password. <a href="@password">Have you forgotten your password?</a>', array('@password' => url('user/password', array('query' => array('name' => $form_state['values']['name']))))));
+            break;
+
+          case USER_LOGIN_USERNAME_OR_EMAIL:
+            $form_state->setErrorByName('name', $this->t('Sorry, unrecognized username, e-mail address or password. <a href="@password">Have you forgotten your password?</a>', array('@password' => url('user/password', array('query' => array('name' => $form_state['values']['name']))))));
+            break;
+        }
         $accounts = $this->userStorage->loadByProperties(array('name' => $form_state['values']['name']));
         if (!empty($accounts)) {
           $this->logger('user')->notice('Login attempt failed for %user.', array('%user' => $form_state['values']['name']));
diff --git a/core/modules/user/src/Tests/UserLoginTest.php b/core/modules/user/src/Tests/UserLoginTest.php
index 15fd7ce..0b61a08 100644
--- a/core/modules/user/src/Tests/UserLoginTest.php
+++ b/core/modules/user/src/Tests/UserLoginTest.php
@@ -9,6 +9,7 @@
 
 use Drupal\simpletest\WebTestBase;
 use Drupal\Core\Password\PhpassHashedPassword;
+use Drupal\user\Entity\User;
 
 /**
  * Ensure that login works as expected.
@@ -29,6 +30,37 @@ function testLoginDestination() {
   }
 
   /**
+   * Test that login credentials work with each method.
+   */
+  function testLoginByEmail() {
+    $account = $this->drupalCreateUser(array());
+
+    // Login test with username only, the default method.
+    // Using e-mail should fail with username only method.
+    $this->drupalLogin($account);
+    // Using username should pass.
+    $this->assertFailedLogin($account, NULL, TRUE);
+
+    // Login test with e-mail only method.
+    \Drupal::config('user.settings')
+      ->set('user_login_method', USER_LOGIN_EMAIL_ONLY)
+      ->save();
+    // Using e-mail shoud pass.
+    $this->drupalLogin($account, TRUE);
+    // Using username should fail.
+    $this->assertFailedLogin($account);
+
+    // Login test with username or e-mail method.
+    \Drupal::config('user.settings')
+      ->set('user_login_method', USER_LOGIN_USERNAME_OR_EMAIL)
+      ->save();;
+    // Using e-mail should pass.
+    $this->drupalLogin($account, TRUE);
+    // Using username should pass.
+    $this->drupalLogin($account);
+  }
+
+  /**
    * Test the global login flood control.
    */
   function testGlobalLoginFloodControl() {
@@ -146,10 +178,15 @@ function testPasswordRehashOnLogin() {
    * @param $flood_trigger
    *   Whether or not to expect that the flood control mechanism will be
    *   triggered.
+   * @param bool $by_email
+   *   Authenticate with email instead of username.
    */
-  function assertFailedLogin($account, $flood_trigger = NULL) {
+  function assertFailedLogin(User $account, $flood_trigger = NULL, $by_email = FALSE) {
+    if ($this->loggedInUser) {
+      $this->drupalLogout();
+    }
     $edit = array(
-      'name' => $account->getUsername(),
+      'name' => $by_email ? $account->getEmail() : $account->getUsername(),
       'pass' => $account->pass_raw,
     );
     $this->drupalPostForm('user', $edit, t('Log in'));
@@ -164,7 +201,24 @@ function assertFailedLogin($account, $flood_trigger = NULL) {
       }
     }
     else {
-      $this->assertText(t('Sorry, unrecognized username or password. Have you forgotten your password?'));
+      switch (\Drupal::config('user.settings')->get('user_login_method')) {
+        case USER_LOGIN_USERNAME_ONLY:
+          $this->assertText(t('Sorry, unrecognized username or password. Have you forgotten your password?'));
+          break;
+
+        case USER_LOGIN_EMAIL_ONLY:
+          if (!$by_email) {
+            $this->assertText(t('The e-mail address @email is not valid.', array('@email' => $account->getUsername())));
+          }
+          else {
+            $this->assertText(t('Sorry, unrecognized e-mail address or password. Have you forgotten your password?'));
+          }
+          break;
+
+        case USER_LOGIN_USERNAME_OR_EMAIL:
+          $this->assertText(t('Sorry, unrecognized username, e-mail address or password. Have you forgotten your password?'));
+          break;
+      }
     }
   }
 }
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 2254e16..d0320a5 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -49,6 +49,23 @@
 const USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL = 'visitors_admin_approval';
 
 /**
+ * Users can login with username only.
+ */
+const USER_LOGIN_USERNAME_ONLY = 'username_only';
+
+/**
+ * Users can login with e-mail address only.
+ */
+const USER_LOGIN_EMAIL_ONLY = 'email_only';
+
+/**
+ * Users can login using either username or e-mail address.
+ */
+const USER_LOGIN_USERNAME_OR_EMAIL = 'username_or_email';
+
+/**
+
+/**
  * Implement hook_help().
  */
 function user_help($route_name, RouteMatchInterface $route_match) {
