diff --git a/lib/SimpleSAML/XML/SAML20/AuthnResponse.php b/lib/SimpleSAML/XML/SAML20/AuthnResponse.php
index 851d8aa69211371e5d02c16ca6ab68f8ab3bd60e..dabd787f0e043e0912c1812bf66d813b13b07b45 100644
--- a/lib/SimpleSAML/XML/SAML20/AuthnResponse.php
+++ b/lib/SimpleSAML/XML/SAML20/AuthnResponse.php
@@ -34,38 +34,55 @@ class SimpleSAML_XML_SAML20_AuthnResponse extends SimpleSAML_XML_AuthnResponse {
 	/**
 	 * This variable contains an XML validator for this message.
 	 */
-	private $validator = null;
-	
+	private $validator = NULL;
 
-	function __construct(SimpleSAML_Configuration $configuration, SimpleSAML_Metadata_MetaDataStorageHandler $metadatastore) {
-		$this->configuration = $configuration;
-		$this->metadata = $metadatastore;
-	}
 
-	
-	public function validate() {
-	
-		$dom = $this->getDOM();
+	/**
+	 * This varaible contains the entitiyid of the IdP which issued this message.
+	 */
+	private $issuer = NULL;
 
-		/* Validate the signature. */
-		$this->validator = new SimpleSAML_XML_Validator($dom, 'ID');
 
-		// Get the issuer of the response.
-		$issuer = $this->getIssuer();
+	/**
+	 * This variable contains the NameID of this subject. It is an associative array with
+	 * two keys:
+	 * - 'Format'  The type of the NameID.
+	 * - 'value'   Tha value of the NameID.
+	 *
+	 * This variable will be set by the processSubject function. A exception will be thrown if the response
+	 * contains two different NameIDs.
+	 */
+	private $nameid = NULL;
 
-		/* Get the metadata of the issuer. */
-		$md = $this->metadata->getMetaData($issuer, 'saml20-idp-remote');
 
-		/* Get fingerprint for the certificate of the issuer. */
-		$issuerFingerprint = $md['certFingerprint'];
+	/**
+	 * This variable contains the SessionIndex, as set by a AuthnStatement element in an assertion.
+	 */
+	private $sessionIndex = NULL;
+
+
+	/**
+	 * This associative array contains the attribute we extract from the response.
+	 */
+	private $attributes = array();
 
-		/* Validate the fingerprint. */
-		$this->validator->validateFingerprint($issuerFingerprint);
 
-		return true;
+	function __construct(SimpleSAML_Configuration $configuration, SimpleSAML_Metadata_MetaDataStorageHandler $metadatastore) {
+		$this->configuration = $configuration;
+		$this->metadata = $metadatastore;
 	}
 
 
+	/* The following methods aren't used anymore. They are included because it is required by inheritance.
+	 * TODO: Remove them.
+	 */
+	public function validate() { throw new Exception('TODO!'); }
+	public function createSession() { throw new Exception('TODO!'); }
+	public function getAttributes() { throw new Exception('TODO!'); }
+	public function getIssuer() { throw new Exception('TODO!'); }
+	public function getNameID() { throw new Exception('TODO!'); }
+
+
 	/**
 	 * This function runs an xPath query on this authentication response.
 	 *
@@ -94,128 +111,314 @@ class SimpleSAML_XML_SAML20_AuthnResponse extends SimpleSAML_XML_AuthnResponse {
 	}
 
 
-	public function createSession() {
-	
-		SimpleSAML_Session::init(true, 'saml2');
-		$session = SimpleSAML_Session::getInstance();
-		$session->setAttributes($this->getAttributes());
-			
-		$session->setNameID($this->getNameID());
-		$session->setSessionIndex($this->getSessionIndex());
-		$session->setIdP($this->getIssuer());
-		
-		return $session;
+	/**
+	 * This function checks if the user has added the given id to 'saml2.relaxvalidation'
+	 * in the saml2-idp-remote configuration.
+	 *
+	 * @param $id   The id which identifies a part of the verification which may be relaxed.
+	 * @return TRUE if this id is added to the list, FALSE if not.
+	 */
+	private function isValidationRelaxed($id) {
+
+		assert('is_string($id)');
+		assert('$this->issuer != NULL');
+
+		/* Get the metadata of the issuer. */
+		$md = $this->metadata->getMetaData($this->issuer, 'saml20-idp-remote');
+
+		if(!array_key_exists('saml2.relaxvalidation', $md)) {
+			/* The user hasn't added a saml2.relaxvalidation option. */
+			return FALSE;
+		}
+
+		$rv = $md['saml2.relaxvalidation'];
+		if(!is_array($rv)) {
+			throw new Exception('saml2.relaxvalidation must be an array.');
+		}
+
+		return in_array($id, $rv, TRUE);
 	}
-	
-	
-	// TODO: Not tested, but neigther is it used.
-	function getSessionIndex() {
-		$token = $this->getDOM();
-		if ($token instanceof DOMDocument) {
-			$xPath = new DOMXpath($token);
-			$xPath->registerNamespace('mysaml', self::SAML2_ASSERT_NS);
-			$xPath->registerNamespace('mysamlp', self::SAML2_PROTOCOL_NS);
-	
-			$query = '/mysamlp:Response/mysaml:Assertion/mysaml:AuthnStatement';
-			$nodelist = $xPath->query($query);
-			if ($node = $nodelist->item(0)) {
-				return $node->getAttribute('SessionIndex');
-			}
+
+
+	/**
+	 * This function finds the issuer of this response. It will first search the Response element,
+	 * and if it isn't found there, it will search all Assertion elements.
+	 */
+	private function findIssuer() {
+
+		/* First check the Response element. */
+		$issuer = $this->doXPathQuery('/samlp:Response/saml:Issuer')->item(0);
+		if($issuer !== NULL) {
+			return $issuer->textContent;
 		}
-		return NULL;
+
+		/* Then we search the Assertion elements. */
+		$issuers = $this->doXPathQuery('/samlp:Response/saml:Assertion/saml:Issuer');
+
+		if($issuers->length === 0) {
+			throw new Exception('Unable to determine the issuer of this SAML2 AuthnResponse message.');
+		}
+
+		/* Since all Issuer elements should be equal in this version of simpleSAMLphp, we pick
+		 * the first Issuer element we find.
+		 */
+		return $issuers->item(0)->textContent;
 	}
 
+
+	/**
+	 * This function validates the signature element of this response. It will throw an exception
+	 * if it is unable to validate the signature.
+	 */
+	private function validateSignature() {
 	
-	public function getAttributes() {
+		$dom = $this->getDOM();
+
+		/* Validate the signature. */
+		$this->validator = new SimpleSAML_XML_Validator($dom, 'ID');
 
-		$md = $this->metadata->getMetadata($this->getIssuer(), 'saml20-idp-remote');
+		/* Get the metadata of the issuer. */
+		$md = $this->metadata->getMetaData($this->issuer, 'saml20-idp-remote');
 
-		$base64 = isset($md['base64attributes']) ? $md['base64attributes'] : false;
+		/* Get fingerprint for the certificate of the issuer. */
+		$issuerFingerprint = $md['certFingerprint'];
+
+		/* Validate the fingerprint. */
+		$this->validator->validateFingerprint($issuerFingerprint);
+	}
+
+
+	/**
+	 * This function processes a Subject node. It will throw
+	 * an Exception if the subject cannot be confirmed. On successful verification,
+	 * the data stored about this subject will be saved.
+	 */
+	private function processSubject($subject) {
+
+		/* We currently require urn:oasis:names:tc:SAML:2.0:cm:bearer subject confirmation. */
+		$bearerValidated = false;
 
-		$attributes = array();
-		$token = $this->getDOM();
+		/* Iterate over the SubjectConfirmation nodes, looking for it. */
+		foreach($this->doXPathQuery('saml:SubjectConfirmation', $subject) as $subjectConfirmation) {
+			$method = $subjectConfirmation->getAttributeNode('Method');
+			if($method === NULL) {
+				throw new Exception('SubjectConfirmation is missing the required Method attribute.');
+			}
+			if($method->value !== 'urn:oasis:names:tc:SAML:2.0:cm:bearer') {
+				throw new Exception('Unhandled SubjectConfirmationData: ' . $method->value);
+			}
+
+			$subjectConfirmationData = $this->doXPathQuery('saml:SubjectConfirmationData', $subjectConfirmation);
+			if($subjectConfirmationData === NULL) {
+				throw new Exception('Bearer confirmation node without verification data.');
+			}
 
-		if($this->validator === NULL) {
-			throw new Exception('Called getAttributes on a SAML2 AuthnResponse which hasn\'t been validated.');
+			/* TODO: Verify this subject. */
 		}
 
 
-		if ( !($token instanceof DOMDocument)) {
-			throw new Exception('Called getAttributes on a SAML2 AuthnResponse which doesn\'t contain a message.');
+		/* We expect the subject node to contain a NameID element which identifies this subject. */
+		$nameid = $this->doXPathQuery('saml:NameID', $subject)->item(0);
+		if($nameid === NULL) {
+			throw new Exception('Could not find the NameID node in a Subject node.');
 		}
 
-		$assertions = $this->doXPathQuery('/samlp:Response/saml:Assertion');
-		foreach($assertions as $assertion) {
+		$format = $nameid->getAttribute('Format');
+		$value = $nameid->textContent;
 
-			if(!$this->validator->isNodeValidated($assertion)) {
-				throw new Exception('A SAML2 AuthnResponse contained an Assertion which isn\'t verified by the signature.');
-			}
+		if($this->nameid === NULL) {
+			/* We haven't saved a nameID earlier. Save it now. */
+			$this->nameid = array('Format' => $format, 'value' => $value);
+			return;
+		}
 
-			foreach($this->doXPathQuery('saml:Conditions', $assertion) as $condition) {
+		/* We have saved a nameID earlier. Verify that this nameID is equal. */
+		if($this->nameid['Format'] !== $format || $this->nameid['value'] !== $value) {
+			throw new Exception('Multiple assertions with different nameIDs is unsupported by simpleSAMLphp');
+		}
+	}
 
-				$start = $condition->getAttribute("NotBefore");
-				$end = $condition->getAttribute("NotOnOrAfter");
 
-				if (! SimpleSAML_Utilities::checkDateConditions($start, $end)) {
-					throw new Exception("Date check failed (between $start and $end). Check if the clocks on the SP and IdP are synchronized. Alternatively you can get this message, when you move back in history or refresh an old page.");
-				}
+	/**
+	 * This function processes a Conditions node. It will throw an exception if any of the conditions
+	 * are invalid.
+	 */
+	private function processConditions($conditions) {
+
+		/* First verify the NotBefore and NotOnOrAfter attributes if they are present. */
+		$notBefore = $conditions->getAttribute("NotBefore");
+		$notOnOrAfter = $conditions->getAttribute("NotOnOrAfter");
+		if (! SimpleSAML_Utilities::checkDateConditions($notBefore, $notOnOrAfter)) {
+			throw new Exception('Date check failed (between ' . $notBefore . ' and ' . $notOnOrAfter . ').' .
+				' Check if the clocks on the SP and IdP are synchronized. Alternatively' .
+				' you can get this message, when you move back in history or refresh an old page.');
+		}
+
+
+		if($this->doXPathQuery('Condition', $conditions)->length > 0) {
+			if(!$this->isValidationRelaxed('unknowncondition')) {
+				throw new Exception('A Conditions node in a SAML2 AuthnResponse contained a' .
+					' Condition node. This is unsupported by simpleSAMLphp. To disable this' .
+					' check, add \'unknowncondition\' to the \'saml2.relaxvalidation\' list in' .
+					' \'saml2-idp-remote\'.');
 			}
+		}
 
-			foreach($this->doXPathQuery('saml:AttributeStatement/saml:Attribute/saml:AttributeValue', $assertion) as $attribute) {
 
-				$name = $attribute->parentNode->getAttribute('Name');
-				$value = $attribute->textContent;
+		$spEntityId = $this->metadata->getMetaDataCurrentEntityID('saml20-sp-hosted');
 
-				if(!array_key_exists($name, $attributes)) {
-					$attributes[$name] = array();
-				}
+		/* The specification says that every AudienceRestriction element must be valid, but only one
+		 * Audience element in each AudienceRestriction element must be valid.
+		 */
+		foreach($this->doXPathQuery('AudienceRestriction', $conditions) as $ar) {
 
-				if ($base64) {
-					foreach(explode('_', $value) AS $v) {
-						$attributes[$name][] = base64_decode($v);
-					}
-				} else {
-					$attributes[$name][] = $value;
+			$validAudience = false;
+			foreach($this->doXPathQuery('Audience', $ar) as $a) {
+				if($a->textContent === $spEntityId) {
+					$validAudience = true;
 				}
 			}
+			if(!$validAudience) {
+				throw new Exception('Could not verify audience of SAML2 AuthnResponse.');
+			}
 		}
 
-		return $attributes;
+		/* We ignore OneTimeUse and ProxyRestriction conditions. */
 	}
 
-	
-	public function getIssuer() {
-		$dom = $this->getDOM();
-		$issuer = null;
-		if ($issuerNodes = $dom->getElementsByTagName('Issuer')) {
-			if ($issuerNodes->length > 0) {
-				$issuer = $issuerNodes->item(0)->textContent;
+
+	/**
+	 * This function processes a AuthnStatement node. It will throw an exception if the statement is
+	 * invalid.
+	 */
+	private function processAuthnStatement($authnStatement) {
+		/* Extract the SessionIndex. */
+		$sessionIndex = $authnStatement->getAttributeNode('SessionIndex');
+		if($sessionIndex !== NULL) {
+			$sessionIndex = $sessionIndex->value;
+			if($this->sessionIndex === NULL) {
+				$this->sessionIndex = $sessionIndex;
+			} elseif($this->sessionIndex !== $sessionIndex) {
+				throw new Exception('Got two different session indexes in a SAML2 AuthnResponse.');
 			}
 		}
-		return $issuer;
 	}
-	
-	public function getNameID() {
-		
-		$dom = $this->getDOM();
-		$nameID = array();
-		
-		if ($dom instanceof DOMDocument) {
-			$xPath = new DOMXpath($dom);
-			$xPath->registerNamespace('mysaml', self::SAML2_ASSERT_NS);
-			$xPath->registerNamespace('mysamlp', self::SAML2_PROTOCOL_NS);
-	
-			$query = '/mysamlp:Response/mysaml:Assertion/mysaml:Subject/mysaml:NameID';
-			$nodelist = $xPath->query($query);
-			if ($node = $nodelist->item(0)) {
-
-				$nameID["value"] = $node->nodeValue;
-				//$nameID["NameQualifier"] = $node->getAttribute('NameQualifier');
-				//$nameID["SPNameQualifier"] = $node->getAttribute('SPNameQualifier');
-				$nameID["Format"] = $node->getAttribute('Format');
+
+
+	/**
+	 * This function processes a AttributeStatement node.
+	 */
+	private function processAttributeStatement($attributeStatement) {
+
+		$md = $this->metadata->getMetadata($this->issuer, 'saml20-idp-remote');
+		$base64 = isset($md['base64attributes']) ? $md['base64attributes'] : false;
+
+		foreach($this->doXPathQuery('saml:Attribute/saml:AttributeValue', $attributeStatement) as $attribute) {
+
+			$name = $attribute->parentNode->getAttribute('Name');
+			$value = $attribute->textContent;
+
+			if(!array_key_exists($name, $this->attributes)) {
+				$this->attributes[$name] = array();
+			}
+
+			if ($base64) {
+				foreach(explode('_', $value) AS $v) {
+					$this->attributes[$name][] = base64_decode($v);
+				}
+			} else {
+				$this->attributes[$name][] = $value;
 			}
 		}
-		return $nameID;
+	}
+
+
+	/**
+	 * This function processes a Assertion node. It will throw an exception if the assertion is invalid.
+	 */
+	private function processAssertion($assertion) {
+
+		/* Make sure that the assertion is signed. */
+		if(!$this->validator->isNodeValidated($assertion)) {
+			throw new Exception('A SAML2 AuthnResponse contained an Assertion which isn\'t verified by' .
+				' the signature.');
+		}
+
+		$subject = $this->doXPathQuery('saml:Subject', $assertion)->item(0);
+		if($subject === NULL) {
+			if(!$this->isValidationRelaxed('nosubject')) {
+				throw new Exception('Could not find required Subject information in a SAML2' .
+					' AuthnResponse. To disable this check, add \'nosubject\' to the' .
+					' \'saml2.relaxvalidation\' list in \'saml2-idp-remote\'.');
+			}
+		} else {
+			$this->processSubject($subject);
+		}
+
+		$conditions = $this->doXPathQuery('saml:Conditions', $assertion)->item(0);
+		if($conditions === NULL) {
+			if(!$this->isValidationRelaxed('noconditions')) {
+				throw new Exception('Could not find required Conditions node in a SAML2' .
+					' AuthnResponse. To disable this check, add \'noconditions\' to the' .
+					' \'saml2.relaxvalidation\' list in \'saml2-idp-remote\'.');
+			}
+		} else {
+			$this->processConditions($conditions);
+		}
+
+		$authnStatement = $this->doXPathQuery('saml:AuthnStatement', $assertion)->item(0);
+		if($authnStatement === NULL) {
+			if(!$this->isValidationRelaxed('noauthnstatement')) {
+				throw new Exception('Could not find required AuthnStatement node in a SAML2' .
+					' AuthnResponse. To disable this check, add \'noauthnstatement\' to the' .
+					' \'saml2.relaxvalidation\' list in \'saml2-idp-remote\'.');
+			}
+		} else {
+			$this->processAuthnStatement($authnStatement);
+		}
+
+		$attributeStatement = $this->doXPathQuery('saml:AttributeStatement', $assertion)->item(0);
+		if($attributeStatement === NULL) {
+			if(!$this->isValidationRelaxed('noattributestatement')) {
+				throw new Exception('Could not find required AttributeStatement in a SAML2' .
+					' AuthnResponse. To disable this check, add \'noattributestatement\' to the' .
+					' \'saml2.relaxvalidation\' list in \'saml2-idp-remote\'.');
+			}
+		} else {
+			$this->processAttributeStatement($attributeStatement);
+		}
+	}
+
+
+	/**
+	 * This function processes a response message and adds information from it to the
+	 * current session if it is valid. It throws an exception if it is invalid.
+	 */
+	public function process() {
+		/* Find the issuer of this response. */
+		$this->issuer = $this->findIssuer();
+
+		/* Validate the signature element. */
+		$this->validateSignature();
+
+		/* Process all assertions. */
+		$assertions = $this->doXPathQuery('/samlp:Response/saml:Assertion');
+		foreach($assertions as $assertion) {
+			$this->processAssertion($assertion);
+		}
+
+		if($this->nameid === NULL) {
+			throw new Exception('No nameID found in AuthnResponse.');
+		}
+
+		/* Update the session information */
+		SimpleSAML_Session::init(true, 'saml2');
+		$session = SimpleSAML_Session::getInstance();
+
+		$session->setAttributes($this->attributes);
+		$session->setNameID($this->nameid);
+		$session->setSessionIndex($this->sessionIndex);
+		$session->setIdP($this->issuer);
 	}
 
 
diff --git a/metadata-templates/saml20-idp-remote.php b/metadata-templates/saml20-idp-remote.php
index f97c08c14a7101a9394e6f047ef5b621a13415ab..a9e934c7d11f0bd82b68c025180f2768b8c4e2ad 100644
--- a/metadata-templates/saml20-idp-remote.php
+++ b/metadata-templates/saml20-idp-remote.php
@@ -31,6 +31,25 @@ $metadata = array(
 		'request.signing' => false,
 		'certificate' => "idp.example.org.crt",
 
+		/*
+		 * It is possible to relax some parts of the validation of SAML2 messages.
+		 * To relax a part, add the id to the 'saml2.relaxvalidation' array.
+		 *
+		 * Valid ids:
+		 * - 'unknowncondition'         Disables errors when encountering unknown <Condition> nodes.
+		 * - 'nosubject'                Ignore missing <Subject> in <Assertion>.
+		 * - 'noconditions'             Ignore missing <Conditions> in <Assertion>.
+		 * - 'noauthnstatement'         Ignore missing <AuthnStatement> in <Assertion>.
+		 * - 'noattributestatement'     Ignore missing <AttributeStatement> in <Assertion>.
+		 *
+		 * Example:
+		 * 'saml2.relaxvalidation' => array('unknowncondition', 'noattributestatement'),
+		 *
+		 * Default:
+		 * 'saml2.relaxvalidation' => array(),
+		 */
+		'saml2.relaxvalidation' => array(),
+
 	),
 
 
diff --git a/www/saml2/sp/AssertionConsumerService.php b/www/saml2/sp/AssertionConsumerService.php
index 2101498deac125adb874d2f8eb2990f8e747ee3d..650ee213d1285f0e41160f2966fda89f86c5748a 100644
--- a/www/saml2/sp/AssertionConsumerService.php
+++ b/www/saml2/sp/AssertionConsumerService.php
@@ -41,25 +41,17 @@ try {
 
 	$binding = new SimpleSAML_Bindings_SAML20_HTTPPost($config, $metadata);
 	$authnResponse = $binding->decodeResponse($_POST);
-	
-	$authnResponse->validate();
-	
-	$session = $authnResponse->createSession();
-	if (isset($session)) {
-		
-		$attributes = $session->getAttributes();
 
-		$logger->log(LOG_NOTICE, $session->getTrackID(), 'SAML2.0', 'SP.AssertionConsumerService', 'AuthnResponse', '-', 
-			'Successfully created local session from Authentication Response');
+	$authnResponse->process();
+
+	$logger->log(LOG_NOTICE, $session->getTrackID(), 'SAML2.0', 'SP.AssertionConsumerService', 'AuthnResponse', '-',
+		     'Successfully created local session from Authentication Response');
 	
-		$relayState = $authnResponse->getRelayState();
-		if (isset($relayState)) {
-			SimpleSAML_Utilities::redirect($relayState);
-		} else {
-			throw new Exception('Could not find RelayState parameter, you are stucked here.');
-		}
+	$relayState = $authnResponse->getRelayState();
+	if (isset($relayState)) {
+		SimpleSAML_Utilities::redirect($relayState);
 	} else {
-		throw new Exception('Unkown error. Could not get session.');
+		throw new Exception('Could not find RelayState parameter, you are stuck here.');
 	}
 
 } catch(Exception $exception) {