/// <summary> /// Get the IdP Logout Response and extract metadata to the returned DTO class /// </summary> /// <param name="base64Response"></param> /// <returns></returns> public static IdpLogoutResponse GetLogoutResponse(string base64Response) { const string VALUE_NOT_AVAILABLE = "N/A"; string idpResponse; if (String.IsNullOrEmpty(base64Response)) { throw new ArgumentNullException("The base64Response parameter can't be null or empty."); } try { idpResponse = Encoding.UTF8.GetString(Convert.FromBase64String(base64Response)); } catch (Exception ex) { throw new ArgumentException("Unable to converto base64 response to ascii string.", ex); } try { // Verify signature XmlDocument xml = new XmlDocument { PreserveWhitespace = true }; xml.LoadXml(idpResponse); if (!XmlSigningHelper.VerifySignature(xml)) { throw new Exception("Unable to verify the signature of the IdP response."); } // Parse XML document XDocument xdoc = new XDocument(); xdoc = XDocument.Parse(idpResponse); string destination = VALUE_NOT_AVAILABLE; string id = VALUE_NOT_AVAILABLE; string inResponseTo = VALUE_NOT_AVAILABLE; DateTimeOffset issueInstant = DateTimeOffset.MinValue; string version = VALUE_NOT_AVAILABLE; string statusCodeValue = VALUE_NOT_AVAILABLE; string statusCodeInnerValue = VALUE_NOT_AVAILABLE; string statusMessage = VALUE_NOT_AVAILABLE; string statusDetail = VALUE_NOT_AVAILABLE; // Extract response metadata XElement responseElement = xdoc.Elements("{urn:oasis:names:tc:SAML:2.0:protocol}LogoutResponse").Single(); destination = responseElement.Attribute("Destination").Value; id = responseElement.Attribute("ID").Value; inResponseTo = responseElement.Attribute("InResponseTo").Value; issueInstant = DateTimeOffset.Parse(responseElement.Attribute("IssueInstant").Value); version = responseElement.Attribute("Version").Value; // Extract Issuer metadata string issuer = responseElement.Elements("{urn:oasis:names:tc:SAML:2.0:assertion}Issuer").Single().Value.Trim(); // Extract Status metadata XElement StatusElement = responseElement.Descendants("{urn:oasis:names:tc:SAML:2.0:protocol}Status").Single(); IEnumerable <XElement> statusCodeElements = StatusElement.Descendants("{urn:oasis:names:tc:SAML:2.0:protocol}StatusCode"); statusCodeValue = statusCodeElements.First().Attribute("Value").Value.Replace("urn:oasis:names:tc:SAML:2.0:status:", ""); statusCodeInnerValue = statusCodeElements.Count() > 1 ? statusCodeElements.Last().Attribute("Value").Value.Replace("urn:oasis:names:tc:SAML:2.0:status:", "") : VALUE_NOT_AVAILABLE; statusMessage = StatusElement.Elements("{urn:oasis:names:tc:SAML:2.0:protocol}StatusMessage").SingleOrDefault()?.Value ?? VALUE_NOT_AVAILABLE; statusDetail = StatusElement.Elements("{urn:oasis:names:tc:SAML:2.0:protocol}StatusDetail").SingleOrDefault()?.Value ?? VALUE_NOT_AVAILABLE; return(new IdpLogoutResponse(destination, id, inResponseTo, issueInstant, version, issuer, statusCodeValue, statusCodeInnerValue, statusMessage, statusDetail)); } catch (Exception ex) { throw new ArgumentException("Unable to read AttributeStatement attributes from SAML2 document.", ex); } }
/// <summary> /// Get the IdP Authn Response and extract metadata to the returned DTO class /// </summary> /// <param name="base64Response"></param> /// <returns>IdpSaml2Response</returns> public static IdpAuthnResponse GetAuthnResponse(string base64Response) { const string VALUE_NOT_AVAILABLE = "N/A"; string idpResponse; if (String.IsNullOrEmpty(base64Response)) { throw new ArgumentNullException("The base64Response parameter can't be null or empty."); } try { idpResponse = Encoding.UTF8.GetString(Convert.FromBase64String(base64Response)); } catch (Exception ex) { throw new ArgumentException("Unable to converto base64 response to ascii string.", ex); } try { // Verify signature XmlDocument xml = new XmlDocument { PreserveWhitespace = true }; xml.LoadXml(idpResponse); if (!XmlSigningHelper.VerifySignature(xml)) { throw new Exception("Unable to verify the signature of the IdP response."); } // Parse XML document XDocument xdoc = new XDocument(); xdoc = XDocument.Parse(idpResponse); string destination = VALUE_NOT_AVAILABLE; string id = VALUE_NOT_AVAILABLE; string inResponseTo = VALUE_NOT_AVAILABLE; DateTimeOffset issueInstant = DateTimeOffset.MinValue; string version = VALUE_NOT_AVAILABLE; string statusCodeValue = VALUE_NOT_AVAILABLE; string statusCodeInnerValue = VALUE_NOT_AVAILABLE; string statusMessage = VALUE_NOT_AVAILABLE; string statusDetail = VALUE_NOT_AVAILABLE; string assertionId = VALUE_NOT_AVAILABLE; DateTimeOffset assertionIssueInstant = DateTimeOffset.MinValue; string assertionVersion = VALUE_NOT_AVAILABLE; string assertionIssuer = VALUE_NOT_AVAILABLE; string subjectNameId = VALUE_NOT_AVAILABLE; string subjectConfirmationMethod = VALUE_NOT_AVAILABLE; string subjectConfirmationDataInResponseTo = VALUE_NOT_AVAILABLE; DateTimeOffset subjectConfirmationDataNotOnOrAfter = DateTimeOffset.MinValue; string subjectConfirmationDataRecipient = VALUE_NOT_AVAILABLE; DateTimeOffset conditionsNotBefore = DateTimeOffset.MinValue; DateTimeOffset conditionsNotOnOrAfter = DateTimeOffset.MinValue; string audience = VALUE_NOT_AVAILABLE; DateTimeOffset authnStatementAuthnInstant = DateTimeOffset.MinValue; string authnStatementSessionIndex = VALUE_NOT_AVAILABLE; Dictionary <string, string> spidUserInfo = new Dictionary <string, string>(); // Extract response metadata XElement responseElement = xdoc.Elements("{urn:oasis:names:tc:SAML:2.0:protocol}Response").Single(); destination = responseElement.Attribute("Destination").Value; id = responseElement.Attribute("ID").Value; inResponseTo = responseElement.Attribute("InResponseTo").Value; issueInstant = DateTimeOffset.Parse(responseElement.Attribute("IssueInstant").Value); version = responseElement.Attribute("Version").Value; // Extract Issuer metadata string issuer = responseElement.Elements("{urn:oasis:names:tc:SAML:2.0:assertion}Issuer").Single().Value.Trim(); // Extract Status metadata XElement StatusElement = responseElement.Descendants("{urn:oasis:names:tc:SAML:2.0:protocol}Status").Single(); IEnumerable <XElement> statusCodeElements = StatusElement.Descendants("{urn:oasis:names:tc:SAML:2.0:protocol}StatusCode"); statusCodeValue = statusCodeElements.First().Attribute("Value").Value.Replace("urn:oasis:names:tc:SAML:2.0:status:", ""); statusCodeInnerValue = statusCodeElements.Count() > 1 ? statusCodeElements.Last().Attribute("Value").Value.Replace("urn:oasis:names:tc:SAML:2.0:status:", "") : VALUE_NOT_AVAILABLE; statusMessage = StatusElement.Elements("{urn:oasis:names:tc:SAML:2.0:protocol}StatusMessage").SingleOrDefault()?.Value ?? VALUE_NOT_AVAILABLE; statusDetail = StatusElement.Elements("{urn:oasis:names:tc:SAML:2.0:protocol}StatusDetail").SingleOrDefault()?.Value ?? VALUE_NOT_AVAILABLE; if (statusCodeValue == "Success") { // Extract Assertion metadata XElement assertionElement = responseElement.Elements("{urn:oasis:names:tc:SAML:2.0:assertion}Assertion").Single(); assertionId = assertionElement.Attribute("ID").Value; assertionIssueInstant = DateTimeOffset.Parse(assertionElement.Attribute("IssueInstant").Value); assertionVersion = assertionElement.Attribute("Version").Value; assertionIssuer = assertionElement.Elements("{urn:oasis:names:tc:SAML:2.0:assertion}Issuer").Single().Value.Trim(); // Extract Subject metadata XElement subjectElement = assertionElement.Elements("{urn:oasis:names:tc:SAML:2.0:assertion}Subject").Single(); subjectNameId = subjectElement.Elements("{urn:oasis:names:tc:SAML:2.0:assertion}NameID").Single().Value.Trim(); subjectConfirmationMethod = subjectElement.Elements("{urn:oasis:names:tc:SAML:2.0:assertion}SubjectConfirmation").Single().Attribute("Method").Value; XElement confirmationDataElement = subjectElement.Descendants("{urn:oasis:names:tc:SAML:2.0:assertion}SubjectConfirmationData").Single(); subjectConfirmationDataInResponseTo = confirmationDataElement.Attribute("InResponseTo").Value; subjectConfirmationDataNotOnOrAfter = DateTimeOffset.Parse(confirmationDataElement.Attribute("NotOnOrAfter").Value); subjectConfirmationDataRecipient = confirmationDataElement.Attribute("Recipient").Value; // Extract Conditions metadata XElement conditionsElement = assertionElement.Elements("{urn:oasis:names:tc:SAML:2.0:assertion}Conditions").Single(); conditionsNotBefore = DateTimeOffset.Parse(conditionsElement.Attribute("NotBefore").Value); conditionsNotOnOrAfter = DateTimeOffset.Parse(conditionsElement.Attribute("NotOnOrAfter").Value); audience = conditionsElement.Descendants("{urn:oasis:names:tc:SAML:2.0:assertion}Audience").Single().Value.Trim(); // Extract AuthnStatement metadata XElement authnStatementElement = assertionElement.Elements("{urn:oasis:names:tc:SAML:2.0:assertion}AuthnStatement").Single(); authnStatementAuthnInstant = DateTimeOffset.Parse(authnStatementElement.Attribute("AuthnInstant").Value); authnStatementSessionIndex = authnStatementElement.Attribute("SessionIndex").Value; // Extract SPID user info foreach (XElement attribute in xdoc.Descendants("{urn:oasis:names:tc:SAML:2.0:assertion}AttributeStatement").Elements()) { spidUserInfo.Add( attribute.Attribute("Name").Value, attribute.Elements().Single(a => a.Name == "{urn:oasis:names:tc:SAML:2.0:assertion}AttributeValue").Value.Trim() ); } } return(new IdpAuthnResponse(destination, id, inResponseTo, issueInstant, version, issuer, statusCodeValue, statusCodeInnerValue, statusMessage, statusDetail, assertionId, assertionIssueInstant, assertionVersion, assertionIssuer, subjectNameId, subjectConfirmationMethod, subjectConfirmationDataInResponseTo, subjectConfirmationDataNotOnOrAfter, subjectConfirmationDataRecipient, conditionsNotBefore, conditionsNotOnOrAfter, audience, authnStatementAuthnInstant, authnStatementSessionIndex, spidUserInfo)); } catch (Exception ex) { throw new ArgumentException("Unable to read AttributeStatement attributes from SAML2 document.", ex); } }