diff --git a/flask_saml2/sp/idphandler.py b/flask_saml2/sp/idphandler.py
index 50788ad..7c640c1 100644
--- a/flask_saml2/sp/idphandler.py
+++ b/flask_saml2/sp/idphandler.py
@@ -257,8 +257,9 @@ def validate_response(self, response: ResponseParser):
# Validate the AudienceRestriction elements, if they exist
audiences = response._xpath(response.conditions, './saml:AudienceRestriction/saml:Audience')
entity_id = self.sp.get_sp_entity_id()
+ adnc = [el.text for el in audiences]
if len(audiences) and not any(el.text == entity_id for el in audiences):
- raise CannotHandleAssertion("No valid AudienceRestriction found")
+ raise CannotHandleAssertion(f"No valid AudienceRestriction found: {adnc}, {entity_id}")
def format_datetime(self, value: datetime.datetime) -> str:
"""
diff --git a/flask_saml2/sp/parser.py b/flask_saml2/sp/parser.py
index 2dd63a0..e40046a 100644
--- a/flask_saml2/sp/parser.py
+++ b/flask_saml2/sp/parser.py
@@ -1,4 +1,4 @@
-from typing import Mapping, Optional
+from typing import List, Mapping, Optional, Union
from flask_saml2.types import XmlNode
from flask_saml2.utils import cached_property
@@ -51,10 +51,20 @@ def nameid_format(self) -> str:
return self._xpath(self.subject, 'saml:NameID/@Format')[0]
@cached_property
- def attributes(self) -> Mapping[str, str]:
+ def attributes(self) -> Mapping[str, Union[str, List[str]]]:
attributes = self._xpath(self.assertion, 'saml:AttributeStatement/saml:Attribute')
- return {el.get('Name'): self._xpath(el, 'saml:AttributeValue')[0].text
- for el in attributes}
+ ret = {}
+ for el in attributes:
+ name = el.get('Name')
+ attrs = self._xpath(el, 'saml:AttributeValue')
+ if len(attrs) == 1:
+ ret[name] = attrs[0].text
+ else:
+ vals = []
+ for a in attrs:
+ vals.append(a.text)
+ ret[name] = vals
+ return ret
@cached_property
def conditions(self) -> Optional[XmlNode]:
diff --git a/flask_saml2/sp/views.py b/flask_saml2/sp/views.py
index 1c116b6..fd1c6ac 100644
--- a/flask_saml2/sp/views.py
+++ b/flask_saml2/sp/views.py
@@ -81,15 +81,21 @@ def post(self):
saml_request = request.form['SAMLResponse']
relay_state = request.form['RelayState']
+ errors = []
+
for handler in self.sp.get_idp_handlers():
try:
response = handler.get_response_parser(saml_request)
auth_data = handler.get_auth_data(response)
return self.sp.login_successful(auth_data, relay_state)
- except CannotHandleAssertion:
- continue
+ except CannotHandleAssertion as e:
+ errors.append(e)
except UserNotAuthorized:
return self.sp.render_template('flask_saml2_sp/user_not_authorized.html')
+ error_string = ""
+ for e in errors:
+ error_string = f"{error_string} - {e}
"
+ return f"Could not log in for various reasons, here is a list of errors encountered:
{error_string}"
class Metadata(SAML2View):