Forums, Documentation & Knowledge Base - ComponentSpace

SAML Single Log Out issue


https://www.componentspace.com/forums/Topic1704.aspx

By WeiC - 9/28/2015

We are having some issue with SLO. ComponentSpace.SAML2.dll we are using  is version 2.4.0.11, that we purchased a few years ago. When I looked at the developer guide online http://www.componentspace.com/Documentation/SAML%20v2.0%20Developer%20Guide.pdf, it is showing the following example for idP-Initiated SLO: 

5.1.4 SAMLIdentityProvider.InitiateSLOThe InitiateSLO method sends a logout request to each service provider in session as partof IdP-initiated SLO.For example: SAMLIdentityProvider.InitiateSLO( Response, null);.....

However, in the the library we are using, I couldn't find SAMLIdentityProvider.InitiateSLO. I can only find SingleLogoutService.SendLogoutRequestByHTTPPost. So we implemented using SingleLogoutService, but it doesn't work correctly. The request doesn't get posted at all and no error shows in the the log file neither. Below is the c# code we are using to implement SLO.

A La Carte and Feature Direct Reference Guide  public void SendSAMLSLORequest(string sessionId)
        {
            MetadataReader spReader = new MetadataReader(string.Format(SPMetadata, env));
            spReader.Process();

            MetadataReader idpReader = new MetadataReader(string.Format(IDPMetadata, env));
            idpReader.Process();

            LogoutRequest samlRequest = CreateSAMLRequest(spReader, idpReader, sessionId);
            SendSAMLRequest(samlRequest, spReader, idpReader, sessionId);

        }
private LogoutRequest CreateSAMLRequest(MetadataReader spReader, MetadataReader idpReader, string sessionIndex)
        {
            LogoutRequest result = new LogoutRequest();
            try
            {
                result.NameID = new NameID(client.OpaqueId);
                result.Destination = spReader.SingleLogOutServiceUrl;
                result.Issuer = new Issuer(idpReader.EntityId);
                result.IssueInstant = DateTime.UtcNow;
                result.NotOnOrAfter = DateTime.UtcNow.AddMinutes(10);
                result.Reason = "IDP Logout";
                result.SessionIndexes = new List<SessionIndex>();
                SessionIndex session = new SessionIndex(sessionIndex);
                result.SessionIndexes.Add(session);
            }
            catch (Exception ex)
            {
                …
            }

            return result;
        }

        private void SendSAMLRequest(LogoutRequest samlSLORequest, MetadataReader spReader, MetadataReader idpReader, string sessionIndex)
        {
            XmlElement samlRequestXml = samlSLORequest.ToXml();
            if (idpReader.SigningCert != null && idpReader.SigningCert.PrivateKey != null)
            {
                SAMLMessageSignature.Generate(samlRequestXml, idpReader.SigningCert.PrivateKey, idpReader.SigningCert);
                SingleLogoutService.SendLogoutRequestByHTTPPost(Response, spReader.SingleLogOutServiceUrl, samlRequestXml, null);
                 ...
            }
            else
            {
                …
            }
        }

Does anyone have ideas what is wrong with our SLO implementation? How can we use SAMLIdentityProvider.InitiateSLO?  Do we need to upgrade our library? 

thanks in advance!
By ComponentSpace - 10/31/2020

puffyqi - 10/30/2020
ComponentSpace - 10/30/2020
Removing the Response.Redirect is correct. You can do a redirect and send a SAML logout request in the same HTTP response.

Are you saying you're seeing an HTTP Post with the logout request sent to https://localhost:3443/samlSP/SAML/SLOService.aspx but no SAML logout response is being returned?

Have you tried setting a breakpoint in /SLOService.aspx to see what happens with the logout request?

Hi,

I manage to solve the issue. Since I still need the Response.redirect, I use alternative solution to solve it and get the logout response.

Steps:

1. Generate LogoutRequest XML element using ComponentSpace
2. Use HttpWebRequest to send form data POST request (passing in the URL and the LogoutRequest.OuterXML())

Below is the code:
public string postData(string destinationUrl, string requestXml)
   {
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(destinationUrl);

    string base64data = Base64Encode(requestXml);
    string postdata = "SAMLRequest=" + HttpUtility.UrlEncode(base64data);

    byte[] bytes;
    bytes = Encoding.ASCII.GetBytes(postdata);
    request.ContentType = "application/x-www-form-urlencoded";
    request.ContentLength = bytes.Length;
    request.Method = "POST";
    Stream requestStream = request.GetRequestStream();
    requestStream.Write(bytes, 0, bytes.Length);
    requestStream.Close();
    HttpWebResponse response;
    response = (HttpWebResponse)request.GetResponse();

    log("postData destinationurl: " + destinationUrl + ", statuscode: " + response.StatusCode);

    if (response.StatusCode == HttpStatusCode.OK)
    {
      Stream responseStream = response.GetResponseStream();
      string responseStr = new StreamReader(responseStream).ReadToEnd();
      return responseStr;
    }
    return null;
   }

At SAML2ServiceProvider side,

I change the code at Page_Load to use SingleLogoutService.ReceiveLogoutMessageByHTTPPost instead of SingleLogoutService.ReceiveLogoutMessageByHTTPRedirect.
Then at SendLogoutResponse(), i comment out the usage of SingleLogoutService.SendLogoutResponseByHTTPRedirect and change to Response.Write(logoutResponseXml.OuterXml). So that responseStr will be able to get the logoutresponse:
 <samlp:LogoutResponse ID="_93b0a02a-f12b-46a3-9e95-5a62184aaee4" Version="2.0" IssueInstant="2020-10-31T06:11:12.469Z" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"><saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">http://localhost:51394/http://localhost:51394/http://localhost:51394/</saml:Issuer><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" /></samlp:Status></samlp:LogoutResponse>


Hopefully, this can help other people who have similar issues.

Thanks :)

The recommended approach, if using the SAML low-level API, is to call SendLogoutRequestByHTTPRedirect or SendLogoutRequestByHTTPPost to send the logout request and ReceiveLogoutMessageByHTTPRedirect or ReceiveLogoutMessageByHTTPPost to receive the logout request. What you did obviously worked but is more code than is required.