/// <summary> /// Validates the given LogoutResponse object except for xml signature. /// XML signature checking is expected to be done prior to calling /// this method based on the appropriate profile. /// </summary> /// <param name="logoutResponse">LogoutResponse object.</param> /// <param name="logoutRequests"> /// Collection of previously sent logoutRequests used to compare with /// the InResponseTo attribute of the LogoutResponse (if present). /// </param> private void Validate(LogoutResponse logoutResponse, ICollection logoutRequests) { if (logoutResponse == null) { throw new Saml2Exception(Resources.ServiceProviderUtilityLogoutResponseIsNull); } ServiceProviderUtility.CheckInResponseTo(logoutResponse, logoutRequests); this.CheckIssuer(logoutResponse.Issuer); this.CheckCircleOfTrust(logoutResponse.Issuer); ServiceProviderUtility.CheckStatusCode(logoutResponse.StatusCode); }
/// <summary> /// Retrieve the LogoutResponse object found within the HttpRequest /// in the context of the HttpContext, performing validation of /// the LogoutResponse prior to returning to the user. /// </summary> /// <param name="context"> /// HttpContext containing session, request, and response objects. /// </param> /// <returns>LogoutResponse object</returns> public LogoutResponse GetLogoutResponse(HttpContext context) { LogoutResponse logoutResponse = null; HttpRequest request = context.Request; // Check if a saml response was received... if (String.IsNullOrEmpty(request[Saml2Constants.ResponseParameter])) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityNoSamlResponseReceived); } // Obtain the LogoutRequest object... if (request.HttpMethod == "GET") { string samlResponse = Saml2Utils.ConvertFromBase64Decompress(request[Saml2Constants.ResponseParameter]); logoutResponse = new LogoutResponse(samlResponse); } else { string samlResponse = Saml2Utils.ConvertFromBase64(request[Saml2Constants.ResponseParameter]); logoutResponse = new LogoutResponse(samlResponse); } XmlDocument xmlDoc = (XmlDocument)logoutResponse.XmlDom; StringBuilder logMessage = new StringBuilder(); logMessage.Append("LogoutResponse:\r\n").Append(xmlDoc.OuterXml); FedletLogger.Info(logMessage.ToString()); string prevLogoutRequestId = logoutResponse.InResponseTo; try { if (request.HttpMethod == "GET") { string queryString = request.RawUrl.Substring(request.RawUrl.IndexOf("?", StringComparison.Ordinal) + 1); FedletLogger.Info("LogoutResponse query string:\r\n" + queryString); this.ValidateForRedirect(logoutResponse, LogoutRequestCache.GetSentLogoutRequests(context), queryString); } else { this.ValidateForPost(logoutResponse, LogoutRequestCache.GetSentLogoutRequests(context)); } } catch (Saml2Exception se) { // log and throw again... logMessage = new StringBuilder(); logMessage.Append(se.Message).Append("\r\n").Append(xmlDoc.InnerXml); FedletLogger.Warning(logMessage.ToString()); throw; } finally { LogoutRequestCache.RemoveSentLogoutRequest(context, prevLogoutRequestId); } return logoutResponse; }
/// <summary> /// Checks the signature of the given logoutResponse assuming /// the signature is within the XML. /// </summary> /// <param name="logoutResponse">SAMLv2 LogoutRequest object.</param> private void CheckSignature(LogoutResponse logoutResponse) { IdentityProvider idp = (IdentityProvider)this.IdentityProviders[logoutResponse.Issuer]; if (idp == null) { throw new Saml2Exception(Resources.InvalidIssuer); } Saml2Utils.ValidateSignedXml( idp.SigningCertificate, logoutResponse.XmlDom, logoutResponse.XmlSignature, logoutResponse.Id); }
/// <summary> /// Checks the signature of the given LogoutResponse with /// the raw query string. /// </summary> /// <param name="logoutResponse">SAMLv2 LogoutResponse object.</param> /// <param name="queryString"> /// Raw query string that contains the response and possible signature. /// </param> private void CheckSignature(LogoutResponse logoutResponse, string queryString) { IdentityProvider idp = (IdentityProvider)this.IdentityProviders[logoutResponse.Issuer]; Saml2Utils.ValidateSignedQueryString(idp.SigningCertificate, queryString); }
/// <summary> /// Validates the given LogoutResponse object obtained from a /// Redirect. If this service provider desires the logout respone to /// be signed, XML signature checking will be performed. /// </summary> /// <param name="logoutResponse">LogoutResponse object.</param> /// <param name="logoutRequests"> /// Collection of previously sent logoutRequests used to compare with /// the InResponseTo attribute of the LogoutResponse (if present). /// </param> /// <param name="queryString"> /// Raw query string that contains the request and possible signature /// </param> public void ValidateForRedirect(LogoutResponse logoutResponse, ICollection logoutRequests, string queryString) { if (logoutResponse == null) { throw new Saml2Exception(Resources.ServiceProviderUtilityLogoutResponseIsNull); } if (this.ServiceProvider.WantLogoutResponseSigned) { this.CheckSignature(logoutResponse, queryString); } this.Validate(logoutResponse, logoutRequests); }
/// <summary> /// Checks the InResponseTo field of the given LogoutResponse to /// see if it is one of the managed logout requests. /// </summary> /// <param name="logoutResponse">SAMLv2 LogoutResponse.</param> /// <param name="logoutRequests"> /// Collection of previously sent LogoutRequests. /// </param> private static void CheckInResponseTo(LogoutResponse logoutResponse, ICollection logoutRequests) { if (logoutRequests != null && logoutResponse.InResponseTo != null) { IEnumerator i = logoutRequests.GetEnumerator(); while (i.MoveNext()) { LogoutRequest logoutRequest = (LogoutRequest)i.Current; if (logoutRequest.Id == logoutResponse.InResponseTo) { // Found one, return quietly. return; } } } // Didn't find one, complain loudly. throw new Saml2Exception(Resources.LogoutResponseInvalidInResponseTo); }
/// <summary> /// Sends a SOAP LogoutRequest to the specified IDP. /// </summary> /// <param name="logoutRequest"> /// LogoutRequest object. /// </param> /// <param name="idpEntityId">Entity ID of the IDP.</param> public void SendSoapLogoutRequest(LogoutRequest logoutRequest, string idpEntityId) { HttpWebRequest request = null; HttpWebResponse response = null; LogoutResponse logoutResponse = null; IdentityProvider idp = (IdentityProvider)this.IdentityProviders[idpEntityId]; if (logoutRequest == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityLogoutRequestIsNull); } else if (idp == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdentityProviderNotFound); } else if (idp.GetSingleLogoutServiceLocation(Saml2Constants.HttpSoapProtocolBinding) == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdpSingleLogoutSvcLocNotDefined); } try { Uri soapLogoutSvcUri = new Uri(idp.GetSingleLogoutServiceLocation(Saml2Constants.HttpSoapProtocolBinding)); request = (HttpWebRequest)WebRequest.Create(soapLogoutSvcUri); XmlDocument logoutRequestXml = (XmlDocument)logoutRequest.XmlDom; if (idp.WantLogoutRequestSigned) { if (string.IsNullOrEmpty(this.ServiceProvider.SigningCertificateAlias)) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilitySignFailedNoCertAlias); } else { Saml2Utils.SignXml( this.ServiceProvider.SigningCertificateAlias, logoutRequestXml, logoutRequest.Id, true); } } string soapMessage = Saml2Utils.CreateSoapMessage(logoutRequestXml.InnerXml); byte[] byteArray = Encoding.UTF8.GetBytes(soapMessage); request.ContentType = "text/xml"; request.ContentLength = byteArray.Length; request.AllowAutoRedirect = false; request.Method = "POST"; Stream requestStream = request.GetRequestStream(); requestStream.Write(byteArray, 0, byteArray.Length); requestStream.Close(); response = (HttpWebResponse)request.GetResponse(); StreamReader streamReader = new StreamReader(response.GetResponseStream()); string responseContent = streamReader.ReadToEnd(); streamReader.Close(); XmlDocument soapResponse = new XmlDocument(); soapResponse.PreserveWhitespace = true; soapResponse.LoadXml(responseContent); XmlNamespaceManager soapNsMgr = new XmlNamespaceManager(soapResponse.NameTable); soapNsMgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/"); soapNsMgr.AddNamespace("samlp", "urn:oasis:names:tc:SAML:2.0:protocol"); soapNsMgr.AddNamespace("saml", "urn:oasis:names:tc:SAML:2.0:assertion"); soapNsMgr.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#"); XmlElement root = soapResponse.DocumentElement; XmlNode responseXml = root.SelectSingleNode("/soap:Envelope/soap:Body/samlp:LogoutResponse", soapNsMgr); string logoutResponseXml = responseXml.OuterXml; logoutResponse = new LogoutResponse(logoutResponseXml); StringBuilder logMessage = new StringBuilder(); logMessage.Append("LogoutResponse:\r\n").Append(logoutResponseXml); FedletLogger.Info(logMessage.ToString()); ArrayList logoutRequests = new ArrayList(); logoutRequests.Add(logoutRequest); this.Validate(logoutResponse, logoutRequests); } catch (WebException we) { throw new ServiceProviderUtilityException(Resources.LogoutRequestWebException, we); } finally { if (response != null) { response.Close(); } } }
/// <summary> /// Writes a SOAP LogoutResponse to the Response object found within /// the given HttpContext based on the given logout request. /// </summary> /// <param name="context"> /// HttpContext containing session, request, and response objects. /// </param> /// <param name="logoutRequest"> /// LogoutRequest object. /// </param> public void SendSoapLogoutResponse(HttpContextBase context, LogoutRequest logoutRequest) { IIdentityProvider idp; if (!IdentityProviders.TryGetValue(logoutRequest.Issuer, out idp)) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdentityProviderNotFound); } var parameters = new NameValueCollection(); parameters[Saml2Constants.Binding] = Saml2Constants.HttpSoapProtocolBinding; var logoutResponse = new LogoutResponse(idp, ServiceProvider, logoutRequest, parameters, _saml2Utils); var logoutResponseXml = (XmlDocument) logoutResponse.XmlDom; if (idp.WantLogoutResponseSigned) { if (string.IsNullOrEmpty(ServiceProvider.SigningCertificateAlias)) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilitySignFailedNoCertAlias); } _saml2Utils.SignXml( ServiceProvider.SigningCertificateAlias, logoutResponseXml, logoutResponse.Id, true); } _logger.Info("LogoutResponse:\r\n{0}", logoutResponseXml.OuterXml); string soapMessage = _saml2Utils.CreateSoapMessage(logoutResponseXml.OuterXml); context.Response.ContentType = "text/xml"; context.Response.Write(soapMessage); }
/// <summary> /// Sends a SOAP LogoutRequest to the specified IDP. /// </summary> /// <param name="logoutRequest"> /// LogoutRequest object. /// </param> /// <param name="idpEntityId">Entity ID of the IDP.</param> public void SendSoapLogoutRequest(LogoutRequest logoutRequest, string idpEntityId) { HttpWebRequest request = null; HttpWebResponse response = null; LogoutResponse logoutResponse = null; IdentityProvider idp = (IdentityProvider)this.IdentityProviders[idpEntityId]; if (logoutRequest == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityLogoutRequestIsNull); } else if (idp == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdentityProviderNotFound); } else if (idp.GetSingleLogoutServiceLocation(Saml2Constants.HttpSoapProtocolBinding) == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdpSingleLogoutSvcLocNotDefined); } try { Uri soapLogoutSvcUri = new Uri(idp.GetSingleLogoutServiceLocation(Saml2Constants.HttpSoapProtocolBinding)); if (soapLogoutSvcUri.Scheme == "https") { System.Net.ServicePointManager.ServerCertificateValidationCallback += delegate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors) { if (ServiceProvider.TrustAllCerts || sslPolicyErrors.HasFlag(System.Net.Security.SslPolicyErrors.None)) { return true; } StringBuilder logErrorMessage = new StringBuilder(); logErrorMessage.Append("SSLPolicyError: ").Append(sslPolicyErrors); FedletLogger.Error(logErrorMessage.ToString()); return false; }; } request = (HttpWebRequest)WebRequest.Create(soapLogoutSvcUri); string authCertAlias = ConfigurationManager.AppSettings[Saml2Constants.MutualAuthCertAlias]; if (soapLogoutSvcUri.Scheme == "https" && !string.IsNullOrWhiteSpace(authCertAlias)) { X509Certificate2 cert = FedletCertificateFactory.GetCertificateByFriendlyName(authCertAlias); if (cert != null) { request.ClientCertificates.Add(cert); } } XmlDocument logoutRequestXml = (XmlDocument)logoutRequest.XmlDom; if (idp.WantLogoutRequestSigned) { if (string.IsNullOrEmpty(this.ServiceProvider.SigningCertificateAlias)) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilitySignFailedNoCertAlias); } else { Saml2Utils.SignXml( this.ServiceProvider.SigningCertificateAlias, logoutRequestXml, logoutRequest.Id, true, this.ServiceProvider); } } string soapMessage = Saml2Utils.CreateSoapMessage(logoutRequestXml.InnerXml); byte[] byteArray = Encoding.UTF8.GetBytes(soapMessage); request.ContentType = "text/xml"; request.ContentLength = byteArray.Length; request.AllowAutoRedirect = false; request.Method = "POST"; Stream requestStream = request.GetRequestStream(); requestStream.Write(byteArray, 0, byteArray.Length); requestStream.Close(); response = (HttpWebResponse)request.GetResponse(); StreamReader streamReader = new StreamReader(response.GetResponseStream()); string responseContent = streamReader.ReadToEnd(); streamReader.Close(); XmlDocument soapResponse = new XmlDocument(); soapResponse.PreserveWhitespace = true; soapResponse.LoadXml(responseContent); XmlNamespaceManager soapNsMgr = new XmlNamespaceManager(soapResponse.NameTable); soapNsMgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/"); soapNsMgr.AddNamespace("samlp", "urn:oasis:names:tc:SAML:2.0:protocol"); soapNsMgr.AddNamespace("saml", "urn:oasis:names:tc:SAML:2.0:assertion"); soapNsMgr.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#"); XmlElement root = soapResponse.DocumentElement; XmlNode responseXml = root.SelectSingleNode("/soap:Envelope/soap:Body/samlp:LogoutResponse", soapNsMgr); string logoutResponseXml = responseXml.OuterXml; logoutResponse = new LogoutResponse(logoutResponseXml); StringBuilder logMessage = new StringBuilder(); logMessage.Append("LogoutResponse:\r\n").Append(logoutResponseXml); FedletLogger.Info(logMessage.ToString()); ArrayList logoutRequests = new ArrayList(); logoutRequests.Add(logoutRequest); this.Validate(logoutResponse, logoutRequests); } catch (WebException we) { throw new ServiceProviderUtilityException(Resources.LogoutRequestWebException, we); } finally { if (response != null) { response.Close(); } } }
/// <summary> /// Send the SAML LogoutResponse message based on the received /// LogoutRequest. POST (default) or Redirect is supported. /// </summary> /// <param name="context"> /// HttpContext containing session, request, and response objects. /// </param> /// <param name="logoutRequest"> /// LogoutRequest corresponding to the ensuing LogoutResponse to send. /// </param> public void SendLogoutResponse(HttpContext context, LogoutRequest logoutRequest) { if (logoutRequest == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityLogoutRequestIsNull); } IdentityProvider idp = (IdentityProvider)this.IdentityProviders[logoutRequest.Issuer]; if (idp == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdpNotDeterminedFromLogoutRequest); } // send logout response based on how it was received if (context.Request.HttpMethod == "GET") { NameValueCollection parameters = new NameValueCollection(); parameters[Saml2Constants.Binding] = Saml2Constants.HttpRedirectProtocolBinding; LogoutResponse logoutResponse = new LogoutResponse(idp, this.ServiceProvider, logoutRequest, parameters); StringBuilder logMessage = new StringBuilder(); XmlDocument xmlDoc = (XmlDocument)logoutResponse.XmlDom; logMessage.Append("LogoutResponse:\r\n").Append(xmlDoc.OuterXml); FedletLogger.Info(logMessage.ToString()); parameters = Saml2Utils.GetRequestParameters(context.Request); string redirectUrl = this.GetLogoutResponseRedirectLocation(logoutResponse, idp.EntityId, parameters); context.Response.Redirect(redirectUrl.ToString(), true); } else { NameValueCollection parameters = new NameValueCollection(); parameters[Saml2Constants.Binding] = Saml2Constants.HttpPostProtocolBinding; LogoutResponse logoutResponse = new LogoutResponse(idp, this.ServiceProvider, logoutRequest, parameters); StringBuilder logMessage = new StringBuilder(); XmlDocument xmlDoc = (XmlDocument)logoutResponse.XmlDom; logMessage.Append("LogoutResponse:\r\n").Append(xmlDoc.OuterXml); FedletLogger.Info(logMessage.ToString()); parameters = Saml2Utils.GetRequestParameters(context.Request); string postHtml = this.GetLogoutResponsePostHtml(logoutResponse, idp.EntityId, parameters); context.Response.Write(postHtml); context.Response.End(); } }
/// <summary> /// Retrieve the LogoutResponse object found within the HttpRequest /// in the context of the HttpContext, performing validation of /// the LogoutResponse prior to returning to the user. /// </summary> /// <param name="context"> /// HttpContext containing session, request, and response objects. /// </param> /// <returns>LogoutResponse object</returns> public LogoutResponse GetLogoutResponse(HttpContextBase context) { LogoutResponse logoutResponse; HttpRequestBase request = context.Request; // Check if a saml response was received... if (String.IsNullOrEmpty(request[Saml2Constants.ResponseParameter])) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityNoSamlResponseReceived); } // Obtain the LogoutRequest object... if (request.HttpMethod == "GET") { string samlResponse = _saml2Utils.ConvertFromBase64Decompress(request[Saml2Constants.ResponseParameter]); logoutResponse = new LogoutResponse(samlResponse); } else { string samlResponse = _saml2Utils.ConvertFromBase64(request[Saml2Constants.ResponseParameter]); logoutResponse = new LogoutResponse(samlResponse); } var xmlDoc = (XmlDocument) logoutResponse.XmlDom; _logger.Info("LogoutResponse:\r\n{0}", xmlDoc.OuterXml); string prevLogoutRequestId = logoutResponse.InResponseTo; try { if (request.HttpMethod == "GET") { string queryString = request.RawUrl.Substring(request.RawUrl.IndexOf("?", StringComparison.Ordinal) + 1); _logger.Info("LogoutResponse query string:\r\n{0}", queryString); ValidateForRedirect(logoutResponse, LogoutRequestCache.GetSentLogoutRequests(context), queryString); } else { ValidateForPost(logoutResponse, LogoutRequestCache.GetSentLogoutRequests(context)); } } catch (Saml2Exception se) { // add some context se.Data["xml"] = xmlDoc.InnerXml; throw; } finally { LogoutRequestCache.RemoveSentLogoutRequest(context, prevLogoutRequestId); } return logoutResponse; }
/// <summary> /// Checks the signature of the given logoutResponse assuming /// the signature is within the XML. /// </summary> /// <param name="logoutResponse">SAMLv2 LogoutRequest object.</param> private void CheckSignature(LogoutResponse logoutResponse) { IIdentityProvider idp; if (!IdentityProviders.TryGetValue(logoutResponse.Issuer, out idp)) { throw new Saml2Exception(Resources.InvalidIssuer); } _saml2Utils.ValidateSignedXml( idp.SigningCertificate, logoutResponse.XmlDom, logoutResponse.XmlSignature, logoutResponse.Id); }
/// <summary> /// Validates the given LogoutResponse object obtained from a POST. If /// this service provider desires the logout respone to be signed, XML /// signature checking will be performed. /// </summary> /// <param name="logoutResponse">LogoutResponse object.</param> /// <param name="logoutRequests"> /// Collection of previously sent logoutRequests used to compare with /// the InResponseTo attribute of the LogoutResponse (if present). /// </param> public void ValidateForPost(LogoutResponse logoutResponse, ICollection logoutRequests) { if (logoutResponse == null) { throw new Saml2Exception(Resources.ServiceProviderUtilityLogoutResponseIsNull); } if (ServiceProvider.WantLogoutResponseSigned) { CheckSignature(logoutResponse); } Validate(logoutResponse, logoutRequests); }
/// <summary> /// Gets the HTML for use of submitting the LogoutResponse with POST. /// </summary> /// <param name="logoutResponse"> /// LogoutResponse to packaged for a POST. /// </param> /// <param name="idpEntityId">Entity ID of the IDP.</param> /// <param name="parameters"> /// NameVallueCollection of additional parameters. /// </param> /// <returns> /// HTML with auto-form submission with POST of the LogoutRequest /// </returns> public string GetLogoutResponsePostHtml(LogoutResponse logoutResponse, string idpEntityId, NameValueCollection parameters) { if (logoutResponse == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityLogoutResponseIsNull); } IdentityProvider idp = (IdentityProvider)this.IdentityProviders[idpEntityId]; if (idp == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdentityProviderNotFound); } string sloPostResponseLocation = idp.GetSingleLogoutServiceResponseLocation(Saml2Constants.HttpPostProtocolBinding); if (sloPostResponseLocation == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdpSingleLogoutSvcResLocNotDefined); } string relayState = null; if (parameters != null && !string.IsNullOrEmpty(parameters[Saml2Constants.RelayState])) { relayState = parameters[Saml2Constants.RelayState]; Saml2Utils.ValidateRelayState(relayState, this.ServiceProvider.RelayStateUrlList); } XmlDocument logoutResponseXml = (XmlDocument)logoutResponse.XmlDom; if (idp.WantLogoutResponseSigned) { if (string.IsNullOrEmpty(this.ServiceProvider.SigningCertificateAlias)) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilitySignFailedNoCertAlias); } else { Saml2Utils.SignXml( this.ServiceProvider.SigningCertificateAlias, logoutResponseXml, logoutResponse.Id, true); } } string packagedLogoutResponse = Saml2Utils.ConvertToBase64(logoutResponseXml.InnerXml); string inputFieldFormat = "<input type=\"hidden\" name=\"{0}\" value=\"{1}\" />"; StringBuilder html = new StringBuilder(); html.Append("<html><head><title>OpenSSO - IDP initiated SLO</title></head>"); html.Append("<body onload=\"document.forms[0].submit();\">"); html.Append("<form method=\"post\" action=\""); html.Append(sloPostResponseLocation); html.Append("\">"); html.Append("<input type=\"hidden\" name=\""); html.Append(Saml2Constants.ResponseParameter); html.Append("\" value=\""); html.Append(packagedLogoutResponse); html.Append("\" />"); if (!string.IsNullOrEmpty(relayState)) { html.Append(string.Format( CultureInfo.InvariantCulture, inputFieldFormat, Saml2Constants.RelayState, HttpUtility.HtmlEncode(relayState))); } html.Append("</form>"); html.Append("</body>"); html.Append("</html>"); return html.ToString(); }
/// <summary> /// Writes a SOAP LogoutResponse to the Response object found within /// the given HttpContext based on the given logout request. /// </summary> /// <param name="context"> /// HttpContext containing session, request, and response objects. /// </param> /// <param name="logoutRequest"> /// LogoutRequest object. /// </param> public void SendSoapLogoutResponse(HttpContext context, LogoutRequest logoutRequest) { IdentityProvider idp = (IdentityProvider)this.IdentityProviders[logoutRequest.Issuer]; if (idp == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdentityProviderNotFound); } NameValueCollection parameters = new NameValueCollection(); parameters[Saml2Constants.Binding] = Saml2Constants.HttpSoapProtocolBinding; LogoutResponse logoutResponse = new LogoutResponse(idp, this.ServiceProvider, logoutRequest, parameters); XmlDocument logoutResponseXml = (XmlDocument)logoutResponse.XmlDom; if (idp.WantLogoutResponseSigned) { if (string.IsNullOrEmpty(this.ServiceProvider.SigningCertificateAlias)) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilitySignFailedNoCertAlias); } else { Saml2Utils.SignXml( this.ServiceProvider.SigningCertificateAlias, logoutResponseXml, logoutResponse.Id, true); } } StringBuilder logMessage = new StringBuilder(); logMessage.Append("LogoutResponse:\r\n").Append(logoutResponseXml.OuterXml); FedletLogger.Info(logMessage.ToString()); string soapMessage = Saml2Utils.CreateSoapMessage(logoutResponseXml.OuterXml); context.Response.ContentType = "text/xml"; context.Response.Write(soapMessage); }
/// <summary> /// Gets the LogoutResponse location along with querystring parameters /// to be used for actual browser requests. /// </summary> /// <param name="logoutResponse"> /// LogoutResponse to packaged for a redirect. /// </param> /// <param name="idpEntityId">Entity ID of the IDP.</param> /// <param name="parameters"> /// NameVallueCollection of additional parameters. /// </param> /// <returns> /// URL with query string parameter for the specified IDP. /// </returns> public string GetLogoutResponseRedirectLocation(LogoutResponse logoutResponse, string idpEntityId, NameValueCollection parameters) { if (logoutResponse == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityLogoutResponseIsNull); } IdentityProvider idp = (IdentityProvider)this.IdentityProviders[idpEntityId]; if (idp == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdentityProviderNotFound); } string sloRedirectResponseLocation = idp.GetSingleLogoutServiceResponseLocation(Saml2Constants.HttpRedirectProtocolBinding); if (sloRedirectResponseLocation == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdpSingleLogoutSvcLocNotDefined); } string packagedLogoutResponse = Saml2Utils.CompressConvertToBase64UrlEncode(logoutResponse.XmlDom); string queryString = Saml2Constants.ResponseParameter + "=" + packagedLogoutResponse; if (parameters != null && !string.IsNullOrEmpty(parameters[Saml2Constants.RelayState])) { string relayState = parameters[Saml2Constants.RelayState]; Saml2Utils.ValidateRelayState(relayState, this.ServiceProvider.RelayStateUrlList); queryString += "&" + Saml2Constants.RelayState; queryString += "=" + HttpUtility.UrlEncode(relayState); } if (idp.WantLogoutResponseSigned) { if (string.IsNullOrEmpty(this.ServiceProvider.SigningCertificateAlias)) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilitySignFailedNoCertAlias); } else { queryString += "&" + Saml2Constants.SignatureAlgorithm; queryString += "=" + HttpUtility.UrlEncode(Saml2Constants.SignatureAlgorithmRsa); queryString = Saml2Utils.SignQueryString(this.ServiceProvider.SigningCertificateAlias, queryString); } } StringBuilder redirectUrl = new StringBuilder(); redirectUrl.Append(sloRedirectResponseLocation); redirectUrl.Append(Saml2Utils.GetQueryStringDelimiter(sloRedirectResponseLocation)); redirectUrl.Append(queryString); FedletLogger.Info("LogoutResponse via Redirect:\r\n" + redirectUrl); return redirectUrl.ToString(); }
/// <summary> /// Send the SAML LogoutResponse message based on the received /// LogoutRequest. POST (default) or Redirect is supported. /// </summary> /// <param name="context"> /// HttpContext containing session, request, and response objects. /// </param> /// <param name="logoutRequest"> /// LogoutRequest corresponding to the ensuing LogoutResponse to send. /// </param> public void SendLogoutResponse(HttpContextBase context, LogoutRequest logoutRequest) { if (logoutRequest == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityLogoutRequestIsNull); } IIdentityProvider idp; if (!IdentityProviders.TryGetValue(logoutRequest.Issuer, out idp)) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdpNotDeterminedFromLogoutRequest); } // send logout response based on how it was received if (context.Request.HttpMethod == "GET") { var parameters = new NameValueCollection(); parameters[Saml2Constants.Binding] = Saml2Constants.HttpRedirectProtocolBinding; var logoutResponse = new LogoutResponse(idp, ServiceProvider, logoutRequest, parameters, _saml2Utils); var xmlDoc = (XmlDocument) logoutResponse.XmlDom; _logger.Info("LogoutResponse:\r\n{0}", xmlDoc.OuterXml); parameters = _saml2Utils.GetRequestParameters(context.Request); string redirectUrl = GetLogoutResponseRedirectLocation(logoutResponse, idp.EntityId, parameters); context.Response.Redirect(redirectUrl, true); } else { var parameters = new NameValueCollection(); parameters[Saml2Constants.Binding] = Saml2Constants.HttpPostProtocolBinding; var logoutResponse = new LogoutResponse(idp, ServiceProvider, logoutRequest, parameters, _saml2Utils); var xmlDoc = (XmlDocument) logoutResponse.XmlDom; _logger.Info("LogoutResponse:\r\n{0}", xmlDoc.OuterXml); parameters = _saml2Utils.GetRequestParameters(context.Request); string postHtml = GetLogoutResponsePostHtml(logoutResponse, idp.EntityId, parameters); context.Response.Write(postHtml); context.Response.End(); } }