2
votes

I'm using the sustainsys saml2 owin package, and I'm having problems with SP-initiated SLO. I'm new to the saml process, so there's a good chance I'm doing something wrong.

Our signout workflow is as follows:

  1. User hits myapp.com/signout
  2. myapp redirects to myapp.com/saml2/logout
  3. The owin package generates a saml request and sends it to the Idp's slo route
  4. The Idp responds with a successful saml response to the same url: myapp.com/saml2/logout
  5. At this point, the owin package is generating another saml request to sign the user out of the idp. It would get stuck in an infinite redirect, if the Idp didn't halt the process.

Here's a snapshot of my network panel in chrome: saml2 signout

I'm using https://github.com/mcguinness/saml-idp as a development Idp, and here's a stub of my owin configuration: C# owin config

I suspect I've misconfigured something or I'm using the saml2/logout route inappropriately, but I also find it odd that the owin package would generate another request when it gets a successful response.

Update 11.9.2018

Here's my verbose log starting from the logout process:

Expanded Saml2Url
  AssertionConsumerServiceUrl: http://locala.foliotek.com/saml2/linuxdev/Acs
  SignInUrl: http://locala.foliotek.com/saml2/linuxdev/SignIn
  LogoutUrl: http://locala.foliotek.com/saml2/linuxdev/Logout
  ApplicationUrl: http://locala.foliotek.com/
=================
Initiating logout, checking requirements for federated logout
  Issuer of LogoutNameIdentifier claim (should be Idp entity id): http://myidentityprovider.com
  Issuer is a known Idp: True
  Session index claim (should have a value): http://Sustainsys.se/Saml2/SessionIndex: 1926000282
  Idp has SingleLogoutServiceUrl: http://myidentityprovider.com/saml/slo
  There is a signingCertificate in SPOptions: True
  Idp configured to DisableOutboundLogoutRequests (should be false): False
=================
Expanded Saml2Url
  AssertionConsumerServiceUrl: http://myserviceprovider.com/saml2/linuxdev/Acs
  SignInUrl: http://myserviceprovider.com/saml2/linuxdev/SignIn
  LogoutUrl: http://myserviceprovider.com/saml2/linuxdev/Logout
  ApplicationUrl: http://myserviceprovider.com/
=================
Initiating logout, checking requirements for federated logout
  Issuer of LogoutNameIdentifier claim (should be Idp entity id): http://myidentityprovider.com
  Issuer is a known Idp: True
  Session index claim (should have a value): http://Sustainsys.se/Saml2/SessionIndex: 1926000282
  Idp has SingleLogoutServiceUrl: http://myidentityprovider.com/saml/slo
  There is a signingCertificate in SPOptions: True
  Idp configured to DisableOutboundLogoutRequests (should be false): False
=================
Expanded Saml2Url
  AssertionConsumerServiceUrl: http://myserviceprovider.com/saml2/samltestid/Acs
  SignInUrl: http://myserviceprovider.com/saml2/samltestid/SignIn
  LogoutUrl: http://myserviceprovider.com/saml2/samltestid/Logout
  ApplicationUrl: http://myserviceprovider.com/
=================
Initiating logout, checking requirements for federated logout
  Issuer of LogoutNameIdentifier claim (should be Idp entity id): http://myidentityprovider.com
  Issuer is a known Idp: False
  Session index claim (should have a value): http://Sustainsys.se/Saml2/SessionIndex: 1926000282
  Idp has SingleLogoutServiceUrl: 
  There is a signingCertificate in SPOptions: True
  Idp configured to DisableOutboundLogoutRequests (should be false): 
=================
Expanded Saml2Url
  AssertionConsumerServiceUrl: http://myserviceprovider.com/saml2/linuxdev/Acs
  SignInUrl: http://myserviceprovider.com/saml2/linuxdev/SignIn
  LogoutUrl: http://myserviceprovider.com/saml2/linuxdev/Logout
  ApplicationUrl: http://myserviceprovider.com/
=================
Http POST binding extracted message
<samlp:LogoutResponse xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="_d02d42fbb8ed00bbee02" InResponseTo="idf75b17a7713e4f698f891edf1fcca117" Version="2.0" IssueInstant="2018-11-09T16:44:01Z" Destination="http://myserviceprovider.com/saml2/linuxdev/logout"><saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">http://myidentityprovider.com</saml:Issuer><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" /><Reference URI="#_d02d42fbb8ed00bbee02"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" /><DigestValue>X+93fiv6vuuy8sIhmFFxIVxNgAy/f1Zk62RRh/rn91I=</DigestValue></Reference></SignedInfo><SignatureValue>qXMlLe2fciQR6u7Ddx40RFI51IJ5r8A3m7X7mrgIMHBdFf2vypiCFxqOrEOKCSIqWzDUxVXujWyMQzO/zZtVyZlm6xXnb3lId0VDHLEIUT/8kyNsodzvzPIyTMaMMV/cmhQ3UZlYRv9BeyPswpkosFTn/xc6c+BX9z+w4AN4KDMFfYlTeu/uyDBa1u5zr/Ze6OXwP7///Mo/zdy2ZXyHJhia+yscWZ+Hrb49ekI9csJvuic0p6ttJPjS72tmEesGR1vLT0Y/5T+SqOVmmbmN8hZygRxrEwgfo9oNI+8BBC7aYK2PCtTZZFwoO3KsEEttQjxzKTbzja9s8XslGxfKkw==</SignatureValue><KeyInfo><X509Data><X509Certificate>MIIDgzCCAmugAwIBAgIJALOc35pt94LuMA0GCSqGSIb3DQEBCwUAMFgxCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNaXNzb3VyaTERMA8GA1UEBwwIQ29sdW1iaWExFTATBgNVBAoMDEZvbGlvdGVrIEluYzEMMAoGA1UEAwwDSWRwMB4XDTE4MTEwOTE1MDI0MVoXDTM4MTEwNDE1MDI0MVowWDELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE1pc3NvdXJpMREwDwYDVQQHDAhDb2x1bWJpYTEVMBMGA1UECgwMRm9saW90ZWsgSW5jMQwwCgYDVQQDDANJZHAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDhX8PX+C/1orPpFnOIRy7UkrLU7YCq6DNzB5QSUzT366++ZCWFzx+Ub+HFxbR/htY/EAramlCNFawOSGS6mnWev/tiokGObXdMK6tAXyZZMc/u9Rg65EjM892Oep6gIEWgjnE+l7M8v84QOWqAl+GaeM8YZJKHXAZ+7MVMgkMWeYKrksvQdKrQjhyzqoLmBNL5yGBgEH1KEtFy0A0qYdiwdWptvaeWkTk6tp3kfminRaQ1bj/BmMwAWeDbE7EFkk7wF1ig4QhTINoVFQhPGa/+sPg+NuDNlGszDBV3fmfpHwPpjRr4zzoNyJnMvf3u1+C63c7DPSC+uKGvYlgeWbc/AgMBAAGjUDBOMB0GA1UdDgQWBBQvczxcOnaazyGJ8H3vi1vY6g24xDAfBgNVHSMEGDAWgBQvczxcOnaazyGJ8H3vi1vY6g24xDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAoMsoDnrEPCS+VIqVlnlbxxd4lx5AYMvUTZPugJ88+Jjp/1kkKbxWzbJBR1yl0v9quLoQ/u5XkYoSI3u/azydywpADlgsKHrL7Ger+ZU2pdSCK9LTbOP3gnginmPldB7LW6jxWxuEYadWLpYocEFU6Ua7XJUDOzMpO3SXxmhiyhvQC2PF0Q1uehNkwIpUP+9I9ulAXxjScyputgYjkWjiLYu+gcWYW6DmeWqJKyYR6XSwaa+QV4/UPupBmSc1Bx7BuF29+1RwJyTEI6Uz5wQe+lbzZ5ay3J3oa3lilwYg/HYq4mQzVucHEhQLsU9ZIfuGStMHX23sdzWuEBbcQgCCd</X509Certificate></X509Data></KeyInfo></Signature><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" /></samlp:Status></samlp:LogoutResponse>
=================

Here's my info log from the logout process

Sending logout request to http://myidentityprovider.com
=================
Sending logout request to http://myidentityprovider.com
=================
Federated logout not possible, redirecting to post-logout and clearing local session
=================
Received logout response Microsoft.IdentityModel.Tokens.Saml2.Saml2Id, redirecting to http://myidentityprovider.com/saml/slo?SAMLRequest=fZJdS8MwFIbvB%2FsPJbeyNv3Y1oatOBhCYXrhxAvvYpKu0XzUnlT2803rJpugkJvz8bznPYesgGqVtGRnD7Z3j%2BKjF%2BCCo1YGyHdpjfrOEEtBAjFUCyCOkf3mfkeSEJO2s84yq9Al8z9CAUTnpDUoqLZrJHmRFuw1LnKcUpYt2JImC1rXOc94yuZpnaHgWXTggTXyvKcAelEZcNQ4n8JxPovjGS6e4gXJUjLPX1Cw9WtIQ91INc61JIpqq6R%2Fpj9y8RmOkRPvIbOaLPESR4P3CJRF5XQSBKtxFTIO68qThLKMKnrFjlgSnXVX0RV3ofTgL1Ftgzvbaer%2BPlEcxmNG8lk9thKhqVQbzjsBgErXCN4Py4GWrrlxjdUU3m4PQ9Pg52zge9yFgZbsvYA%2FSGW4OJZxkSwwxkmenIhf9enkJ3%2F1Ocov&RelayState=MnZ2DPYtc9cY8CkEaR5CRJDz&SigAlg=http:%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256&Signature=LS0QmYpXX2utWqmEKQJmMeQukm%2FFFVUZCP8I0C7sIt1LklVK0NzuqrJgG9VGwO6uPBZObpZ%2FU9%2BZVddCoIGmg3FCKrhhW7hspsQNN%2FGqpf0QY3kxW%2Bt956TqgynW0yM4I9%2Fc7X%2F9Sy4keFu1uxihjemm%2BCNlZdRS71ch4SyG4YStmKZrWJns1T6H4m8d2eBK7O2KVn9iqwIh6OaV5S6obhpMH9gzx5Y01uc5fTm2gfdoExuVNsKbZB8ycois1MEEz7Uox5zRm09gEfCNMHKf2Dp%2Fwd7GmQoK84VvPoNrxl5047WxfKxkhQPTRFbM5h50peFjOlnFN0yKw9C3DARSBw%3D%3D
=================

After digging into the code a little, I've found if I do the following:

GetLogoutResponseState = (req) => { return null; }

...in my Saml2AuthenticationOptions.Notifications, it works as expected.

From here, I still suspect I'm configuring something wrong, or the IDP is sending the wrong data, but I don't know where StoredRequestState is being initialized. It appears that it contains the wrong returnUrl.

2
What is the method of the second request to logout? A POST containing a SamlResponse in the body?Anders Abel
Yes. After base64 decoding the SamlResponse it appears to contain a successful status code. Which is what I would expect: The idp let's the sp know that the SLO request was successful. Right?Dustin Smith
Yes, that sounds right. Please enable Katana logging to get the Saml2 log messages and see if that reveals anything.Anders Abel
Would the Katana logs reveal anything that the Logger within the SPOptions wouldn't reveal? My current Saml2Logger writes to text files, and I can supply those if that's what you're searching for. Although, they didn't look unusual IIRC.Dustin Smith
It's the output from the SPOptions logger I'm after. By default it uses Katana tracing, that's why I asked for that. Yes, those logs would help. Specifically to see if the received response is detected and handled and to see code path is taken in LogoutCommand.Run()Anders Abel

2 Answers

0
votes

Looking at the logs, I think that this is the sequence of events.

  1. SignOut on your application is hit and redirects to the logout endpoint in the Saml2 library.
  2. The logout endpoint is hit and initiates a logout request to the Idp.
  3. The Idp renders a page (that probably contains some kind of SAML response - but might be incorrect).
  4. The logout endpoint is hit again and this is wrong
    • Either this is a duplicate request.
    • Or it is a response that doesn't get properly detected by the Sustainsys.Saml2 library

What makes me really confused is that in the second run all the requirements for a federated logout are fulfilled. Specifically it does find the LogoutNameIdentifier. That shouldn't be possible, as the local session cookie is normally cleared when the redirect to the Idp is done.

To understand further what is happening I recommend that you download the Sustainsy.Saml2 source, link directly to the project and set a breakpoint at LogoutCommand.Run. That should help you understand better and be able to further examine the requests.

0
votes

I tracked my problem down to the LogoutCommand.Run being called twice during the initial GET request to /saml2/logout. Once from the Saml2AuthenticationHandler .ApplyResponseGrantAsync method, and once from the Saml2AuthenticationHandler.InvokeAsync.

The solution was to set Compatibility = new Compatibility { StrictOwinAuthenticationMode = true } in my SPOptions. Which prevents the LogoutCommand from executing within the ApplyResponseGrantAsync method.

I'm a little unclear on the Passive vs Active authentication modes, but my guess is that I'm indicating to the Sustainsys package that I'm controlling the authentication (including signing out) manually. Maybe Anders can clear that up?