diff --git a/core/core.libraries.yml b/core/core.libraries.yml
index 0ff5fb09ec..1fc80a1f25 100644
--- a/core/core.libraries.yml
+++ b/core/core.libraries.yml
@@ -260,6 +260,15 @@ drupal.progress:
     - core/jquery
     - core/drupalSettings
 
+drupal.password-unmask:
+  version: VERSION
+  js:
+    misc/password-unmask.js: {}
+  dependencies:
+    - core/drupal
+    - core/jquery
+    - core/jquery.once
+
 drupal.states:
   version: VERSION
   js:
diff --git a/core/lib/Drupal/Core/Render/Element/PasswordConfirm.php b/core/lib/Drupal/Core/Render/Element/PasswordConfirm.php
index 07c31490f4..e31bc84b6b 100644
--- a/core/lib/Drupal/Core/Render/Element/PasswordConfirm.php
+++ b/core/lib/Drupal/Core/Render/Element/PasswordConfirm.php
@@ -81,7 +81,10 @@ public static function processPasswordConfirm(&$element, FormStateInterface $for
       '#title' => t('Confirm password'),
       '#value' => empty($element['#value']) ? NULL : $element['#value']['pass2'],
       '#required' => $element['#required'],
-      '#attributes' => ['class' => ['password-confirm', 'js-password-confirm']],
+      '#attributes' => [
+        'class' => ['password-confirm', 'js-password-confirm'],
+        'data-drupal-password-strength' => TRUE,
+      ],
       '#error_no_message' => TRUE,
     ];
     $element['#element_validate'] = [[get_called_class(), 'validatePasswordConfirm']];
diff --git a/core/lib/Drupal/Core/Render/Element/PasswordUnmask.php b/core/lib/Drupal/Core/Render/Element/PasswordUnmask.php
new file mode 100644
index 0000000000..dd11d8e6da
--- /dev/null
+++ b/core/lib/Drupal/Core/Render/Element/PasswordUnmask.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace Drupal\Core\Render\Element;
+
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Provides a form element for entering a password.
+ *
+ * Password form element will mask user input which can be unmasked by the user.
+ *
+ * Usage example:
+ * @code
+ * $form['pass'] = array(
+ *   '#type' => 'password_unmask',
+ *   '#title' => t('Password'),
+ *   '#size' => 25,
+ * );
+ * @endcode
+ *
+ * @see \Drupal\Core\Render\Element\Password
+ *
+ * @FormElement("password_unmask")
+ */
+class PasswordUnmask extends Password {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getInfo() {
+    $info = parent::getInfo();
+    $info['#process'][] = [static::class, 'processUnmask'];
+
+    return $info;
+  }
+
+  /**
+   * Process callback for #type password_unmask elements.
+   *
+   * Adds the attributes needed for the password strength bar and the password
+   * unmask button.
+   *
+   * @param array $element
+   *   An associative array containing the properties and children of the
+   *   generic input element.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   * @param array $complete_form
+   *   The complete form structure.
+   *
+   * @return array
+   *   The processed element.
+   */
+  public static function processUnmask(array &$element, FormStateInterface $form_state, array &$complete_form) {
+    $element['#attached']['library'][] = 'core/drupal.password-unmask';
+    $element['#attributes']['class'][] = 'password-field';
+    $element['#attributes']['class'][] = 'js-password-field';
+    $element['#attributes']['data-drupal-password-unmask'] = TRUE;
+    $element['#attributes']['data-drupal-password-strength'] = TRUE;
+
+    return $element;
+  }
+
+}
diff --git a/core/misc/password-unmask.es6.js b/core/misc/password-unmask.es6.js
new file mode 100644
index 0000000000..0cfefa086f
--- /dev/null
+++ b/core/misc/password-unmask.es6.js
@@ -0,0 +1,59 @@
+/**
+ * @file
+ * Creates a reveal password link on password inputs.
+ */
+
+(function($, Drupal) {
+  const hidePass = Drupal.t('Hide password');
+  const showPass = Drupal.t('Show password');
+
+  /**
+   * Handle password unmask button click to toggle unmasking password.
+   *
+   * @param {jQuery.Event} event
+   *   The jQuery event.
+   */
+  const unmaskClickHandler = event => {
+    const $pass = $(event.data.password);
+    const $button = $(event.target);
+
+    if ($pass.attr('type') === 'password') {
+      $pass.attr('type', 'text');
+      $button.text(hidePass);
+    } else {
+      $pass.attr('type', 'password');
+      $button.text(showPass);
+    }
+  };
+
+  /**
+   * Create password unmask button and bind event.
+   *
+   * @param {number} index
+   *   The index in the loop, as provided by `jQuery.each`
+   * @param {HTMLElement} element
+   *   The password input form element.
+   */
+  const unmaskButton = (index, element) => {
+    const $trigger = $(
+      `<button type="button" class="link toggle-password">${showPass}</button>`,
+    );
+
+    $trigger.on('click', { password: element }, unmaskClickHandler);
+    $trigger.insertAfter(element);
+  };
+
+  /**
+   * Initialize password unmask button.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.passwordUnmask = {
+    attach(context) {
+      $(context)
+        .find('input[type=password][data-drupal-password-unmask]')
+        .once('password-unmask')
+        .each(unmaskButton);
+    },
+  };
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/misc/password-unmask.js b/core/misc/password-unmask.js
new file mode 100644
index 0000000000..57167afa88
--- /dev/null
+++ b/core/misc/password-unmask.js
@@ -0,0 +1,61 @@
+/**
+ * @file
+ * Creates a reveal password link on password inputs.
+ */
+
+(function ($, Drupal) {
+
+  'use strict';
+
+  var showPass = Drupal.t('Show password');
+  var hidePass = Drupal.t('Hide password');
+
+  /**
+   * Handle password unmask button click to toggle unmasking password.
+   *
+   * @param {jQuery.Event} event
+   *   The jQuery event.
+   */
+  function unmaskClickHandler(event) {
+    var $pass = $(event.data.password);
+    var $button = $(event.target);
+
+    if ($pass.attr('type') === 'password') {
+      $pass.attr('type', 'text');
+      $button.text(hidePass);
+    }
+    else {
+      $pass.attr('type', 'password');
+      $button.text(showPass);
+    }
+  }
+
+  /**
+   * Create password unmask button and bind event.
+   *
+   * @param {number} index
+   *   The index in the loop, as provided by `jQuery.each`
+   * @param {HTMLElement} element
+   *   The password input form element.
+   */
+  function unmaskButton(index, element) {
+    var $trigger = $('<button type="button" class="link toggle-password">' + showPass + '</button>');
+
+    $trigger.on('click', {password: element}, unmaskClickHandler);
+    $trigger.insertAfter(element);
+  }
+
+  /**
+   * Initialize reveal links.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.passwordUnmask = {
+    attach: function (context) {
+      $(context).find('input[type=password][data-drupal-password-unmask]')
+        .once('password-unmask')
+        .each(unmaskButton);
+    }
+  };
+
+})(jQuery, Drupal);
diff --git a/core/modules/system/tests/modules/form_test/form_test.routing.yml b/core/modules/system/tests/modules/form_test/form_test.routing.yml
index ce4dfa98fb..0142e05db5 100644
--- a/core/modules/system/tests/modules/form_test/form_test.routing.yml
+++ b/core/modules/system/tests/modules/form_test/form_test.routing.yml
@@ -520,3 +520,10 @@ form_test.javascript_states_form:
     _form: '\Drupal\form_test\Form\JavascriptStatesForm'
   requirements:
     _access: 'TRUE'
+
+form_test.password_unmask:
+  path: '/form-test/password-reveal'
+  defaults:
+    _form: '\Drupal\form_test\Form\FormTestPasswordUnmask'
+  requirements:
+    _access: 'TRUE'
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestPasswordUnmask.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestPasswordUnmask.php
new file mode 100644
index 0000000000..959e2d9c6d
--- /dev/null
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestPasswordUnmask.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace Drupal\form_test\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Form constructor for testing #type 'password_unmask' elements.
+ */
+class FormTestPasswordUnmask extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'form_test_password_unmask';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $form['password_one'] = [
+      '#title' => $this->t('Password One'),
+      '#type' => 'password_unmask',
+      '#size' => 25,
+    ];
+    $form['password_two'] = [
+      '#title' => $this->t('Password Two'),
+      '#type' => 'password_unmask',
+      '#size' => 25,
+    ];
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+  }
+
+}
diff --git a/core/modules/user/config/install/user.settings.yml b/core/modules/user/config/install/user.settings.yml
index 8372ccdb73..7e126093fe 100644
--- a/core/modules/user/config/install/user.settings.yml
+++ b/core/modules/user/config/install/user.settings.yml
@@ -13,4 +13,5 @@ register: visitors
 cancel_method: user_cancel_block
 password_reset_timeout: 86400
 password_strength: true
+password_type_reveal: false
 langcode: en
diff --git a/core/modules/user/config/schema/user.schema.yml b/core/modules/user/config/schema/user.schema.yml
index 2f9bda44f2..5c5ae432ad 100644
--- a/core/modules/user/config/schema/user.schema.yml
+++ b/core/modules/user/config/schema/user.schema.yml
@@ -50,6 +50,9 @@ user.settings:
     password_strength:
       type: boolean
       label: 'Enable password strength indicator'
+    password_type_reveal:
+      type: boolean
+      label: 'Use password reveal on account form'
 
 user.mail:
  type: config_object
diff --git a/core/modules/user/src/AccountForm.php b/core/modules/user/src/AccountForm.php
index 9758d5ec64..d91e2b84dc 100644
--- a/core/modules/user/src/AccountForm.php
+++ b/core/modules/user/src/AccountForm.php
@@ -121,11 +121,21 @@ public function form(array $form, FormStateInterface $form_state) {
     // Display password field only for existing users or when user is allowed to
     // assign a password during registration.
     if (!$register) {
-      $form['account']['pass'] = [
-        '#type' => 'password_confirm',
-        '#size' => 25,
-        '#description' => $this->t('To change the current user password, enter the new password in both fields.'),
-      ];
+      if ($config->get('password_type_reveal')) {
+        $form['account']['pass'] = [
+          '#title' => $this->t('Password'),
+          '#type' => 'password_unmask',
+          '#size' => 25,
+          '#description' => $this->t('To change the current user password, enter the new password.'),
+        ];
+      }
+      else {
+        $form['account']['pass'] = [
+          '#type' => 'password_confirm',
+          '#size' => 25,
+          '#description' => $this->t('To change the current user password, enter the new password in both fields.'),
+        ];
+      }
 
       // To skip the current password field, the user must have logged in via a
       // one-time link and have the token in the URL. Store this in $form_state
@@ -163,12 +173,23 @@ public function form(array $form, FormStateInterface $form_state) {
       }
     }
     elseif (!$config->get('verify_mail') || $admin_create) {
-      $form['account']['pass'] = [
-        '#type' => 'password_confirm',
-        '#size' => 25,
-        '#description' => $this->t('Provide a password for the new account in both fields.'),
-        '#required' => TRUE,
-      ];
+      if ($config->get('password_type_reveal')) {
+        $form['account']['pass'] = [
+          '#title' => $this->t('Password'),
+          '#type' => 'password_unmask',
+          '#size' => 25,
+          '#description' => $this->t('Provide a password for the new account.'),
+          '#required' => TRUE,
+        ];
+      }
+      else {
+        $form['account']['pass'] = [
+          '#type' => 'password_confirm',
+          '#size' => 25,
+          '#description' => $this->t('Provide a password for the new account in both fields.'),
+          '#required' => TRUE,
+        ];
+      }
     }
 
     // When not building the user registration form, prevent web browsers from
diff --git a/core/modules/user/src/AccountSettingsForm.php b/core/modules/user/src/AccountSettingsForm.php
index 0ef0920cbf..b40ce8aaee 100644
--- a/core/modules/user/src/AccountSettingsForm.php
+++ b/core/modules/user/src/AccountSettingsForm.php
@@ -168,6 +168,12 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       '#title' => $this->t('Enable password strength indicator'),
       '#default_value' => $config->get('password_strength'),
     ];
+    $form['registration_cancellation']['user_password_type_reveal'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Enable show password button'),
+      '#description' => $this->t('New users will be able to unmask their password to reveal what they have typed. With this setting enabled, users will not be required to enter a password twice, in two separate fields, to confirm during registration.'),
+      '#default_value' => $config->get('password_type_reveal'),
+    ];
     $form['registration_cancellation']['user_cancel_method'] = [
       '#type' => 'radios',
       '#title' => $this->t('When cancelling a user account'),
@@ -432,6 +438,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
       ->set('anonymous', $form_state->getValue('anonymous'))
       ->set('register', $form_state->getValue('user_register'))
       ->set('password_strength', $form_state->getValue('user_password_strength'))
+      ->set('password_type_reveal', $form_state->getValue('user_password_type_reveal'))
       ->set('verify_mail', $form_state->getValue('user_email_verification'))
       ->set('cancel_method', $form_state->getValue('user_cancel_method'))
       ->set('notify.status_activated', $form_state->getValue('user_mail_status_activated_notify'))
diff --git a/core/modules/user/tests/src/Functional/UserEditTest.php b/core/modules/user/tests/src/Functional/UserEditTest.php
index 6a71927a96..c1b48e0bb9 100644
--- a/core/modules/user/tests/src/Functional/UserEditTest.php
+++ b/core/modules/user/tests/src/Functional/UserEditTest.php
@@ -120,6 +120,20 @@ public function testUserEdit() {
     $this->assertText(t('The changes have been saved.'));
     $this->assertNoFieldChecked('edit-status-0');
     $this->assertFieldChecked('edit-status-1');
+
+    // Test editing the user with a password_unmask field.
+    $config->set('password_type_reveal', TRUE)->save();
+    $config->set('password_strength', TRUE)->save();
+
+    $this->drupalGet("user/" . $admin_user->id() . "/edit");
+    $this->assertRaw(t('Password strength:'), 'The password strength indicator is displayed.');
+    $this->assertFieldByXPath('//input[@type="password"][@name="pass"]', NULL, 'The password field is displayed as masked.');
+
+    $edit = [];
+    $edit['pass'] = $this->randomMachineName();
+    $edit['current_pass'] = $admin_user->pass_raw;
+    $this->drupalPostForm("user/" . $admin_user->id() . "/edit", $edit, t('Save'));
+    $this->assertRaw(t("The changes have been saved."));
   }
 
   /**
diff --git a/core/modules/user/user.install b/core/modules/user/user.install
index 2540ac2cd3..2876831f61 100644
--- a/core/modules/user/user.install
+++ b/core/modules/user/user.install
@@ -103,3 +103,10 @@ function user_update_8100() {
     $config->set('status_blocked', $mail)->save(TRUE);
   }
 }
+
+/**
+ * Add the configuration setting for password_type_reveal.
+ */
+function user_update_8800() {
+  \Drupal::configFactory()->getEditable('user.settings')->set('password_type_reveal', FALSE)->save(TRUE);
+}
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 325d240903..538d566449 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -1286,6 +1286,9 @@ function user_element_info_alter(array &$types) {
   if (isset($types['password_confirm'])) {
     $types['password_confirm']['#process'][] = 'user_form_process_password_confirm';
   }
+  if (isset($types['password_unmask'])) {
+    $types['password_unmask']['#process'][] = 'user_form_process_password_unmask';
+  }
 }
 
 /**
@@ -1304,22 +1307,30 @@ function user_form_process_password_confirm($element) {
   ];
 
   if (\Drupal::config('user.settings')->get('password_strength')) {
-    $password_settings['showStrengthIndicator'] = TRUE;
-    $password_settings += [
-      'strengthTitle' => t('Password strength:'),
-      'hasWeaknesses' => t('Recommendations to make your password stronger:'),
-      'tooShort' => t('Make it at least 12 characters'),
-      'addLowerCase' => t('Add lowercase letters'),
-      'addUpperCase' => t('Add uppercase letters'),
-      'addNumbers' => t('Add numbers'),
-      'addPunctuation' => t('Add punctuation'),
-      'sameAsUsername' => t('Make it different from your username'),
-      'weak' => t('Weak'),
-      'fair' => t('Fair'),
-      'good' => t('Good'),
-      'strong' => t('Strong'),
-      'username' => \Drupal::currentUser()->getAccountName(),
-    ];
+    $password_settings = _user_password_strength_settings($password_settings);
+  }
+
+  $element['#attached']['library'][] = 'user/drupal.user';
+  $element['#attached']['drupalSettings']['password'] = $password_settings;
+
+  return $element;
+}
+
+/**
+ * Form element process handler for client-side password validation.
+ *
+ * This #process handler is automatically invoked for 'password_unmask' form
+ * elements to add the JavaScript to display and hide the password and string
+ * translations for dynamic password validation.
+ */
+function user_form_process_password_unmask($element) {
+  $password_settings = [
+    'confirmTitle' => '',
+    'showStrengthIndicator' => FALSE,
+  ];
+
+  if (\Drupal::config('user.settings')->get('password_strength')) {
+    $password_settings = _user_password_strength_settings($password_settings);
   }
 
   $element['#attached']['library'][] = 'user/drupal.user';
@@ -1328,6 +1339,36 @@ function user_form_process_password_confirm($element) {
   return $element;
 }
 
+/**
+ * Helper to enable password strength indicator settings.
+ *
+ * @param array $password_settings
+ *   Current #attached password settings (optional).
+ *
+ * @return array
+ *   Password settings with password strength indicator enabled.
+ */
+function _user_password_strength_settings(array $password_settings = []) {
+  $password_settings['showStrengthIndicator'] = TRUE;
+  $password_settings += [
+    'strengthTitle' => t('Password strength:'),
+    'hasWeaknesses' => t('Recommendations to make your password stronger:'),
+    'tooShort' => t('Make it at least 12 characters'),
+    'addLowerCase' => t('Add lowercase letters'),
+    'addUpperCase' => t('Add uppercase letters'),
+    'addNumbers' => t('Add numbers'),
+    'addPunctuation' => t('Add punctuation'),
+    'sameAsUsername' => t('Make it different from your username'),
+    'weak' => t('Weak'),
+    'fair' => t('Fair'),
+    'good' => t('Good'),
+    'strong' => t('Strong'),
+    'username' => \Drupal::currentUser()->getAccountName(),
+  ];
+
+  return $password_settings;
+}
+
 /**
  * Implements hook_modules_uninstalled().
  */
diff --git a/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/PasswordRevealTest.php b/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/PasswordRevealTest.php
new file mode 100644
index 0000000000..1df8882f5d
--- /dev/null
+++ b/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/PasswordRevealTest.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Drupal\FunctionalJavascriptTests\Core\Form;
+
+use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+
+/**
+ * Tests the state of elements based on another elements.
+ *
+ * @group javascript
+ */
+class PasswordRevealTest extends WebDriverTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['form_test'];
+
+  /**
+   * Test the password_unmask form element functionality.
+   */
+  public function testPasswordRevealFormElement() {
+    $this->drupalGet('/form-test/password-reveal');
+    $page = $this->getSession()->getPage();
+
+    $toogle_password_one_link = $page->find('css', '.toggle-password');
+    $password_one_field = $page->findById('edit-password-one');
+    $password_two_field = $page->findById('edit-password-two');
+
+    /* Initial state: before the show password link is clicked. */
+
+    // Test that both password fields type is password.
+    $this->assertEquals($password_one_field->getAttribute('type'), 'password');
+    $this->assertEquals($password_two_field->getAttribute('type'), 'password');
+
+    // Change state: click the "show password" of password one link.
+    $toogle_password_one_link->click();
+
+    // Test that the password one field type is now text.
+    $this->assertEquals($password_one_field->getAttribute('type'), 'text');
+    // Test that the password two field type is still password.
+    $this->assertEquals($password_two_field->getAttribute('type'), 'password');
+
+    // Change state: click the "hide password" link.
+    $toogle_password_one_link->click();
+
+    // Test that the password field type is password.
+    $this->assertEquals($password_one_field->getAttribute('type'), 'password');
+    // Test that the password two field type is still password.
+    $this->assertEquals($password_two_field->getAttribute('type'), 'password');
+
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Render/Element/PasswordRevealTest.php b/core/tests/Drupal/Tests/Core/Render/Element/PasswordRevealTest.php
new file mode 100644
index 0000000000..00ea26a4cf
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Render/Element/PasswordRevealTest.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Drupal\Tests\Core\Render\Element;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\Element\PasswordUnmask;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Render\Element\PasswordUnmask
+ * @group Render
+ */
+class PasswordRevealTest extends UnitTestCase {
+
+  /**
+   * @covers ::valueCallback
+   *
+   * @dataProvider providerTestValueCallback
+   */
+  public function testValueCallback($expected, $input) {
+    $element = [];
+    $form_state = $this->prophesize(FormStateInterface::class)->reveal();
+    $this->assertSame($expected, PasswordUnmask::valueCallback($element, $input, $form_state));
+  }
+
+  /**
+   * Data provider for testValueCallback().
+   */
+  public function providerTestValueCallback() {
+    $data = [];
+    $data[] = [NULL, FALSE];
+    $data[] = [NULL, NULL];
+    $data[] = ['', ['test']];
+    $data[] = ['test', 'test'];
+    $data[] = ['123', 123];
+
+    return $data;
+  }
+
+}
diff --git a/core/themes/classy/css/components/user.css b/core/themes/classy/css/components/user.css
index 8baa8f83a3..f0c726f9ec 100644
--- a/core/themes/classy/css/components/user.css
+++ b/core/themes/classy/css/components/user.css
@@ -54,6 +54,11 @@
 [dir="rtl"] .password-parent {
   clear: right;
 }
+@media all and (min-width: 600px) {
+  .password-parent .toggle-password {
+    margin: 0 0.75em;
+  }
+}
 
 /* Styling for the status indicator of the passwords match test.  */
 .password-confirm .ok {
