diff --git a/lib/SimpleSAML/Auth/ProcessingChain.php b/lib/SimpleSAML/Auth/ProcessingChain.php
new file mode 100644
index 0000000000000000000000000000000000000000..4ba54ba4d4b0c4cc21879a60e4a904c362f2368c
--- /dev/null
+++ b/lib/SimpleSAML/Auth/ProcessingChain.php
@@ -0,0 +1,218 @@
+<?php
+
+/**
+ * Class for implementing authentication processing chains for IdPs.
+ *
+ * This class implements a system for additional steps which should be taken by an IdP before
+ * submitting a response to a SP. Examples of additional steps can be additional authentication
+ * checks, or attribute consent requirements.
+ *
+ * @author Olav Morken, UNINETT AS.
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class SimpleSAML_Auth_ProcessingChain {
+
+
+	/**
+	 * The list of remaining filters which should be applied to the state.
+	 */
+	const FILTERS_INDEX = 'SimpleSAML_Auth_ProcessingChain.filters';
+
+
+	/**
+	 * The stage we use for completed requests.
+	 */
+	const COMPLETED_STAGE = 'SimpleSAML_Auth_ProcessingChain.completed';
+
+
+	/**
+	 * The request parameter we will use to pass the state identifier when we redirect after
+	 * having completed processing of the state.
+	 */
+	const AUTHPARAM = 'AuthProcId';
+
+
+	/**
+	 * All authentication processing filters, in the order they should be applied.
+	 */
+	private $filters;
+
+
+	/**
+	 * Initialize an authentication processing chain for the given service provider
+	 * and identity provider.
+	 *
+	 * @param array $idpMetadata  The metadata for the IdP.
+	 * @param array $spMetadata  The metadata for the SP.
+	 */
+	public function __construct($idpMetadata, $spMetadata) {
+		assert('is_array($idpMetadata)');
+		assert('is_array($spMetadata)');
+
+		$this->filters = array();
+
+		if (array_key_exists('authproc', $idpMetadata)) {
+			$idpFilters = self::parseFilterList($idpMetadata['authproc']);
+			self::addFilters($this->filters, $idpFilters);
+		}
+
+		if (array_key_exists('authproc', $spMetadata)) {
+			$spFilters = self::parseFilterList($spMetadata['authproc']);
+			self::addFilters($this->filters, $spFilters);
+		}
+
+
+		SimpleSAML_Logger::debug('Filter config for ' . $idpMetadata['entityid'] . '->' .
+			$spMetadata['entityid'] . ': ' . str_replace("\n", '', var_export($this->filters, TRUE)));
+
+	}
+
+
+	/**
+	 * Sort & merge filter configuration
+	 *
+	 * Inserts unsorted filters into sorted filter list. This sort operation is stable.
+	 *
+	 * @param array &$target  Target filter list. This list must be sorted.
+	 * @param array $src  Source filters. May be unsorted.
+	 */
+	private static function addFilters(&$target, $src) {
+		assert('is_array($target)');
+		assert('is_array($src)');
+
+		foreach ($src as $filter) {
+			$fp = $filter->priority;
+
+			/* Find insertion position for filter. */
+			for($i = count($target)-1; $i >= 0; $i--) {
+				if ($target[$i]->priority <= $fp) {
+					/* The new filter should be inserted after this one. */
+					break;
+				}
+			}
+			/* $i now points to the filter which should preceede the current filter. */
+			array_splice($target, $i+1, 0, array($filter));
+		}
+
+	}
+
+
+	/**
+	 * Parse an array of authentication processing filters.
+	 *
+	 * @param array $filterSrc  Array with filter configuration.
+	 * @return array  Array of SimpleSAML_Auth_ProcessingFilter objects.
+	 */
+	private static function parseFilterList($filterSrc) {
+		assert('is_array($filterSrc)');
+
+		$parsedFilters = array();
+
+		foreach ($filterSrc as $filter) {
+
+			if (is_string($filter)) {
+				$filter = array($filter);
+			}
+
+			if (!is_array($filter)) {
+				throw new Exception('Invalid authentication processing filter configuration: ' .
+					'One of the filters wasn\'t a string or an array.');
+			}
+
+			$parsedFilters[] = self::parseFilter($filter);
+		}
+
+		return $parsedFilters;
+	}
+
+
+	/**
+	 * Parse an authentication processing filter.
+	 *
+	 * @param array $config  Array with the authentication processing filter configuration.
+	 * @return SimpleSAML_Auth_ProcessingFilter  The parsed filter.
+	 */
+	private static function parseFilter($config) {
+		assert('is_array($config)');
+
+		if (!array_key_exists(0, $config)) {
+			throw new Exception('Authentication processing filter without name given.');
+		}
+
+		$className = SimpleSAML_Module::resolveClass($config[0], 'Auth_Process',
+			'SimpleSAML_Auth_ProcessingFilter');
+
+		unset($config[0]);
+		return new $className($config, NULL);
+	}
+
+
+	/**
+	 * Process the given state.
+	 *
+	 * This function will only return if processing completes. If processing requires showing
+	 * a page to the user, we will redirect to the URL set in $state['ReturnURL'] after processing is
+	 * completed.
+	 *
+	 * @param array &$state  The state we are processing.
+	 */
+	public function processState(&$state) {
+		assert('is_array($state)');
+		assert('array_key_exists("ReturnURL", $state)');
+
+		$state[self::FILTERS_INDEX] = $this->filters;
+
+		while (count($state[self::FILTERS_INDEX]) > 0) {
+			$filter = array_shift($state[self::FILTERS_INDEX]);
+			$filter->process($state);
+		}
+
+		/* Completed. */
+	}
+
+
+	/**
+	 * Continues processing of the state.
+	 *
+	 * This function is used to resume processing by filters which for example needed to show
+	 * a page to the user.
+	 *
+	 * This function will never return. In the case of an exception, exception handling should
+	 * be left to the main simpleSAMLphp exception handler.
+	 *
+	 * @param array $state  The state we are processing.
+	 */
+	public static function resumeProcessing($state) {
+		assert('is_array($state)');
+
+		while (count($state[self::FILTERS_INDEX]) > 0) {
+			$filter = array_shift($state[self::FILTERS_INDEX]);
+			$filter->process($state);
+		}
+
+		assert('array_key_exists("ReturnURL", $state)');
+
+		/* Completed. Save state information, and redirect to the URL specified
+		 * in $state['ReturnURL'].
+		 */
+		$id = SimpleSAML_Auth_State::saveState($state, self::COMPLETED_STAGE);
+		SimpleSAML_Utilities::redirect($state['ReturnURL'], array(self::AUTHPARAM => $id));
+	}
+
+
+	/**
+	 * Retrieve a state which has finished processing.
+	 *
+	 * @param string $id  The identifier of the state. This can be found in the request parameter
+	 *                    with index from SimpleSAML_Auth_ProcessingChain::AUTHPARAM.
+	 */
+	public static function fetchProcessedState($id) {
+		assert('is_string($id)');
+
+		return SimpleSAML_Auth_State::loadState($id, self::COMPLETED_STAGE);
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/lib/SimpleSAML/Auth/ProcessingFilter.php b/lib/SimpleSAML/Auth/ProcessingFilter.php
new file mode 100644
index 0000000000000000000000000000000000000000..a9d42aca4ac89b685dbcf52bb50413afcc6a2c6d
--- /dev/null
+++ b/lib/SimpleSAML/Auth/ProcessingFilter.php
@@ -0,0 +1,69 @@
+<?php
+
+
+/**
+ * Base class for authentication processing filters.
+ *
+ * All authentication processing filters must support serialization.
+ *
+ * The current request is stored in an associative array. It has the following defined attributes:
+ * - 'Attributes'  The attributes of the user.
+ * - 'Destination'  Metadata of the destination (SP).
+ * - 'Source'  Metadata of the source (IdP).
+ *
+ * It may also contain other attributes. If an authentication processing filter wishes to store other
+ * information in it, it should have a name on the form 'module:filter:attributename', to avoid name
+ * collisions.
+ *
+ * @author Olav Morken, UNINETT AS.
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+abstract class SimpleSAML_Auth_ProcessingFilter {
+
+	/**
+	 * Priority of this filter.
+	 *
+	 * Used when merging IdP and SP processing chains.
+	 * The priority can be any integer. The default for most filters is 50. Filters may however
+	 * specify their own default, if they typically should be amongst the first or the last filters.
+	 *
+	 * The prioroty can also be overridden by the user by specifying the '%priority' option.
+	 */
+	public $priority = 50;
+
+
+	/**
+	 * Constructor for a processing filter.
+	 *
+	 * Any processing filter which implements its own constructor must call this
+	 * constructor first.
+	 *
+	 * @param array &$config  Configuration for this filter.
+	 * @param mixed $reserved  For future use.
+	 */
+	public function __construct(&$config, $reserved) {
+		assert('is_array($config)');
+
+		if(array_key_exists('%priority', $config)) {
+			$this->priority = $config['%priority'];
+			if(!is_int($this->priority)) {
+				throw new Exception('Invalid priority: ' . var_export($this->priority, TRUE));
+			}
+			unset($config['%priority']);
+		}
+	}
+
+
+	/**
+	 * Process a request.
+	 *
+	 * When a filter returns from this function, it is assumed to have completed its task.
+	 *
+	 * @param array &$request  The request we are currently processing.
+	 */
+	abstract public function process(&$request);
+
+}
+
+?>
\ No newline at end of file
diff --git a/modules/core/default-enable b/modules/core/default-enable
new file mode 100644
index 0000000000000000000000000000000000000000..25615cb47c350d23033eb9801627ed8330bcc3e9
--- /dev/null
+++ b/modules/core/default-enable
@@ -0,0 +1,3 @@
+This file indicates that the default state of this module
+is enabled. To disable, create a file named disable in the
+same directory as this file.
diff --git a/modules/core/lib/Auth/Process/AttributeAdd.php b/modules/core/lib/Auth/Process/AttributeAdd.php
new file mode 100644
index 0000000000000000000000000000000000000000..5d31acae0ef07d92aa32f89fb53d19d600fe3f77
--- /dev/null
+++ b/modules/core/lib/Auth/Process/AttributeAdd.php
@@ -0,0 +1,113 @@
+<?php
+
+/**
+ * Filter to add attributes.
+ *
+ * This filter allows you to add attributes to the attribute set being processed.
+ *
+ * Example - add attribute, single value:
+ * <code>
+ * 'authproc' => array(
+ *   array('core:AttributeAdd', 'source' => 'myidp'),
+ *   ),
+ * </code>
+ *
+ * Examle - add attribute, multiple values:
+ * <code>
+ * 'authproc' => array(
+ *   array('core:AttributeAdd', 'groups' => array('users', 'members')),
+ *   ),
+ * </code>
+ *
+ * Examle - replace attribute, single value:
+ * <code>
+ * 'authproc' => array(
+ *   array('core:AttributeAdd', '%replace', 'uid' => array('guest')),
+ *   ),
+ * </code>
+ *
+ * @author Olav Morken, UNINETT AS.
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class sspmod_core_Auth_Process_AttributeAdd extends SimpleSAML_Auth_ProcessingFilter {
+
+	/**
+	 * Flag which indicates wheter this filter should append new values or replace old values.
+	 */
+	private $replace = FALSE;
+
+
+	/**
+	 * Attributes which should be added/appended.
+	 *
+	 * Assiciative array of arrays.
+	 */
+	private $attributes = array();
+
+
+	/**
+	 * Initialize this filter.
+	 *
+	 * @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)');
+
+		foreach($config as $name => $values) {
+			if(is_int($name)) {
+				if($values === '%replace') {
+					$this->replace = TRUE;
+				} else {
+					throw new Exception('Unknown flag: ' . var_export($values, TRUE));
+				}
+				continue;
+			}
+
+			if(!is_string($name)) {
+				throw new Exception('Invalid attribute name: ' . var_export($name, TRUE));
+			}
+
+			if(!is_array($values)) {
+				$values = array($values);
+			}
+			foreach($values as $value) {
+				if(!is_string($value)) {
+					throw new Exception('Invalid value for attribute ' . $name . ': ' .
+						var_export($values, TRUE));
+				}
+			}
+
+			$this->attributes[$name] = $values;
+		}
+	}
+
+
+	/**
+	 * Apply filter to add or replace attributes.
+	 *
+	 * Add or replace existing attributes with the configured values.
+	 *
+	 * @param array &$request  The current request
+	 */
+	public function process(&$request) {
+		assert('is_array($request)');
+		assert('array_key_exists("Attributes", $request)');
+
+		$attributes =& $request['Attributes'];
+
+		foreach($this->attributes as $name => $values) {
+			if($this->replace === TRUE || !array_key_exists($name, $attributes)) {
+				$attributes[$name] = $values;
+			} else {
+				$attributes[$name] = array_merge($attributes[$name], $values);
+			}
+		}
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/modules/core/lib/Auth/Process/AttributeLimit.php b/modules/core/lib/Auth/Process/AttributeLimit.php
new file mode 100644
index 0000000000000000000000000000000000000000..d96df12c35d59aaed7fd5da8389b626019271a53
--- /dev/null
+++ b/modules/core/lib/Auth/Process/AttributeLimit.php
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * A filter for limiting which attributes are passed on.
+ *
+ * Example - remove all attributes except 'cn' and 'mail':
+ * <code>
+ * 'authproc' => array(
+ *   array('core:AttributeLimit', 'cn', 'mail'),
+ *   ),
+ * </code>
+ *
+ * @author Olav Morken, UNINETT AS.
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class sspmod_core_Auth_Process_AttributeLimit extends SimpleSAML_Auth_ProcessingFilter {
+
+	/**
+	 * List of attributes which this filter will allow through.
+	 */
+	private $allowedAttributes = array();
+
+
+	/**
+	 * Initialize this filter.
+	 *
+	 * @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)');
+
+		foreach($config as $name) {
+
+			if(!is_string($name)) {
+				throw new Exception('Invalid attribute name: ' . var_export($name, TRUE));
+			}
+
+			$this->allowedAttributes[] = $name;
+		}
+	}
+
+
+	/**
+	 * Apply filter to remove attributes.
+	 *
+	 * Removes all attributes which aren't one of the allowed attributes.
+	 *
+	 * @param array &$request  The current request
+	 */
+	public function process(&$request) {
+		assert('is_array($request)');
+		assert('array_key_exists("Attributes", $request)');
+
+		$attributes =& $request['Attributes'];
+
+		foreach($attributes as $name => $values) {
+			if(!in_array($name, $this->allowedAttributes, TRUE)) {
+				unset($attributes[$name]);
+			}
+		}
+
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/modules/core/lib/Auth/Process/AttributeMap.php b/modules/core/lib/Auth/Process/AttributeMap.php
new file mode 100644
index 0000000000000000000000000000000000000000..9631b0b9e90bcde38c74f1c608bfcb15c66abe85
--- /dev/null
+++ b/modules/core/lib/Auth/Process/AttributeMap.php
@@ -0,0 +1,108 @@
+<?php
+
+/**
+ * Attribute filter for renaming attributes.
+ *
+ * Example 1 - apply map stored in attributemap/defaultmap.php:
+ * <code>
+ * 'authproc' => array(
+ *   array('core:AttributeMap', 'defaultmaps'),
+ *   ),
+ * </code>
+ *
+ * Example 2 - rename attributes 'mail' and 'uid' to 'email' and 'user':
+ * <code>
+ * 'authproc' => array(
+ *   array('core:AttributeMap', 'mail' => 'email', 'uid' => 'user'),
+ *   ),
+ * </code>
+ *
+ * @author Olav Morken, UNINETT AS.
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class sspmod_core_Auth_Process_AttributeMap extends SimpleSAML_Auth_ProcessingFilter {
+
+	/**
+	 * Assosiative array with the mappings of attribute names.
+	 */
+	private $map = array();
+
+
+	/**
+	 * 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)');
+
+		foreach($config as $origName => $newName) {
+			if(is_int($origName)) {
+				/* No index given - this is a map file. */
+				$this->loadMapFile($newName);
+				continue;
+			}
+
+			if(!is_string($origName)) {
+				throw new Exception('Invalid attribute name: ' . var_export($origName, TRUE));
+			}
+
+			if(!is_string($newName)) {
+				throw new Exception('Invalid attribute name: ' . var_export($newName, TRUE));
+			}
+
+			$this->map[$origName] = $newName;
+		}
+	}
+
+
+	/**
+	 * Loads and merges in a file with a attribute map.
+	 *
+	 * @param string $fileName  Name of attribute map file. Expected to be in the attributenamemapdir.
+	 */
+	private function loadMapFile($fileName) {
+		$config = SimpleSAML_Configuration::getInstance();
+		$filePath = $config->getPathValue('attributenamemapdir') . $fileName . '.php';
+
+		if(!file_exists($filePath)) {
+			throw new Exception('Could not find attributemap file: ' . $filePath);
+		}
+
+		$attributemap = NULL;
+		include($filePath);
+		if(!is_array($attributemap)) {
+			throw new Exception('Attribute map file "' . $filePath . '" didn\'t define an attribute map.');
+		}
+
+		$this->map = array_merge($this->map, $attributemap);
+	}
+
+
+	/**
+	 * Apply filter to rename attributes.
+	 *
+	 * @param array &$request  The current request
+	 */
+	public function process(&$request) {
+		assert('is_array($request');
+		assert('array_key_exists("Attributes", $request)');
+
+		$attributes =& $request['Attributes'];
+
+		foreach($attributes as $name => $values) {
+			if(array_key_exists($name, $this->map)) {
+				$attributes[$this->map[$name]] = $values;
+				unset($attributes[$name]);
+			}
+		}
+
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/modules/exampleauth/lib/Auth/Process/RedirectTest.php b/modules/exampleauth/lib/Auth/Process/RedirectTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..02a4220d6a3023c7dc9f32c2bb95f35cb547914d
--- /dev/null
+++ b/modules/exampleauth/lib/Auth/Process/RedirectTest.php
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * A simple processing filter for testing that redirection works as it should.
+ *
+ */
+class sspmod_exampleauth_Auth_Process_RedirectTest extends SimpleSAML_Auth_ProcessingFilter {
+
+
+	/**
+	 * Initialize processing of the redirect test.
+	 *
+	 * @param array &$state  The state we should update.
+	 */
+	public function process(&$state) {
+		assert('is_array($state)');
+		assert('array_key_exists("Attributes", $state)');
+
+		/* To check whether the state is saved correctly. */
+		$state['Attributes']['RedirectTest1'] = array('OK');
+
+		/* Save state and redirect. */
+		$id = SimpleSAML_Auth_State::saveState($state, 'exampleauth:redirectfilter-test');
+		$url = SimpleSAML_Module::getModuleURL('exampleauth/redirecttest.php');
+		SimpleSAML_Utilities::redirect($url, array('StateId' => $id));
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/modules/exampleauth/www/redirecttest.php b/modules/exampleauth/www/redirecttest.php
new file mode 100644
index 0000000000000000000000000000000000000000..39a1b5450b381dbc7c5976c3be4e18f27894e342
--- /dev/null
+++ b/modules/exampleauth/www/redirecttest.php
@@ -0,0 +1,22 @@
+<?php
+
+/**
+ * Request handler for redirect filter test.
+ *
+ * @author Olav Morken, UNINETT AS.
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+
+if (!array_key_exists('StateId', $_REQUEST)) {
+	throw new SimpleSAML_Error_BadRequest('Missing required StateId query parameter.');
+}
+
+$id = $_REQUEST['StateId'];
+$state = SimpleSAML_Auth_State::loadState($id, 'exampleauth:redirectfilter-test');
+
+$state['Attributes']['RedirectTest2'] = array('OK');
+
+SimpleSAML_Auth_ProcessingChain::resumeProcessing($state);
+
+?>
\ No newline at end of file
diff --git a/www/saml2/idp/SSOService.php b/www/saml2/idp/SSOService.php
index 55d53a06230acb697824946f10b0fa77ef2f3e74..b71d18711c8229ca793c9d74abbffeffa2d9433b 100644
--- a/www/saml2/idp/SSOService.php
+++ b/www/saml2/idp/SSOService.php
@@ -139,6 +139,13 @@ if (isset($_GET['SAMLRequest'])) {
 		SimpleSAML_Utilities::fatalError($session->getTrackID(), 'CACHEAUTHNREQUEST', $exception);
 	}
 	
+} elseif(isset($_REQUEST[SimpleSAML_Auth_ProcessingChain::AUTHPARAM])) {
+
+	/* Resume from authentication processing chain. */
+	$authProcId = $_REQUEST[SimpleSAML_Auth_ProcessingChain::AUTHPARAM];
+	$authProcState = SimpleSAML_Auth_ProcessingChain::fetchProcessedState($authProcId);
+	$requestcache = $authProcState['core:saml20-idp:requestcache'];
+
 } else {
 	SimpleSAML_Utilities::fatalError($session->getTrackID(), 'SSOSERVICEPARAMS');
 }
@@ -270,6 +277,34 @@ if($needAuth && !$isPassive) {
 				
 		$filteredattributes = $afilter->getAttributes();
 		
+
+		/* Authentication processing operations. */
+		if (array_key_exists('AuthProcState', $requestcache)) {
+			/* Processed earlier, saved in requestcache. */
+			$authProcState = $requestcache['AuthProcState'];
+
+		} elseif (isset($authProcState)) {
+			/* Returned from redirect during processing. */
+			$requestcache['AuthProcState'] = $authProcState;
+
+		} else {
+			/* Not processed. */
+			$pc = new SimpleSAML_Auth_ProcessingChain($idpmetadata, $spmetadata);
+
+			$authProcState = array(
+				'core:saml20-idp:requestcache' => $requestcache,
+				'ReturnURL' => SimpleSAML_Utilities::selfURLNoQuery(),
+				'Attributes' => $filteredattributes,
+				'Destination' => $spmetadata,
+				'Source' => $idpmetadata,
+				);
+
+			$pc->processState($authProcState);
+
+			$requestcache['AuthProcState'] = $authProcState;
+		}
+
+		$filteredattributes = $authProcState['Attributes'];
 		
 		
 		
diff --git a/www/shib13/idp/SSOService.php b/www/shib13/idp/SSOService.php
index 495f20d48cc25f06106656906efbd82eea669279..0aecf9ca2b6ad6e9b228a090e9bfb9ed826077f4 100644
--- a/www/shib13/idp/SSOService.php
+++ b/www/shib13/idp/SSOService.php
@@ -91,6 +91,12 @@ if (isset($_GET['shire'])) {
 		SimpleSAML_Utilities::fatalError($session->getTrackID(), 'CACHEAUTHNREQUEST', $exception);
 	}
 	
+} elseif(isset($_REQUEST[SimpleSAML_Auth_ProcessingChain::AUTHPARAM])) {
+
+	/* Resume from authentication processing chain. */
+	$authProcId = $_REQUEST[SimpleSAML_Auth_ProcessingChain::AUTHPARAM];
+	$authProcState = SimpleSAML_Auth_ProcessingChain::fetchProcessedState($authProcId);
+	$requestcache = $authProcState['core:shib13-idp:requestcache'];
 
 } else {
 	SimpleSAML_Utilities::fatalError($session->getTrackID(), 'SSOSERVICEPARAMS');
@@ -184,6 +190,35 @@ if (!$session->isAuthenticated($authority) ) {
 		
 		$filteredattributes = $afilter->getAttributes();
 		
+
+		/* Authentication processing operations. */
+		if (array_key_exists('AuthProcState', $requestcache)) {
+			/* Processed earlier, saved in requestcache. */
+			$authProcState = $requestcache['AuthProcState'];
+
+		} elseif (isset($authProcState)) {
+			/* Returned from redirect during processing. */
+			$requestcache['AuthProcState'] = $authProcState;
+
+		} else {
+			/* Not processed. */
+			$pc = new SimpleSAML_Auth_ProcessingChain($idpmetadata, $spmetadata);
+
+			$authProcState = array(
+				'core:shib13-idp:requestcache' => $requestcache,
+				'ReturnURL' => SimpleSAML_Utilities::selfURLNoQuery(),
+				'Attributes' => $filteredattributes,
+				'Destination' => $spmetadata,
+				'Source' => $idpmetadata,
+				);
+
+			$pc->processState($authProcState);
+
+			$requestcache['AuthProcState'] = $authProcState;
+		}
+
+		$filteredattributes = $authProcState['Attributes'];
+
 		
 		/*
 		 * Dealing with attribute release consent.