diff --git a/config-templates/authsources.php b/config-templates/authsources.php
index 093e9dfaa5b5330e1140f72d593418e35023cfd7..87d1834190581d8ee29dc136ecdc8df3ef5433ea 100644
--- a/config-templates/authsources.php
+++ b/config-templates/authsources.php
@@ -52,6 +52,12 @@ $config = array(
 	/*
 	'example-userpass' => array(
 		'exampleauth:UserPass',
+
+		// Give the user an option to save their username for future login attempts
+		// And when enabled, what should the default be, to save the username or not
+		//'remember.username.enabled' => FALSE,
+		//'remember.username.checked' => FALSE,
+
 		'student:studentpass' => array(
 			'uid' => array('test'),
 			'eduPersonAffiliation' => array('member', 'student'),
@@ -213,6 +219,11 @@ $config = array(
 	'example-ldap' => array(
 		'ldap:LDAP',
 
+		// Give the user an option to save their username for future login attempts
+		// And when enabled, what should the default be, to save the username or not
+		//'remember.username.enabled' => FALSE,
+		//'remember.username.checked' => FALSE,
+
 		// The hostname of the LDAP server.
 		'hostname' => 'ldap.example.org',
 
@@ -278,6 +289,11 @@ $config = array(
 	'example-ldapmulti' => array(
 		'ldap:LDAPMulti',
 
+		// Give the user an option to save their username for future login attempts
+		// And when enabled, what should the default be, to save the username or not
+		//'remember.username.enabled' => FALSE,
+		//'remember.username.checked' => FALSE,
+
 		// The way the organization as part of the username should be handled.
 		// Three possible values:
 		// - 'none':   No handling of the organization. Allows '@' to be part
diff --git a/dictionaries/login.definition.json b/dictionaries/login.definition.json
index 478f852a559a9c199d036f7405a44eed5f69ab76..1acb64e1063b3695bcebcdb3517cb3f3659119ec 100644
--- a/dictionaries/login.definition.json
+++ b/dictionaries/login.definition.json
@@ -55,5 +55,8 @@
 	},
 	"contact_info": {
 		"en": "Contact information:"
+	},
+	"remember_username": {
+		"en": "Remember my username"
 	}
 }
diff --git a/modules/core/lib/Auth/UserPassBase.php b/modules/core/lib/Auth/UserPassBase.php
index 0b63e92838fcd660b8dd6102b30a8bc51d474932..b7ee4dc74bdd813070a2386f4e3187a634d891e9 100644
--- a/modules/core/lib/Auth/UserPassBase.php
+++ b/modules/core/lib/Auth/UserPassBase.php
@@ -39,6 +39,22 @@ abstract class sspmod_core_Auth_UserPassBase extends SimpleSAML_Auth_Source {
 	 */
 	protected $loginLinks;
 
+	/**
+	 * Storage for authsource config option remember.username.enabled
+	 * loginuserpass.php and loginuserpassorg.php pages/templates use this option to
+	 * present users with a checkbox to save their username for the next login request.
+	 * @var bool
+	 */
+	protected $rememberUsernameEnabled = FALSE;
+
+	/**
+	 * Storage for authsource config option remember.username.checked
+	 * loginuserpass.php and loginuserpassorg.php pages/templates use this option
+	 * to default the remember username checkbox to checked or not.
+	 * @var bool
+	 */
+	protected $rememberUsernameChecked = FALSE;
+
 
 	/**
 	 * Constructor for this authentication source.
@@ -59,6 +75,16 @@ abstract class sspmod_core_Auth_UserPassBase extends SimpleSAML_Auth_Source {
 
 		/* Call the parent constructor first, as required by the interface. */
 		parent::__construct($info, $config);
+
+		// Get the remember username config options
+		if (isset($config['remember.username.enabled'])) {
+			$this->rememberUsernameEnabled = (bool) $config['remember.username.enabled'];
+			unset($config['remember.username.enabled']);
+		}
+		if (isset($config['remember.username.checked'])) {
+			$this->rememberUsernameChecked = (bool) $config['remember.username.checked'];
+			unset($config['remember.username.checked']);
+		}
 	}
 
 
@@ -79,6 +105,22 @@ abstract class sspmod_core_Auth_UserPassBase extends SimpleSAML_Auth_Source {
 		return $this->loginLinks;
 	}
 
+	/**
+	 * Getter for the authsource config option remember.username.enabled
+	 * @return bool
+	 */
+	public function getRememberUsernameEnabled() {
+		return $this->rememberUsernameEnabled;
+	}
+
+	/**
+	 * Getter for the authsource config option remember.username.checked
+	 * @return bool
+	 */
+	public function getRememberUsernameChecked() {
+		return $this->rememberUsernameChecked;
+	}
+
 
 	/**
 	 * Initialize login.
diff --git a/modules/core/lib/Auth/UserPassOrgBase.php b/modules/core/lib/Auth/UserPassOrgBase.php
index e07eccb4341a2e93eb6e1886f57d8a5a6162848c..1c151fff84e2d81f8d3d677fbe1edf9b1fd65ef7 100644
--- a/modules/core/lib/Auth/UserPassOrgBase.php
+++ b/modules/core/lib/Auth/UserPassOrgBase.php
@@ -42,6 +42,22 @@ abstract class sspmod_core_Auth_UserPassOrgBase extends SimpleSAML_Auth_Source {
 	 */
 	private $usernameOrgMethod;
 
+	/**
+	 * Storage for authsource config option remember.username.enabled
+	 * loginuserpass.php and loginuserpassorg.php pages/templates use this option to
+	 * present users with a checkbox to save their username for the next login request.
+	 * @var bool
+	 */
+	protected $rememberUsernameEnabled = FALSE;
+
+	/**
+	 * Storage for authsource config option remember.username.checked
+	 * loginuserpass.php and loginuserpassorg.php pages/templates use this option
+	 * to default the remember username checkbox to checked or not.
+	 * @var bool
+	 */
+	protected $rememberUsernameChecked = FALSE;
+
 
 	/**
 	 * Constructor for this authentication source.
@@ -59,6 +75,14 @@ abstract class sspmod_core_Auth_UserPassOrgBase extends SimpleSAML_Auth_Source {
 		/* Call the parent constructor first, as required by the interface. */
 		parent::__construct($info, $config);
 
+		// Get the remember username config options
+		if (isset($config['remember.username.enabled'])) {
+			$this->rememberUsernameEnabled = (bool) $config['remember.username.enabled'];
+		}
+		if (isset($config['remember.username.checked'])) {
+			$this->rememberUsernameChecked = (bool) $config['remember.username.checked'];
+		}
+
 		$this->usernameOrgMethod = 'none';
 	}
 
@@ -96,6 +120,22 @@ abstract class sspmod_core_Auth_UserPassOrgBase extends SimpleSAML_Auth_Source {
 		return $this->usernameOrgMethod;
 	}
 
+	/**
+	 * Getter for the authsource config option remember.username.enabled
+	 * @return bool
+	 */
+	public function getRememberUsernameEnabled() {
+		return $this->rememberUsernameEnabled;
+	}
+
+	/**
+	 * Getter for the authsource config option remember.username.checked
+	 * @return bool
+	 */
+	public function getRememberUsernameChecked() {
+		return $this->rememberUsernameChecked;
+	}
+
 
 	/**
 	 * Initialize login.
diff --git a/modules/core/templates/loginuserpass.php b/modules/core/templates/loginuserpass.php
index e2657cd674dac252daff12189938af14dd46e340..76e6d4ceae22446e7630ea4b46e9902adb9dc274 100644
--- a/modules/core/templates/loginuserpass.php
+++ b/modules/core/templates/loginuserpass.php
@@ -40,13 +40,44 @@ if ($this->data['forceUsername']) {
 }
 ?>
 			</td>
-			<td style="padding: .4em;" rowspan="3">
-				<input type="submit" tabindex="4" value="<?php echo $this->t('{login:login_button}'); ?>" />
+<?php
+if ($this->data['rememberUsernameEnabled']) {
+	$rowspan = 1;
+} elseif (array_key_exists('organizations', $this->data)) {
+	$rowspan = 3;
+} else {
+	$rowspan = 2;
+}
+?>
+			<td style="padding: .4em;" rowspan="<?php echo $rowspan; ?>">
+<?php
+if ($this->data['rememberUsernameEnabled']) {
+	echo str_repeat("\t", 4);
+	echo '<input type="checkbox" id="remember_username" tabindex="4" name="remember_username" value="Yes" ';
+	echo ($this->data['rememberUsernameChecked'] ? 'checked="Yes" /> ' : '/> ');
+	echo $this->t('{login:remember_username}');
+} else {
+	$text = $this->t('{login:login_button}');
+	echo str_repeat("\t", 4);
+	echo "<input type=\"submit\" tabindex=\"4\" value=\"{$text}\" />";
+}
+?>
 			</td>
 		</tr>
 		<tr>
 			<td style="padding: .3em;"><?php echo $this->t('{login:password}'); ?></td>
 			<td><input id="password" type="password" tabindex="2" name="password" /></td>
+<?php
+// Move submit button to next row if remember checkbox enabled
+if ($this->data['rememberUsernameEnabled']) {
+	$rowspan = (array_key_exists('organizations', $this->data) ? 2 : 1);
+?>
+			<td style="padding: .4em;" rowspan="<?php echo $rowspan; ?>">
+				<input type="submit" tabindex="5" value="<?php echo $this->t('{login:login_button}'); ?>" />
+			</td>
+<?php
+}
+?>
 		</tr>
 
 <?php
diff --git a/modules/core/www/loginuserpass.php b/modules/core/www/loginuserpass.php
index 4e41a13053a0559de93abb2eca2c2ffedc3871af..77f1b91277916346fa98b368c63fc8290c18f89e 100644
--- a/modules/core/www/loginuserpass.php
+++ b/modules/core/www/loginuserpass.php
@@ -27,6 +27,8 @@ if ($source === NULL) {
 
 if (array_key_exists('username', $_REQUEST)) {
 	$username = $_REQUEST['username'];
+} elseif ($source->getRememberUsernameEnabled() && array_key_exists($source->getAuthId() . '-username', $_COOKIE)) {
+	$username = $_COOKIE[$source->getAuthId() . '-username'];
 } elseif (isset($state['core:username'])) {
 	$username = (string)$state['core:username'];
 } else {
@@ -46,6 +48,14 @@ if (!empty($_REQUEST['username']) || !empty($password)) {
 		$username = $state['forcedUsername'];
 	}
 
+	if ($source->getRememberUsernameEnabled()) {
+		$sessionHandler = SimpleSAML_SessionHandler::getSessionHandler();
+		$params = $sessionHandler->getCookieParams();
+		$params['expire'] = time();
+		$params['expire'] += (isset($_REQUEST['remember_username']) && $_REQUEST['remember_username'] == 'Yes' ? 31536000 : -300);
+		setcookie($source->getAuthId() . '-username', $username, $params['expire'], $params['path'], $params['domain'], $params['secure'], $params['httponly']);
+	}
+
 	$errorCode = sspmod_core_Auth_UserPassBase::handleLogin($authStateId, $username, $password);
 } else {
 	$errorCode = NULL;
@@ -57,9 +67,14 @@ $t->data['stateparams'] = array('AuthState' => $authStateId);
 if (array_key_exists('forcedUsername', $state)) {
 	$t->data['username'] = $state['forcedUsername'];
 	$t->data['forceUsername'] = TRUE;
+	$t->data['rememberUsernameEnabled'] = FALSE;
+	$t->data['rememberUsernameChecked'] = FALSE;
 } else {
 	$t->data['username'] = $username;
 	$t->data['forceUsername'] = FALSE;
+	$t->data['rememberUsernameEnabled'] = $source->getRememberUsernameEnabled();
+	$t->data['rememberUsernameChecked'] = $source->getRememberUsernameChecked();
+	if (isset($_COOKIE[$source->getAuthId() . '-username'])) $t->data['rememberUsernameChecked'] = TRUE;
 }
 $t->data['links'] = $source->getLoginLinks();
 $t->data['errorcode'] = $errorCode;
diff --git a/modules/core/www/loginuserpassorg.php b/modules/core/www/loginuserpassorg.php
index f3713db4f5ded706a0d1fc77fdc9dc06982caa29..b1d1a083183ac3bdfaed9bfe34dd276a562ae83f 100644
--- a/modules/core/www/loginuserpassorg.php
+++ b/modules/core/www/loginuserpassorg.php
@@ -18,10 +18,17 @@ $authStateId = $_REQUEST['AuthState'];
 /* Retrieve the authentication state. */
 $state = SimpleSAML_Auth_State::loadState($authStateId, sspmod_core_Auth_UserPassOrgBase::STAGEID);
 
+$source = SimpleSAML_Auth_Source::getById($state[sspmod_core_Auth_UserPassOrgBase::AUTHID]);
+if ($source === NULL) {
+	throw new Exception('Could not find authentication source with id ' . $state[sspmod_core_Auth_UserPassOrgBase::AUTHID]);
+}
+
 $organizations = sspmod_core_Auth_UserPassOrgBase::listOrganizations($authStateId);
 
 if (array_key_exists('username', $_REQUEST)) {
 	$username = $_REQUEST['username'];
+} elseif ($source->getRememberUsernameEnabled() && array_key_exists($source->getAuthId() . '-username', $_COOKIE)) {
+	$username = $_COOKIE[$source->getAuthId() . '-username'];
 } elseif (isset($state['core:username'])) {
 	$username = (string)$state['core:username'];
 } else {
@@ -45,6 +52,15 @@ if (array_key_exists('organization', $_REQUEST)) {
 $errorCode = NULL;
 if ($organizations === NULL || !empty($organization)) {
 	if (!empty($username) && !empty($password)) {
+
+		if ($source->getRememberUsernameEnabled()) {
+			$sessionHandler = SimpleSAML_SessionHandler::getSessionHandler();
+			$params = $sessionHandler->getCookieParams();
+			$params['expire'] = time();
+			$params['expire'] += (isset($_REQUEST['remember_username']) && $_REQUEST['remember_username'] == 'Yes' ? 31536000 : -300);
+			setcookie($source->getAuthId() . '-username', $username, $params['expire'], $params['path'], $params['domain'], $params['secure'], $params['httponly']);
+		}
+
 		$errorCode = sspmod_core_Auth_UserPassOrgBase::handleLogin($authStateId, $username, $password, $organization);
 	}
 }
@@ -54,6 +70,9 @@ $t = new SimpleSAML_XHTML_Template($globalConfig, 'core:loginuserpass.php');
 $t->data['stateparams'] = array('AuthState' => $authStateId);
 $t->data['username'] = $username;
 $t->data['forceUsername'] = FALSE;
+$t->data['rememberUsernameEnabled'] = $source->getRememberUsernameEnabled();
+$t->data['rememberUsernameChecked'] = $source->getRememberUsernameChecked();
+if (isset($_COOKIE[$source->getAuthId() . '-username'])) $t->data['rememberUsernameChecked'] = TRUE;
 $t->data['errorcode'] = $errorCode;
 
 if ($organizations !== NULL) {