private void ValidateLogoutViaPost(System.Collections.Specialized.NameValueCollection requestParams, out string message, out LogoutResponse response) { var parser = new HttpPostBindingParser(requestParams); logger.DebugFormat(TraceMessages.LogoutResponsePostBindingParse, parser.Message); response = Serialization.DeserializeFromXmlString <LogoutResponse>(parser.Message); var idp = IdpSelectionUtil.RetrieveIDPConfiguration(response.Issuer.Value, config); if (idp.Metadata == null) { logger.ErrorFormat(ErrorMessages.UnknownIdentityProvider, idp.Id); throw new Saml20Exception(string.Format(ErrorMessages.UnknownIdentityProvider, idp.Id)); } if (!parser.IsSigned) { logger.Error(ErrorMessages.ResponseSignatureMissing); throw new Saml20Exception(ErrorMessages.ResponseSignatureMissing); } // signature on final message in logout if (!parser.CheckSignature(idp.Metadata.Keys)) { logger.Error(ErrorMessages.ResponseSignatureInvalid); throw new Saml20Exception(ErrorMessages.ResponseSignatureInvalid); } message = parser.Message; }
/// <summary> /// Handle the authentication response from the IDP. /// </summary> /// <param name="context">The context.</param> private void HandleResponse(HttpContext context, Saml2Configuration config) { var defaultEncoding = Encoding.UTF8; var doc = GetDecodedSamlResponse(context, defaultEncoding); Logger.DebugFormat(TraceMessages.SamlResponseReceived, doc.OuterXml); // Determine whether the assertion should be decrypted before being validated. bool isEncrypted; var assertion = GetAssertion(doc.DocumentElement, out isEncrypted); if (isEncrypted) { assertion = GetDecryptedAssertion(assertion, config).Assertion.DocumentElement; } // Check if an encoding-override exists for the IdP endpoint in question var issuer = GetIssuer(assertion); var endpoint = IdpSelectionUtil.RetrieveIDPConfiguration(issuer, config); if (!endpoint.AllowReplayAttacks) { CheckReplayAttack(context, doc.DocumentElement, !endpoint.AllowIdPInitiatedSso); } var status = GetStatusElement(doc.DocumentElement); if (status.StatusCode.Value != Saml20Constants.StatusCodes.Success) { if (status.StatusCode.Value == Saml20Constants.StatusCodes.NoPassive) { Logger.Error(ErrorMessages.ResponseStatusIsNoPassive); throw new Saml20Exception(ErrorMessages.ResponseStatusIsNoPassive); } Logger.ErrorFormat(ErrorMessages.ResponseStatusNotSuccessful, status); throw new Saml20Exception(string.Format(ErrorMessages.ResponseStatusNotSuccessful, status)); } if (!string.IsNullOrEmpty(endpoint.ResponseEncoding)) { Encoding encodingOverride; try { encodingOverride = Encoding.GetEncoding(endpoint.ResponseEncoding); } catch (ArgumentException ex) { Logger.ErrorFormat(ErrorMessages.UnknownEncoding, endpoint.ResponseEncoding); throw new ArgumentException(string.Format(ErrorMessages.UnknownEncoding, endpoint.ResponseEncoding), ex); } if (encodingOverride.CodePage != defaultEncoding.CodePage) { var doc1 = GetDecodedSamlResponse(context, encodingOverride); assertion = GetAssertion(doc1.DocumentElement, out isEncrypted); } } HandleAssertion(context, assertion, config); }
/// <summary> /// Handle the authentication response from the IDP. /// </summary> /// <param name="context">The context.</param> public static Saml20Assertion HandleResponse(Saml2Configuration config, string samlResponse, IDictionary <string, object> session, Func <string, object> getFromCache, Action <string, object, DateTime> setInCache) { var defaultEncoding = Encoding.UTF8; var doc = Utility.GetDecodedSamlResponse(samlResponse, defaultEncoding); logger.DebugFormat(TraceMessages.SamlResponseReceived, doc.OuterXml); // Determine whether the assertion should be decrypted before being validated. bool isEncrypted; var assertion = Utility.GetAssertion(doc.DocumentElement, out isEncrypted); if (isEncrypted) { assertion = Utility.GetDecryptedAssertion(assertion, config).Assertion.DocumentElement; } // Check if an encoding-override exists for the IdP endpoint in question var issuer = Utility.GetIssuer(assertion); var endpoint = IdpSelectionUtil.RetrieveIDPConfiguration(issuer, config); if (!endpoint.AllowReplayAttacks) { Utility.CheckReplayAttack(doc.DocumentElement, !endpoint.AllowIdPInitiatedSso, session); } var status = Utility.GetStatusElement(doc.DocumentElement); if (status.StatusCode.Value != Saml20Constants.StatusCodes.Success) { if (status.StatusCode.Value == Saml20Constants.StatusCodes.NoPassive) { logger.Error(ErrorMessages.ResponseStatusIsNoPassive); throw new Saml20Exception(ErrorMessages.ResponseStatusIsNoPassive); } logger.ErrorFormat(ErrorMessages.ResponseStatusNotSuccessful, status); throw new Saml20Exception(string.Format(ErrorMessages.ResponseStatusNotSuccessful, status)); } if (!string.IsNullOrEmpty(endpoint.ResponseEncoding)) { Encoding encodingOverride; try { encodingOverride = Encoding.GetEncoding(endpoint.ResponseEncoding); } catch (ArgumentException ex) { logger.ErrorFormat(ErrorMessages.UnknownEncoding, endpoint.ResponseEncoding); throw new ArgumentException(string.Format(ErrorMessages.UnknownEncoding, endpoint.ResponseEncoding), ex); } if (encodingOverride.CodePage != defaultEncoding.CodePage) { var doc1 = GetDecodedSamlResponse(samlResponse, encodingOverride); assertion = GetAssertion(doc1.DocumentElement, out isEncrypted); } } return(HandleAssertion(assertion, config, getFromCache, setInCache)); }
/// <summary> /// Handles the SOAP. /// </summary> /// <param name="context">The context.</param> /// <param name="inputStream">The input stream.</param> public static void HandleSoap(HttpArtifactBindingBuilder builder, Stream inputStream, Saml2Configuration config, Action<Saml20Assertion> signonCallback, Func<string, object> getFromCache, Action<string, object, DateTime> setInCache, IDictionary<string, object> session) { var parser = new HttpArtifactBindingParser(inputStream); logger.DebugFormat(TraceMessages.SOAPMessageParse, parser.SamlMessage.OuterXml); if (parser.IsArtifactResolve) { logger.Debug(TraceMessages.ArtifactResolveReceived); var idp = IdpSelectionUtil.RetrieveIDPConfiguration(parser.Issuer, config); if (!parser.CheckSamlMessageSignature(idp.Metadata.Keys)) { logger.Error(ErrorMessages.ArtifactResolveSignatureInvalid); throw new Saml20Exception(ErrorMessages.ArtifactResolveSignatureInvalid); } builder.RespondToArtifactResolve(parser.ArtifactResolve, ((XmlDocument)getFromCache(parser.ArtifactResolve.Artifact)).DocumentElement); } else if (parser.IsArtifactResponse) { logger.Debug(TraceMessages.ArtifactResolveReceived); var idp = IdpSelectionUtil.RetrieveIDPConfiguration(parser.Issuer, config); if (!parser.CheckSamlMessageSignature(idp.Metadata.Keys)) { logger.Error(ErrorMessages.ArtifactResponseSignatureInvalid); throw new Saml20Exception(ErrorMessages.ArtifactResponseSignatureInvalid); } var status = parser.ArtifactResponse.Status; if (status.StatusCode.Value != Saml20Constants.StatusCodes.Success) { logger.ErrorFormat(ErrorMessages.ArtifactResponseStatusCodeInvalid, status.StatusCode.Value); throw new Saml20Exception(string.Format(ErrorMessages.ArtifactResponseStatusCodeInvalid, status.StatusCode.Value)); } if (parser.ArtifactResponse.Any.LocalName == Response.ElementName) { Utility.CheckReplayAttack(parser.ArtifactResponse.Any, true, session); var responseStatus = Utility.GetStatusElement(parser.ArtifactResponse.Any); if (responseStatus.StatusCode.Value != Saml20Constants.StatusCodes.Success) { logger.ErrorFormat(ErrorMessages.ArtifactResponseStatusCodeInvalid, responseStatus.StatusCode.Value); throw new Saml20Exception(string.Format(ErrorMessages.ArtifactResponseStatusCodeInvalid, responseStatus.StatusCode.Value)); } bool isEncrypted; var assertion = Utility.GetAssertion(parser.ArtifactResponse.Any, out isEncrypted); if (assertion == null) { logger.Error(ErrorMessages.ArtifactResponseMissingAssertion); throw new Saml20Exception(ErrorMessages.ArtifactResponseMissingAssertion); } var samlAssertion = isEncrypted ? Utility.HandleEncryptedAssertion(assertion, config, getFromCache, setInCache) : Utility.HandleAssertion(assertion, config, getFromCache, setInCache); signonCallback(samlAssertion); } else { logger.ErrorFormat(ErrorMessages.ArtifactResponseMissingResponse); throw new Saml20Exception(ErrorMessages.ArtifactResponseMissingResponse); } } else { logger.ErrorFormat(ErrorMessages.SOAPMessageUnsupportedSamlMessage); throw new Saml20Exception(ErrorMessages.SOAPMessageUnsupportedSamlMessage); } }
/// <summary> /// Deserializes an assertion, verifies its signature and logs in the user if the assertion is valid. /// </summary> /// <param name="context">The context.</param> /// <param name="elem">The elem.</param> public static Saml20Assertion HandleAssertion(XmlElement elem, Saml2Configuration config, Func <string, object> getFromCache, Action <string, object, DateTime> setInCache) { logger.DebugFormat(TraceMessages.AssertionProcessing, elem.OuterXml); var issuer = GetIssuer(elem); var endp = IdpSelectionUtil.RetrieveIDPConfiguration(issuer, config); PreHandleAssertion(elem, endp); if (endp == null || endp.Metadata == null) { logger.Error(ErrorMessages.AssertionIdentityProviderUnknown); throw new Saml20Exception(ErrorMessages.AssertionIdentityProviderUnknown); } var quirksMode = endp.QuirksMode; var assertion = new Saml20Assertion(elem, null, quirksMode, config); // Check signatures if (!endp.OmitAssertionSignatureCheck) { var keys = endp.Metadata.GetKeys(KeyTypes.Signing); if (keys == null || !keys.Any()) { keys = endp.Metadata.GetKeys(KeyTypes.Encryption); } var trusted = GetTrustedSigners(keys, endp); if (!assertion.CheckSignature(trusted)) { logger.Error(ErrorMessages.AssertionSignatureInvalid); throw new Saml20Exception(ErrorMessages.AssertionSignatureInvalid); } } // Check expiration if (assertion.IsExpired) { logger.Error(ErrorMessages.AssertionExpired); throw new Saml20Exception(ErrorMessages.AssertionExpired); } // Check one time use if (assertion.IsOneTimeUse) { if (getFromCache(assertion.Id) != null) { logger.Error(ErrorMessages.AssertionOneTimeUseExceeded); throw new Saml20Exception(ErrorMessages.AssertionOneTimeUseExceeded); } setInCache(assertion.Id, string.Empty, assertion.NotOnOrAfter); } logger.DebugFormat(TraceMessages.AssertionParsed, assertion.Id); return(assertion); }
/// <summary> /// Handles the SOAP message. /// </summary> /// <param name="context">The context.</param> /// <param name="inputStream">The input stream.</param> private async Task HandleSoap(IOwinContext context, Stream inputStream, NameValueCollection requestParams) { var config = options.Configuration; var parser = new HttpArtifactBindingParser(inputStream); Logger.DebugFormat(TraceMessages.SOAPMessageParse, parser.SamlMessage.OuterXml); var builder = GetBuilder(context); var idp = IdpSelectionUtil.RetrieveIDPConfiguration(parser.Issuer, config); if (parser.IsLogoutReqest) { Logger.DebugFormat(TraceMessages.LogoutRequestReceived, parser.SamlMessage.OuterXml); if (!parser.CheckSamlMessageSignature(idp.Metadata.Keys)) { Logger.ErrorFormat(ErrorMessages.ArtifactResolveSignatureInvalid); throw new Saml20Exception(ErrorMessages.ArtifactResolveSignatureInvalid); } var req = parser.LogoutRequest; var logoutRequestReceivedNotification = new LogoutRequestReceivedNotification <LogoutRequest, SamlAuthenticationOptions>(context, options) { ProtocolMessage = req }; await options.Notifications.LogoutRequestReceived(logoutRequestReceivedNotification); DoLogout(context, true); // Build the response object var response = new Saml20LogoutResponse { Issuer = config.ServiceProvider.Id, StatusCode = Saml20Constants.StatusCodes.Success, InResponseTo = req.Id }; // response.Destination = destination.Url; var doc = response.GetXml(); XmlSignatureUtils.SignDocument(doc, response.Id, config.ServiceProvider.SigningCertificate); if (doc.FirstChild is XmlDeclaration) { doc.RemoveChild(doc.FirstChild); } SendResponseMessage(doc.OuterXml, context); } else { Logger.ErrorFormat(ErrorMessages.SOAPMessageUnsupportedSamlMessage); throw new Saml20Exception(ErrorMessages.SOAPMessageUnsupportedSamlMessage); } }
/// <summary> /// Deserializes an assertion, verifies its signature and logs in the user if the assertion is valid. /// </summary> /// <param name="context">The context.</param> /// <param name="elem">The elem.</param> private void HandleAssertion(HttpContext context, XmlElement elem, Saml2Configuration config) { Logger.DebugFormat(TraceMessages.AssertionProcessing, elem.OuterXml); var issuer = GetIssuer(elem); var endp = IdpSelectionUtil.RetrieveIDPConfiguration(issuer, config); PreHandleAssertion(context, elem, endp); if (endp == null || endp.Metadata == null) { Logger.Error(ErrorMessages.AssertionIdentityProviderUnknown); throw new Saml20Exception(ErrorMessages.AssertionIdentityProviderUnknown); } var quirksMode = endp.QuirksMode; var assertion = new Saml20Assertion(elem, null, quirksMode, config); // Check signatures if (!endp.OmitAssertionSignatureCheck) { if (!assertion.CheckSignature(GetTrustedSigners(endp.Metadata.GetKeys(KeyTypes.Signing), endp))) { Logger.Error(ErrorMessages.AssertionSignatureInvalid); throw new Saml20Exception(ErrorMessages.AssertionSignatureInvalid); } } // Check expiration if (assertion.IsExpired) { Logger.Error(ErrorMessages.AssertionExpired); throw new Saml20Exception(ErrorMessages.AssertionExpired); } // Check one time use if (assertion.IsOneTimeUse) { if (context.Cache[assertion.Id] != null) { Logger.Error(ErrorMessages.AssertionOneTimeUseExceeded); throw new Saml20Exception(ErrorMessages.AssertionOneTimeUseExceeded); } context.Cache.Insert(assertion.Id, string.Empty, null, assertion.NotOnOrAfter, Cache.NoSlidingExpiration); } Logger.DebugFormat(TraceMessages.AssertionParsed, assertion.Id); DoSignOn(context, assertion, config); }
/// <summary> /// Handles a request. /// </summary> /// <param name="context">The context.</param> public void Handle(HttpContext context, Saml2Configuration config) { Logger.Debug(TraceMessages.LogoutHandlerCalled); // Some IDP's are known to fail to set an actual value in the SOAPAction header // so we just check for the existence of the header field. if (Array.Exists(context.Request.Headers.AllKeys, s => s == SoapConstants.SoapAction)) { HandleSoap(context, context.Request.InputStream, config); return; } if (!string.IsNullOrEmpty(context.Request.Params["SAMLart"])) { HandleArtifact(context, ConfigurationFactory.Instance.Configuration, HandleSoap); return; } if (!string.IsNullOrEmpty(context.Request.Params["SAMLResponse"])) { HandleResponse(context, config); } else if (!string.IsNullOrEmpty(context.Request.Params["SAMLRequest"])) { HandleRequest(context); } else { IdentityProvider idpEndpoint = null; // context.Session[IDPLoginSessionKey] may be null if IIS has been restarted if (context.Session[IdpSessionIdKey] != null) { idpEndpoint = IdpSelectionUtil.RetrieveIDPConfiguration((string)context.Session[IdpLoginSessionKey], config); } if (idpEndpoint == null) { // TODO: Reconsider how to accomplish this. context.User = null; FormsAuthentication.SignOut(); Logger.ErrorFormat(ErrorMessages.UnknownIdentityProvider, string.Empty); throw new Saml20Exception(string.Format(ErrorMessages.UnknownIdentityProvider, string.Empty)); } TransferClient(idpEndpoint, context, config); } }
private void ValidateLogoutViaGet(Uri requestUrl, out string message, out LogoutResponse response) { var parser = new HttpRedirectBindingParser(requestUrl); response = Serialization.DeserializeFromXmlString <LogoutResponse>(parser.Message); logger.DebugFormat(TraceMessages.LogoutResponseRedirectBindingParse, parser.Message, parser.SignatureAlgorithm, parser.Signature); var idp = IdpSelectionUtil.RetrieveIDPConfiguration(response.Issuer.Value, config); if (idp.Metadata == null) { logger.ErrorFormat(ErrorMessages.UnknownIdentityProvider, idp.Id); throw new Saml20Exception(string.Format(ErrorMessages.UnknownIdentityProvider, idp.Id)); } if (!parser.VerifySignature(idp.Metadata.Keys)) { logger.Error(ErrorMessages.ResponseSignatureInvalid); throw new Saml20Exception(ErrorMessages.ResponseSignatureInvalid); } message = parser.Message; }
/// <summary> /// Handles the SOAP. /// </summary> /// <param name="context">The context.</param> /// <param name="inputStream">The input stream.</param> private void HandleSoap(HttpContext context, Stream inputStream, Saml2Configuration config) { var parser = new HttpArtifactBindingParser(inputStream); Logger.DebugFormat(TraceMessages.SOAPMessageParse, parser.SamlMessage.OuterXml); var builder = new HttpArtifactBindingBuilder(context, config); if (parser.IsArtifactResolve) { Logger.Debug(TraceMessages.ArtifactResolveReceived); var idp = IdpSelectionUtil.RetrieveIDPConfiguration(parser.Issuer, config); if (!parser.CheckSamlMessageSignature(idp.Metadata.Keys)) { Logger.Error(ErrorMessages.ArtifactResolveSignatureInvalid); throw new Saml20Exception(ErrorMessages.ArtifactResolveSignatureInvalid); } builder.RespondToArtifactResolve(parser.ArtifactResolve); } else if (parser.IsArtifactResponse) { Logger.Debug(TraceMessages.ArtifactResolveReceived); var idp = IdpSelectionUtil.RetrieveIDPConfiguration(parser.Issuer, config); if (!parser.CheckSamlMessageSignature(idp.Metadata.Keys)) { Logger.Error(ErrorMessages.ArtifactResponseSignatureInvalid); throw new Saml20Exception(ErrorMessages.ArtifactResponseSignatureInvalid); } var status = parser.ArtifactResponse.Status; if (status.StatusCode.Value != Saml20Constants.StatusCodes.Success) { Logger.ErrorFormat(ErrorMessages.ArtifactResponseStatusCodeInvalid, status.StatusCode.Value); throw new Saml20Exception(string.Format(ErrorMessages.ArtifactResponseStatusCodeInvalid, status.StatusCode.Value)); } if (parser.ArtifactResponse.Any.LocalName == Response.ElementName) { CheckReplayAttack(context, parser.ArtifactResponse.Any, true); var responseStatus = GetStatusElement(parser.ArtifactResponse.Any); if (responseStatus.StatusCode.Value != Saml20Constants.StatusCodes.Success) { Logger.ErrorFormat(ErrorMessages.ArtifactResponseStatusCodeInvalid, responseStatus.StatusCode.Value); throw new Saml20Exception(string.Format(ErrorMessages.ArtifactResponseStatusCodeInvalid, responseStatus.StatusCode.Value)); } bool isEncrypted; var assertion = GetAssertion(parser.ArtifactResponse.Any, out isEncrypted); if (assertion == null) { Logger.Error(ErrorMessages.ArtifactResponseMissingAssertion); throw new Saml20Exception(ErrorMessages.ArtifactResponseMissingAssertion); } if (isEncrypted) { HandleEncryptedAssertion(context, assertion, config); } else { HandleAssertion(context, assertion, config); } } else { Logger.ErrorFormat(ErrorMessages.ArtifactResponseMissingResponse); throw new Saml20Exception(ErrorMessages.ArtifactResponseMissingResponse); } } else { Logger.ErrorFormat(ErrorMessages.SOAPMessageUnsupportedSamlMessage); throw new Saml20Exception(ErrorMessages.SOAPMessageUnsupportedSamlMessage); } }
/// <summary> /// Handles the request. /// </summary> /// <param name="context">The context.</param> private void HandleRequest(HttpContext context) { Logger.DebugFormat(TraceMessages.LogoutRequestReceived); // Fetch the endpoint configuration var idp = IdpSelectionUtil.RetrieveIDPConfiguration((string)context.Session[IdpLoginSessionKey], ConfigurationFactory.Instance.Configuration); var destination = IdpSelectionUtil.DetermineEndpointConfiguration(BindingType.Redirect, idp.Endpoints.DefaultLogoutEndpoint, idp.Metadata.IDPSLOEndpoints); // Fetch config object var config = ConfigurationFactory.Instance.Configuration; // Build the response object var response = new Saml20LogoutResponse { Issuer = config.ServiceProvider.Id, Destination = destination.Url, StatusCode = Saml20Constants.StatusCodes.Success }; string message; if (context.Request.RequestType == "GET") { // HTTP Redirect binding var parser = new HttpRedirectBindingParser(context.Request.Url); Logger.DebugFormat(TraceMessages.LogoutRequestRedirectBindingParse, parser.Message, parser.SignatureAlgorithm, parser.Signature); var endpoint = config.IdentityProviders.FirstOrDefault(x => x.Id == idp.Id); if (endpoint == null || endpoint.Metadata == null) { Logger.ErrorFormat(ErrorMessages.UnknownIdentityProvider, idp.Id); throw new Saml20Exception(string.Format(ErrorMessages.UnknownIdentityProvider, idp.Id)); } var metadata = endpoint.Metadata; if (!parser.VerifySignature(metadata.GetKeys(KeyTypes.Signing))) { Logger.Error(ErrorMessages.RequestSignatureInvalid); throw new Saml20Exception(ErrorMessages.RequestSignatureInvalid); } message = parser.Message; } else if (context.Request.RequestType == "POST") { // HTTP Post binding var parser = new HttpPostBindingParser(context.Request.Params); Logger.DebugFormat(TraceMessages.LogoutRequestPostBindingParse, parser.Message); if (!parser.IsSigned) { Logger.Error(ErrorMessages.RequestSignatureMissing); throw new Saml20Exception(ErrorMessages.RequestSignatureMissing); } var endpoint = config.IdentityProviders.FirstOrDefault(x => x.Id == idp.Id); if (endpoint == null || endpoint.Metadata == null) { Logger.ErrorFormat(ErrorMessages.UnknownIdentityProvider, idp.Id); throw new Saml20Exception(string.Format(ErrorMessages.UnknownIdentityProvider, idp.Id)); } var metadata = endpoint.Metadata; // Check signature if (!parser.CheckSignature(metadata.GetKeys(KeyTypes.Signing))) { Logger.Error(ErrorMessages.RequestSignatureInvalid); throw new Saml20Exception(ErrorMessages.RequestSignatureInvalid); } message = parser.Message; } else { // Error: We don't support HEAD, PUT, CONNECT, TRACE, DELETE and OPTIONS Logger.ErrorFormat(ErrorMessages.UnsupportedRequestType, context.Request.RequestType); throw new Saml20Exception(string.Format(ErrorMessages.UnsupportedRequestType, context.Request.RequestType)); } Logger.DebugFormat(TraceMessages.LogoutRequestParsed, message); // Log the user out locally DoLogout(context, true); var req = Serialization.DeserializeFromXmlString <LogoutRequest>(message); response.InResponseTo = req.Id; // Respond using redirect binding if (destination.Binding == BindingType.Redirect) { var builder = new HttpRedirectBindingBuilder { RelayState = context.Request.Params["RelayState"], Response = response.GetXml().OuterXml, SigningKey = config.ServiceProvider.SigningCertificate.PrivateKey }; Logger.DebugFormat(TraceMessages.LogoutResponseSent, builder.Response); context.Response.Redirect(destination.Url + "?" + builder.ToQuery(), true); return; } // Respond using post binding if (destination.Binding == BindingType.Post) { var builder = new HttpPostBindingBuilder(destination) { Action = SamlActionType.SAMLResponse }; var responseDocument = response.GetXml(); Logger.DebugFormat(TraceMessages.LogoutResponseSent, responseDocument.OuterXml); XmlSignatureUtils.SignDocument(responseDocument, response.Id, config); builder.Response = responseDocument.OuterXml; builder.RelayState = context.Request.Params["RelayState"]; context.Response.Write(builder.GetPage()); } }
/// <summary> /// Handles the SOAP message. /// </summary> /// <param name="context">The context.</param> /// <param name="inputStream">The input stream.</param> private void HandleSoap(HttpContext context, Stream inputStream, Saml2Configuration config) { var parser = new HttpArtifactBindingParser(inputStream); Logger.DebugFormat(TraceMessages.SOAPMessageParse, parser.SamlMessage.OuterXml); var builder = GetBuilder(context); var idp = IdpSelectionUtil.RetrieveIDPConfiguration(parser.Issuer, config); if (parser.IsArtifactResolve) { Logger.DebugFormat(TraceMessages.ArtifactResolveReceived, parser.SamlMessage); if (!parser.CheckSamlMessageSignature(idp.Metadata.Keys)) { Logger.ErrorFormat(ErrorMessages.ArtifactResolveSignatureInvalid); throw new Saml20Exception(ErrorMessages.ArtifactResolveSignatureInvalid); } builder.RespondToArtifactResolve(parser.ArtifactResolve, parser.SamlMessage); } else if (parser.IsArtifactResponse) { Logger.DebugFormat(TraceMessages.ArtifactResponseReceived, parser.SamlMessage); if (!parser.CheckSamlMessageSignature(idp.Metadata.Keys)) { Logger.Error(ErrorMessages.ArtifactResponseSignatureInvalid); throw new Saml20Exception(ErrorMessages.ArtifactResponseSignatureInvalid); } var status = parser.ArtifactResponse.Status; if (status.StatusCode.Value != Saml20Constants.StatusCodes.Success) { Logger.ErrorFormat(ErrorMessages.ArtifactResponseStatusCodeInvalid, status.StatusCode.Value); throw new Saml20Exception(string.Format(ErrorMessages.ArtifactResponseStatusCodeInvalid, status.StatusCode.Value)); } if (parser.ArtifactResponse.Any.LocalName == LogoutRequest.ElementName) { Logger.DebugFormat(TraceMessages.LogoutRequestReceived, parser.ArtifactResponse.Any.OuterXml); var req = Serialization.DeserializeFromXmlString <LogoutRequest>(parser.ArtifactResponse.Any.OuterXml); // Send logoutresponse via artifact var response = new Saml20LogoutResponse { Issuer = config.ServiceProvider.Id, StatusCode = Saml20Constants.StatusCodes.Success, InResponseTo = req.Id }; var endpoint = IdpSelectionUtil.RetrieveIDPConfiguration((string)context.Session[IdpLoginSessionKey], config); var destination = IdpSelectionUtil.DetermineEndpointConfiguration(BindingType.Redirect, endpoint.Endpoints.DefaultLogoutEndpoint, endpoint.Metadata.IDPSLOEndpoints); builder.RedirectFromLogout(destination, response, context.Request.Params["relayState"], (s, o) => context.Cache.Insert(s, o, null, DateTime.Now.AddMinutes(1), Cache.NoSlidingExpiration)); } else if (parser.ArtifactResponse.Any.LocalName == LogoutResponse.ElementName) { DoLogout(context, false, config); } else { Logger.ErrorFormat(ErrorMessages.ArtifactResponseMissingResponse); throw new Saml20Exception(ErrorMessages.ArtifactResponseMissingResponse); } } else if (parser.IsLogoutReqest) { Logger.DebugFormat(TraceMessages.LogoutRequestReceived, parser.SamlMessage.OuterXml); var req = parser.LogoutRequest; // Build the response object var response = new Saml20LogoutResponse { Issuer = config.ServiceProvider.Id, StatusCode = Saml20Constants.StatusCodes.Success, InResponseTo = req.Id }; // response.Destination = destination.Url; var doc = response.GetXml(); XmlSignatureUtils.SignDocument(doc, response.Id, config); if (doc.FirstChild is XmlDeclaration) { doc.RemoveChild(doc.FirstChild); } SendResponseMessage(doc.OuterXml, context); } else { Logger.ErrorFormat(ErrorMessages.SOAPMessageUnsupportedSamlMessage); throw new Saml20Exception(ErrorMessages.SOAPMessageUnsupportedSamlMessage); } }