diff --git a/lib/SimpleSAML/IdP.php b/lib/SimpleSAML/IdP.php
new file mode 100644
index 0000000000000000000000000000000000000000..1be653d634ed6630b744ddaacbbb9aa326a3c557
--- /dev/null
+++ b/lib/SimpleSAML/IdP.php
@@ -0,0 +1,274 @@
+<?php
+
+/**
+ * IdP class.
+ *
+ * This class implements the various functions used by IdP.
+ *
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class SimpleSAML_IdP {
+
+	/**
+	 * A cache for resolving IdP id's.
+	 *
+	 * @var array
+	 */
+	private static $idpCache = array();
+
+
+	/**
+	 * The identifier for this IdP.
+	 *
+	 * @var string
+	 */
+	private $id;
+
+
+	/**
+	 * The configuration for this IdP.
+	 *
+	 * @var SimpleSAML_Configuration
+	 */
+	private $config;
+
+
+	/**
+	 * Initialize an IdP.
+	 *
+	 * @param string $id  The identifier of this IdP.
+	 */
+	private function __construct($id) {
+		assert('is_string($id)');
+
+		$this->id = $id;
+
+		$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+		if (substr($id, 0, 6) === 'saml2:') {
+			$this->config = $metadata->getMetaDataConfig(substr($id, 6), 'saml20-idp-hosted');
+		} elseif (substr($id, 0, 6) === 'saml1:') {
+			$this->config = $metadata->getMetaDataConfig(substr($id, 6), 'shib13-idp-hosted');
+		} else {
+			assert(FALSE);
+		}
+
+	}
+
+
+	/**
+	 * Retrieve an IdP by ID.
+	 *
+	 * @param string $id  The identifier of the IdP.
+	 * @return SimpleSAML_IdP  The IdP.
+	 */
+	public static function getById($id) {
+		assert('is_string($id)');
+
+		if (isset(self::$idpCache[$id])) {
+			return self::$idpCache[$id];
+		}
+
+		$idp = new self($id);
+		self::$idpCache[$id] = $idp;
+		return $idp;
+	}
+
+
+	/**
+	 * Retrieve the IdP "owning" the state.
+	 *
+	 * @param array &$state  The state array.
+	 * @return SimpleSAML_IdP  The IdP.
+	 */
+	public static function getByState(array &$state) {
+		assert('isset($state["core:IdP"])');
+
+		return self::getById($state['core:IdP']);
+	}
+
+
+	/**
+	 * Retrieve the configuration for this IdP.
+	 *
+	 * @return SimpleSAML_Configuration  The configuration object.
+	 */
+	public function getConfig() {
+
+		return $this->config;
+	}
+
+
+	/**
+	 * Is the current user authenticated?
+	 *
+	 * @return bool  TRUE if the user is authenticated, FALSE if not.
+	 */
+	public function isAuthenticated() {
+
+		$session = SimpleSAML_Session::getInstance();
+
+		$authority = $this->config->getString('auth');
+		if ($session->isValid($authority)) {
+			return TRUE;
+		}
+
+		/* Maybe the 'auth' option didn't point to an authentication source? */
+		if (SimpleSAML_Auth_Source::getById($authority) !== NULL) {
+			/* It was an authentication source - the user is therefore not authenticated. */
+			return FALSE;
+		}
+
+		/* It wasn't an authentication source. */
+		$authority = SimpleSAML_Utilities::getAuthority($this->config->toArray());
+		return $session->isValid($authority);
+	}
+
+
+	/**
+	 * Called after authproc has run.
+	 *
+	 * @param array &$state  The authentication request state array.
+	 */
+	public static function postAuthProc(array &$state) {
+		assert('is_callable($state["Responder"])');
+
+		if (isset($state['core:SP'])) {
+			$session = SimpleSAML_Session::getInstance();
+			$session->setData('core:idp-ssotime', $state['core:IdP'] . ';' . $state['core:SP'],
+				time(), SimpleSAML_Session::DATA_TIMEOUT_LOGOUT);
+		}
+
+		call_user_func($state['Responder'], $state);
+		assert('FALSE');
+	}
+
+
+	/**
+	 * The user is authenticated.
+	 *
+	 * @param array &$state  The authentication request state arrray.
+	 */
+	public static function postAuth(array &$state) {
+
+		$idp = SimpleSAML_IdP::getByState($state);
+
+		if (!$idp->isAuthenticated()) {
+			throw new SimpleSAML_Error_Exception('Not authenticated.');
+		}
+
+		$session = SimpleSAML_Session::getInstance();
+		$state['Attributes'] = $session->getAttributes();
+
+		if (isset($state['SPMetadata'])) {
+			$spMetadata = $state['SPMetadata'];
+		} else {
+			$spMetadata = array();
+		}
+
+		if (isset($state['core:SP'])) {
+			$previousSSOTime = $session->getData('core:idp-ssotime', $state['core:IdP'] . ';' . $state['core:SP']);
+			if ($previousSSOTime !== NULL) {
+				$state['PreviousSSOTimestamp'] = $previousSSOTime;
+			}
+		}
+
+		$idpMetadata = $idp->getConfig()->toArray();
+
+		$pc = new SimpleSAML_Auth_ProcessingChain($idpMetadata, $spMetadata, 'idp');
+
+		$state['ReturnCall'] = array('SimpleSAML_IdP', 'postAuthProc');
+		$state['Destination'] = $spMetadata;
+		$state['Source'] = $idpMetadata;
+
+		$pc->processState($state);
+
+		self::postAuthProc($state);
+	}
+
+
+	/**
+	 * Authenticate the user.
+	 *
+	 * This function authenticates the user.
+	 *
+	 * @param array &$state  The authentication request state.
+	 */
+	private function authenticate(array &$state) {
+
+		if (isset($state['isPassive']) && (bool)$state['isPassive']) {
+			throw new SimpleSAML_Error_NoPassive('Passive authentication not supported.');
+		}
+
+		$auth = $this->config->getString('auth');
+		$authSource = SimpleSAML_Auth_Source::getById($auth);
+		if ($authSource === NULL) {
+			$config = SimpleSAML_Configuration::getInstance();
+			$authurl = '/' . $config->getBaseURL() . $auth;
+
+			$authnRequest = array(
+				'IsPassive' => isset($state['isPassive']) ? $state['isPassive'] : FALSE,
+				'ForceAuthn' => isset($state['ForceAuthn']) ? $state['ForceAuthn'] : FALSE,
+				'State' => $state,
+			);
+
+			$authId = SimpleSAML_Utilities::generateID();
+			$session = SimpleSAML_Session::getInstance();
+			$session->setAuthnRequest('saml2', $authId, $authnRequest);
+
+			$relayState = SimpleSAML_Module::getModuleURL('core/idp/resumeauth.php', array('RequestID' => $authId));
+
+			SimpleSAML_Utilities::redirect($authurl, array(
+				'RelayState' => $relayState,
+				'AuthId' => $authId,
+				'protocol' => 'saml2',
+			));
+		}
+
+		$state['IdPMetadata'] = $this->getConfig()->toArray();
+		SimpleSAML_Auth_Default::initLogin($auth, array('SimpleSAML_IdP', 'postAuth'), NULL, $state);
+	}
+
+
+	/**
+	 * Process authentication requests.
+	 *
+	 * @param array &$state  The authentication request state.
+	 */
+	public function handleAuthenticationRequest(array &$state) {
+		assert('isset($state["Responder"])');
+
+		$state['core:IdP'] = $this->id;
+
+		if (isset($state['SPMetadata']['entityid'])) {
+			$spEntityId = $state['SPMetadata']['entityid'];
+		} elseif (isset($state['SPMetadata']['entityID'])) {
+			$spEntityId = $state['SPMetadata']['entityID'];
+		} else {
+			$spEntityId = NULL;
+		}
+		$state['core:SP'] = $spEntityId;
+
+		/* First, check whether we need to authenticate the user. */
+		if (isset($state['ForceAuthn']) && (bool)$state['ForceAuthn']) {
+			/* Force authentication is in effect. */
+			$needAuth = TRUE;
+		} else {
+			$needAuth = !$this->isAuthenticated();
+		}
+
+		try {
+			if ($needAuth) {
+				$this->authenticate($state);
+				assert('FALSE');
+			}
+			$this->postAuth($state);
+		} catch (SimpleSAML_Error_Exception $e) {
+			SimpleSAML_Auth_State::throwException($state, $e);
+		} catch (Exception $e) {
+			$e = new SimpleSAML_Error_UnserializableException($e);
+			SimpleSAML_Auth_State::throwException($state, $e);
+		}
+	}
+
+}
diff --git a/modules/core/www/idp/resumeauth.php b/modules/core/www/idp/resumeauth.php
new file mode 100644
index 0000000000000000000000000000000000000000..5bf4a394e556f0a813cf310667250ef78b291974
--- /dev/null
+++ b/modules/core/www/idp/resumeauth.php
@@ -0,0 +1,15 @@
+<?php
+
+if (isset($_REQUEST['RequestID'])) {
+	/* Backwards-compatibility with old authentication pages. */
+	$session = SimpleSAML_Session::getInstance();
+	$requestcache = $session->getAuthnRequest('saml2', (string)$_REQUEST['RequestID']);
+	if (!$requestcache) {
+		throw new Exception('Could not retrieve cached RequestID = ' . $authId);
+	}
+	$state = $requestcache['State'];
+	SimpleSAML_IdP::postAuth($state);
+
+} else {
+	throw new SimpleSAML_Error_BadRequest('Missing required URL parameter.');
+}