diff --git a/docs/simplesamlphp-authproc.txt b/docs/simplesamlphp-authproc.txt
index e5b6a42141c1aa27b65eb4906d10b4194237be10..145bd996c4d507ad0a8faeebfec79429efbf0f19 100644
--- a/docs/simplesamlphp-authproc.txt
+++ b/docs/simplesamlphp-authproc.txt
@@ -142,6 +142,9 @@ The following filters are included in the simpleSAMLphp distribution:
 - [`core:TargetedID`](./core:authproc_targetedid): Generate the `eduPersonTargetedID` attribute.
 - [`core:WarnShortSSOInterval`](./core:authproc_warnshortssointerval): Give a warning if the user logs into the same SP twice within a few seconds.
 - [`preprodwarning:Warning`](./preprodwarning:warning): Warn the user about accessing a test IdP.
+- [`saml:AttributeNameID`](./saml:nameid): Generate custon NameID with the value of an attribute.
+- [`saml:PersistentNameID`](./saml:nameid): Generate persistent NameID from an attribute.
+- [`saml:TransientNameID`](./saml:nameid): Generate transient NameID.
 
 
 
diff --git a/modules/saml/docs/nameid.txt b/modules/saml/docs/nameid.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c1d0da4f89e9949900858a53bc922602ae790909
--- /dev/null
+++ b/modules/saml/docs/nameid.txt
@@ -0,0 +1,81 @@
+NameID generation filters
+=========================
+
+This document describes the NameID generation filters in the saml module.
+
+
+Common options
+--------------
+
+`NameQualifier`
+:   The NameQualifier attribute for the generated NameID.
+    This can be a string that is used as the value directly.
+    It can also be `TRUE`, in which case we use the IdP entity ID as the NameQualifier.
+    If it is `FALSE`, no NameQualifier will be included.
+
+:   The default is `FALSE`, which means that we will not include a NameQualifier by default.
+
+`SPNameQualifier`
+:   The SPNameQualifier attribute for the generated NameID.
+    This can be a string that is used as the value directly.
+    It can also be `TRUE`, in which case we use the SP entity ID as the SPNameQualifier.
+    If it is `FALSE`, no SPNameQualifier will be included.
+
+:   The default is `TRUE`, which means that we will use the SP entity ID.
+
+
+`saml:AttributeNameID`
+----------------------
+
+Uses the value of an attribute to generate a NameID.
+
+### Options
+
+`attribute`
+:   The name of the attribute we should use as the unique user ID.
+
+`Format`
+:   The `Format` attribute of the generated NameID.
+
+
+
+`saml:PersistentNameID`
+-----------------------
+
+Generates a persistent NameID with the format `urn:oasis:names:tc:SAML:2.0:nameid-format:persistent`.
+The filter will take the user ID from the attribute described in the `attribute` option, and hash it with the `secretsalt` from `config.php`, and the SP and IdP entity ID.
+The resulting hash is sent as the persistent NameID.
+
+### Options
+
+`attribute`
+:   The name of the attribute we should use as the unique user ID.
+
+
+`saml:TransientNameID`
+----------------------
+
+Generates a transient NameID with the format `urn:oasis:names:tc:SAML:2.0:nameid-format:transient`.
+
+No extra options are available for this filter.
+
+
+Example
+-------
+
+This example makes three NameIDs available:
+
+    'authproc' => array(
+        1 => array(
+            'class' => 'saml:TransientNameID',
+        ),
+        2 => array(
+            'class' => 'saml:PersistentNameID',
+            'attribute' => 'eduPersonPrincipalName',
+        ),
+        3 => array(
+            'class' => 'saml:AttributeNameID',
+            'attribute' => 'eduPersonPrincipalName',
+            'Format' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
+        ),
+    ),
diff --git a/modules/saml/lib/Auth/Process/AttributeNameID.php b/modules/saml/lib/Auth/Process/AttributeNameID.php
new file mode 100644
index 0000000000000000000000000000000000000000..22fa41858f832ecaa6f680d98951a0111b3197d0
--- /dev/null
+++ b/modules/saml/lib/Auth/Process/AttributeNameID.php
@@ -0,0 +1,60 @@
+<?php
+
+/**
+ * Authproc filter to create a NameID from an attribute.
+ *
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class sspmod_saml_Auth_Process_AttributeNameID extends sspmod_saml_BaseNameIDGenerator {
+
+	/**
+	 * The attribute we should use as the NameID.
+	 *
+	 * @var string
+	 */
+	private $attribute;
+
+
+	/**
+	 * Initialize this filter, parse configuration.
+	 *
+	 * @param array $config  Configuration information about this filter.
+	 * @param mixed $reserved  For future use.
+	 */
+	public function __construct($config, $reserved) {
+		parent::__construct($config, $reserved);
+		assert('is_array($config)');
+
+		if (!isset($config['Format'])) {
+			throw new SimpleSAML_Error_Exception('AttributeNameID: Missing required option \'Format\'.');
+		}
+		$this->format = (string)$config['Format'];
+
+		if (!isset($config['attribute'])) {
+			throw new SimpleSAML_Error_Exception('AttributeNameID: Missing required option \'attribute\'.');
+		}
+		$this->attribute = (string)$config['attribute'];
+	}
+
+
+	/**
+	 * Get the NameID value.
+	 *
+	 * @return string|NULL  The NameID value.
+	 */
+	protected function getValue(array &$state) {
+
+		if (!isset($state['Attributes'][$this->attribute]) || count($state['Attributes'][$this->attribute]) === 0) {
+			SimpleSAML_Logger::warning('Missing attribute ' . var_export($this->attribute, TRUE) . ' on user - not generating attribute NameID.');
+			return NULL;
+		}
+		if (count($state['Attributes'][$this->attribute]) > 1) {
+			SimpleSAML_Logger::warning('More than one value in attribute ' . var_export($this->attribute, TRUE) . ' on user - not generating attribute NameID.');
+		}
+		$value = array_values($state['Attributes'][$this->attribute]); /* Just in case the first index is no longer 0. */
+		$value = $value[0];
+		return $value;
+	}
+
+}
diff --git a/modules/saml/lib/Auth/Process/PersistentNameID.php b/modules/saml/lib/Auth/Process/PersistentNameID.php
new file mode 100644
index 0000000000000000000000000000000000000000..aa0a243bce8912faed92466e90477299370c72f3
--- /dev/null
+++ b/modules/saml/lib/Auth/Process/PersistentNameID.php
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * Authproc filter to generate a persistent NameID.
+ *
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class sspmod_saml_Auth_Process_PersistentNameID extends sspmod_saml_BaseNameIDGenerator {
+
+	/**
+	 * Which attribute contains the unique identifier of the user.
+	 *
+	 * @var string
+	 */
+	private $attribute;
+
+
+	/**
+	 * Initialize this filter, parse configuration.
+	 *
+	 * @param array $config  Configuration information about this filter.
+	 * @param mixed $reserved  For future use.
+	 */
+	public function __construct($config, $reserved) {
+		parent::__construct($config, $reserved);
+		assert('is_array($config)');
+
+		$this->format = SAML2_Const::NAMEID_PERSISTENT;
+
+		if (!isset($config['attribute'])) {
+			throw new SimpleSAML_Error_Exception('PersistentNameID: Missing required option \'attribute\'.');
+		}
+		$this->attribute = $config['attribute'];
+	}
+
+
+	/**
+	 * Get the NameID value.
+	 *
+	 * @return string|NULL  The NameID value.
+	 */
+	protected function getValue(array &$state) {
+
+		if (!isset($state['SPMetadata']['entityid'])) {
+			SimpleSAML_Logger::warning('No SP entity ID - not generating persistent NameID.');
+			return NULL;
+		}
+		$spEntityId = $state['SPMetadata']['entityid'];
+
+		if (!isset($state['IdPMetadata']['entityid'])) {
+			SimpleSAML_Logger::warning('No IdP entity ID - not generating persistent NameID.');
+			return NULL;
+		}
+		$idpEntityId = $state['IdPMetadata']['entityid'];
+
+		if (!isset($state['Attributes'][$this->attribute]) || count($state['Attributes'][$this->attribute]) === 0) {
+			SimpleSAML_Logger::warning('Missing attribute ' . var_export($this->attribute, TRUE) . ' on user - not generating persistent NameID.');
+			return NULL;
+		}
+		if (count($state['Attributes'][$this->attribute]) > 1) {
+			SimpleSAML_Logger::warning('More than one value in attribute ' . var_export($this->attribute, TRUE) . ' on user - not generating persistent NameID.');
+		}
+		$uid = array_values($state['Attributes'][$this->attribute]); /* Just in case the first index is no longer 0. */
+		$uid = $uid[0];
+
+		$secretSalt = SimpleSAML_Utilities::getSecretSalt();
+
+		$uidData = 'saml:PersistentNameID:' . $secretSalt;
+		$uidData .= strlen($idpEntityId) . ':' . $idpEntityId;
+		$uidData .= strlen($spEntityId) . ':' . $spEntityId;
+		$uidData .= strlen($uid) . ':' . $uid;
+		$uidData .= $secretSalt;
+
+		return sha1($uidData);
+	}
+
+}
diff --git a/modules/saml/lib/Auth/Process/TransientNameID.php b/modules/saml/lib/Auth/Process/TransientNameID.php
new file mode 100644
index 0000000000000000000000000000000000000000..44e4e20fa6bf67e5f80484fbd30feda63bf09bb1
--- /dev/null
+++ b/modules/saml/lib/Auth/Process/TransientNameID.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * Authproc filter to generate a transient NameID.
+ *
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class sspmod_saml_Auth_Process_TransientNameID extends sspmod_saml_BaseNameIDGenerator {
+
+	/**
+	 * Initialize this filter, parse configuration
+	 *
+	 * @param array $config  Configuration information about this filter.
+	 * @param mixed $reserved  For future use.
+	 */
+	public function __construct($config, $reserved) {
+		parent::__construct($config, $reserved);
+		assert('is_array($config)');
+
+		$this->format = SAML2_Const::NAMEID_TRANSIENT;
+	}
+
+
+	/**
+	 * Get the NameID value.
+	 *
+	 * @return string|NULL  The NameID value.
+	 */
+	protected function getValue(array &$state) {
+
+		return SimpleSAML_Utilities::generateID();
+	}
+
+}
diff --git a/modules/saml/lib/BaseNameIDGenerator.php b/modules/saml/lib/BaseNameIDGenerator.php
new file mode 100644
index 0000000000000000000000000000000000000000..177079a5de39e339320a3ff641832b7480b9a3da
--- /dev/null
+++ b/modules/saml/lib/BaseNameIDGenerator.php
@@ -0,0 +1,116 @@
+<?php
+
+/**
+ * Base filter for generating NameID values.
+ *
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+abstract class sspmod_saml_BaseNameIDGenerator extends SimpleSAML_Auth_ProcessingFilter {
+
+	/**
+	 * What NameQualifier should be used.
+	 * Can be one of:
+	 *  - a string: The qualifier to use.
+	 *  - FALSE: Do not include a NameQualifier. This is the default.
+	 *  - TRUE: Use the IdP entity ID.
+	 *
+	 * @var string|bool
+	 */
+	private $nameQualifier;
+
+
+	/**
+	 * What SPNameQualifier should be used.
+	 * Can be one of:
+	 *  - a string: The qualifier to use.
+	 *  - FALSE: Do not include a SPNameQualifier.
+	 *  - TRUE: Use the SP entity ID. This is the default.
+	 *
+	 * @var string|bool
+	 */
+	private $spNameQualifier;
+
+
+	/**
+	 * The format of this NameID.
+	 *
+	 * This property must be initialized the subclass.
+	 *
+	 * @var string
+	 */
+	protected $format;
+
+
+	/**
+	 * Initialize this filter, parse configuration.
+	 *
+	 * @param array $config  Configuration information about this filter.
+	 * @param mixed $reserved  For future use.
+	 */
+	public function __construct($config, $reserved) {
+		parent::__construct($config, $reserved);
+		assert('is_array($config)');
+
+		if (isset($config['NameQualifier'])) {
+			$this->nameQualifier = $config['NameQualifier'];
+		} else {
+			$this->nameQualifier = FALSE;
+		}
+
+		if (isset($config['SPNameQualifier'])) {
+			$this->spNameQualifier = $config['SPNameQualifier'];
+		} else {
+			$this->spNameQualifier = TRUE;
+		}
+	}
+
+
+	/**
+	 * Get the NameID value.
+	 *
+	 * @return string|NULL  The NameID value.
+	 */
+	abstract protected function getValue(array &$state);
+
+
+	/**
+	 * Generate transient NameID.
+	 *
+	 * @param array &$state  The request state.
+	 */
+	public function process(&$state) {
+		assert('is_array($state)');
+		assert('is_string($this->format)');
+
+		$value = $this->getValue($state);
+		if ($value === NULL) {
+			return;
+		}
+
+		$nameId = array('Value' => $value);
+
+		if ($this->nameQualifier === TRUE) {
+			if (isset($state['IdPMetadata']['entityid'])) {
+				$nameId['NameQualifier'] = $state['IdPMetadata']['entityid'];
+			} else {
+				SimpleSAML_Logger::warning('No IdP entity ID, unable to set NameQualifier.');
+			}
+		} elseif (is_string($this->nameQualifier)) {
+			$nameId['NameQualifier'] = $this->nameQualifier;
+		}
+
+		if ($this->spNameQualifier === TRUE) {
+			if (isset($state['SPMetadata']['entityid'])) {
+				$nameId['SPNameQualifier'] = $state['SPMetadata']['entityid'];
+			} else {
+				SimpleSAML_Logger::warning('No SP entity ID, unable to set SPNameQualifier.');
+			}
+		} elseif (is_string($this->spNameQualifier)) {
+			$nameId['SPNameQualifier'] = $this->spNameQualifier;
+		}
+
+		$state['saml:NameID'][$this->format] = $nameId;
+	}
+
+}