Search code examples
samlidp

SAML IDP initiated SSO transaction


Webapp #1 has a button to webapp #2. When our users click that button, they should be redirected to webapp #2, and be automatically logged-in.

The integration is SAML, so webapp #1 (the IDP) sends a SAML "request" to webapp #2 (the SP), which returns a redirect URL, and webapp #1 redirects to it.

The SP gave me a URL to HTTP POST an assertion identifying the user via the "email" attribute, so I produced this:

<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" IssueInstant="2021-12-22T16:59:43.999Z" Version="2.0"><saml2:Issuer>http://www.whatever.com</saml2:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></ds:SignatureMethod><ds:Reference URI=""><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform><ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ds:DigestMethod><ds:DigestValue>...</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>...</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>...</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature><saml2:Conditions><saml2:OneTimeUse></saml2:OneTimeUse></saml2:Conditions><saml2:AuthnStatement AuthnInstant="2021-12-22T16:59:44.053Z" SessionIndex="cfb8f9b5-9616-47db-bc92-7588ce18cf62" SessionNotOnOrAfter="2021-12-22T16:59:44.068Z"><saml2:AuthnContext></saml2:AuthnContext></saml2:AuthnStatement><saml2:AttributeStatement><saml2:Attribute Name="email"><saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">si@captisintel.com</saml2:AttributeValue></saml2:Attribute></saml2:AttributeStatement></saml2:Assertion>

The SP responds with a 302 and a Location header that looks like this:

Location: ?SAMLRequest=nVNNj9owEP0rke%2FkS4VtLc...

Decoded:

<?xml version="1.0"?><samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="_f848f04c71671a745722" Version="2.0" IssueInstant="2021-12-22T18:36:19.337Z" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="http://auth.whatever2.com/saml/callback" Destination=""><saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">whatever2</saml:Issuer><samlp:NameIDPolicy xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" AllowCreate="true"/><samlp:RequestedAuthnContext xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" Comparison="exact"><saml:AuthnContextClassRef xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></samlp:RequestedAuthnContext></samlp:AuthnRequest>

When I visit that URL, I am presented with the login page. So this is not working.

I have 0 visibility over the SP logs to know what's wrong. All I can guess is that I am not posting this correctly. So does this look right, or should I post this differently? I tried posting the Assertion in the body as-is, wrapping it in a "<samlp:Response" envelope, wrapping it plus prefixing it with "SAMLResponse=". I tried each plain and deflated/base64 encoded.

Another clue is that the SP always returns a 302, no matter what I send them. Even if I send "BLAHBLAH" and not an actual SAML payload, they still respond with a 302.

Any advice?


Solution

  • In a IdP initiated flow, the IdP sends the SAML Response containing a Assertion in a POST like you do. If all goes well the SP should now consider the user authenticated.

    The SAML response should be sent in input control named SAMLResponse and should be Base64 encoded.

    <input type="hidden" name="SAMLResponse" value="<Base64 encoded SAML Response>"/>
    

    I would guess that what happens is that for some reason, the SP does not accept your SAMLResponse and instead starts the normal SP initiated SAML flow by sending a SAML request to the IdP to authenticate the user.

    Why your response is not accepted can be many things. Without the SP logs we can mostly guess.

    Off the bat I see that you assertion is missing a Bearer subject confirmation which is mandatory.

    From the SAML Profiles spec 4.1.4.2

    Any assertion issued for consumption using this profile MUST contain a element with at least one element containing a Method of urn:oasis:names:tc:SAML:2.0:cm:bearer. Such an assertion is termed a bearer assertion. Bearer assertions MAY contain additional elements. There is also more requirements on the assertion in the same chapter

    Some other ideas though:

    • You should have your assertion in a SAML response, but there are several boiler plate elements that need to be set. So ensure it is valid
    • It is not unusual to see that the response message itself must be signed and not just the assertion.

    I would suggest having a look at the SAML profiles spec 4.1.4.2 to understand what assertion and response must contain.

    And also, try agian to see if you can get any feedback on whats wrong from the SP to avoid banging your head bloody doing blind try and fail.

    SAML is not the most stright forward protocol there is and there is a lot to get right to get it working and get it secure. As I always try to say to people starting out with it: Dont build it your self, there is alot of good opensource alternatives out there for doing SAML that will make your system so much more secure and maintainable.

    If you want to get a better understanding of SAML and how to implement it I can recommend my book, SAML 2.0: Designing secure identity federation

    Good uck!