<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: WorkOS Developer Relations</title>
    <description>The latest articles on DEV Community by WorkOS Developer Relations (@workos_devrel).</description>
    <link>https://dev.to/workos_devrel</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F560796%2F8597af39-fab7-4694-b27c-27a0cd963b1c.jpg</url>
      <title>DEV Community: WorkOS Developer Relations</title>
      <link>https://dev.to/workos_devrel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/workos_devrel"/>
    <language>en</language>
    <item>
      <title>Fun with SAML SSO Vulnerabilities and Footguns</title>
      <dc:creator>WorkOS Developer Relations</dc:creator>
      <pubDate>Fri, 29 Jan 2021 21:06:25 +0000</pubDate>
      <link>https://dev.to/workos/fun-with-saml-sso-vulnerabilities-and-footguns-1feo</link>
      <guid>https://dev.to/workos/fun-with-saml-sso-vulnerabilities-and-footguns-1feo</guid>
      <description>&lt;p&gt;If you’re here, you’ve likely been tasked with building SAML-based SSO as a requirement for an enterprise deal. If you’re just diving into the problem space of SSO / SAML, we’d first suggest checking out &lt;a href="https://workos.com/blog/the-developers-guide-to-sso"&gt;The Developer’s Guide to SSO&lt;/a&gt;. Otherwise, buckle up for a brief but titillating foray into why XML-based authentication is... challenging.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is SAML SSO so vulnerability-prone
&lt;/h2&gt;

&lt;p&gt;The attack surface for SAML authentication is extensive, mostly due to the fact that SAML is XML-based. &lt;a href="https://everypageispageone.com/2016/01/28/why-does-xml-suck/"&gt;XML is a semantic-free meta-language&lt;/a&gt; - it’s hard to form, hard to read, and hard to parse. Combined with the high complexity of the &lt;a href="http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.pdf"&gt;SAML specification&lt;/a&gt; and the number of parties involved in establishing authentication, we get what often feels like a &lt;a href="http://www.laputan.org/mud/mud.html"&gt;big ball of mud&lt;/a&gt; and all the accompanying implications. Be prepared to tackle a steep learning curve, lots of bugs, high maintenance costs, attack vectors galore, and an absurd spread of edge cases.&lt;/p&gt;

&lt;p&gt;Most SAML SSO security vulnerabilities are introduced by Service Providers (SPs) improperly validating and processing SAML responses received from Identity Providers (IdPs). This happens because SAML SSO is typically not a core-value feature for an application, nor is the implementation common knowledge for most developers. Unknowns become even more unlikely to be identified and addressed when the pressure is on to just deliver something to unblock a high-value contract - as is oftentimes the case. However, to build SAML SSO safely and securely in-house requires significant buy-in and &lt;a href="https://stackoverflow.blog/2019/07/11/single-sign-on-sso-stack-overflow-okta-integration/"&gt;investment by teams&lt;/a&gt; - on the scale of months, representing hundreds of thousands of dollars in developer time.‍&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If not done right, you expose your application and your customers to potentially huge security risks.&lt;/strong&gt; To drive that home, here are just a few recently published SAML-related vulnerabilities:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nvd.nist.gov/vuln/detail/CVE-2019-15585"&gt;January 27, 2020&lt;/a&gt; - “... GitLab SAML integration had a validation issue that permitted an attacker to takeover another user's account.”&lt;br&gt;
&lt;a href="https://nvd.nist.gov/vuln/detail/CVE-2020-4427"&gt;May 7, 2020&lt;/a&gt; - “... an attacker could exploit this [SAML] vulnerability to bypass the authentication process and gain full administrative access to the system [IBM Data Risk Manager].”&lt;br&gt;
&lt;a href="https://nvd.nist.gov/vuln/detail/CVE-2018-21263"&gt;June 19, 2020&lt;/a&gt; - “An attacker could authenticate to a different user's [Mattermost] account via a crafted SAML response.”&lt;br&gt;
&lt;a href="https://nvd.nist.gov/vuln/detail/CVE-2020-2021"&gt;June 29, 2020&lt;/a&gt; - “... improper verification of signatures in PAN-OS SAML authentication enables an unauthenticated network-based attacker to access protected resources.”&lt;/p&gt;

&lt;p&gt;It should be evident by now that oversights in SAML implementations are ubiquitous and problematic, even among experienced engineering teams.&lt;/p&gt;

&lt;p&gt;So let’s dive into some of the more common security pitfalls developers building SAML-based SSO should be aware of, as well as cover a few suggested countermeasures. Just to be clear, &lt;strong&gt;this guide is by no means comprehensive&lt;/strong&gt; and is meant to provide a starting point for SAML security considerations as well as some follow-on resources.&lt;/p&gt;
&lt;h2&gt;
  
  
  Brief anatomy of a SAML response
&lt;/h2&gt;

&lt;p&gt;Let's say we're integrating our application with Okta via SAML. Below is an example of an XML document we might get when attempting to authenticate a user, containing a simplified but valid SAML response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;
&amp;lt;saml2p:Response xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:xs="http://www.w3.org/2001/XMLSchema" Destination="https://api.workos-test.com/auth/okta/callback" ID="id72697176167120131651975993" InResponseTo="_b464ac6d6621d7a1a814" IssueInstant="2019-11-27T02:45:30.657Z" Version="2.0"&amp;gt;
  &amp;lt;saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity"&amp;gt;http://www.okta.com/exk1klancwHzz1SNi357&amp;lt;/saml2:Issuer&amp;gt;
  &amp;lt;saml2p:Status xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"&amp;gt;
    &amp;lt;saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/&amp;gt;
  &amp;lt;/saml2p:Status&amp;gt;
  &amp;lt;saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xs="http://www.w3.org/2001/XMLSchema" ID="id7269717616793800631152500" IssueInstant="2019-11-27T02:45:30.657Z" Version="2.0"&amp;gt;
    &amp;lt;saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity"&amp;gt;http://www.okta.com/exk1klancwHzz1SNi357&amp;lt;/saml2:Issuer&amp;gt;
    &amp;lt;ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"&amp;gt;
      &amp;lt;ds:SignedInfo&amp;gt;
        &amp;lt;ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/&amp;gt;
        &amp;lt;ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/&amp;gt;
        &amp;lt;ds:Reference URI="#id7269717616793800631152500"&amp;gt;
          &amp;lt;ds:Transforms&amp;gt;
            &amp;lt;ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/&amp;gt;
            &amp;lt;ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"&amp;gt;
              &amp;lt;ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xs"/&amp;gt;
            &amp;lt;/ds:Transform&amp;gt;
          &amp;lt;/ds:Transforms&amp;gt;
          &amp;lt;ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/&amp;gt;
          &amp;lt;ds:DigestValue&amp;gt;k+u/Rfv5sDoEJ9Zsk9DCNzBiWQ4aeJtVCk21SYIFupc=&amp;lt;/ds:DigestValue&amp;gt;
        &amp;lt;/ds:Reference&amp;gt;
      &amp;lt;/ds:SignedInfo&amp;gt;
      &amp;lt;ds:SignatureValue&amp;gt;TxG611vjAtZ/+F2QFtzcByyh7Hb5Dq928NC6a9FvsfT8r/VENRQ6iUfcZiQDImsCNbc58p8ftYRiKf0pFsGz4Y821c/POBqjaW7eL7J6c7EwBJcSRX6/xGbPd1jgvc4QVPfZuchdjJL6a2vAXPaFM3BDa2mpqp2/bd4VqkjAibvoygqNaI/TzTT5E28nSAez39Y+dzL16jlo4d/5T3g0gqLwsbD0w6KveyJXSQpjyuj1nel3R1w8SZITZmdNBwiPwbk04iE7zWbTNVkh9Dgo+xhhwSpqwBzq4KiCvYl7HhHCgjJVKCPh28V/2xANqZjTtgNB3lrnmz/MwnRn9H5Jsg==&amp;lt;/ds:SignatureValue&amp;gt;
      &amp;lt;ds:KeyInfo&amp;gt;
        &amp;lt;ds:X509Data&amp;gt;
          &amp;lt;ds:X509Certificate&amp;gt;MIIDpDCCAoygAwIBAgIGAWz5LOgCMA0GCSqGSIb3DQEBCwUAMIGSMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEUMBIGA1UECwwLU1NPUHJvdmlkZXIxEzARBgNVBAMMCmRldi04Mzk3NDAxHDAaBgkqhkiG9w0BCQEWDWluZm9Ab2t0YS5jb20wHhcNMTkwOTAzMjIwODI1WhcNMjkwOTAzMjIwOTI1WjCBkjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBE9rdGExFDASBgNVBAsMC1NTT1Byb3ZpZGVyMRMwEQYDVQQDDApkZXYtODM5NzQwMRwwGgYJKoZIhvcNAQkBFg1pbmZvQG9rdGEuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzCXtN0wgczIzU3nKnFb64XZPJB4m+JWX45lUDYoxOLJJKpv41pQ9Cq/SmYTj3EL23ZHA5W/xr9jSbfPYOYiJ6R3T8q6n2cLFWTXsS7axRxTC7Y6tUK+7RM5yydWxB17CsgSCmMGAqux2Z1HkA9JEJxVXCQDTVHww7tjO6bFrRXdWszJt5f6ESNIHfKgm+WXole61Kz3IW4vCQRdfAl7eoK4MBWERJN16j9N9NLpGaPBYs65oF1LQ7WWWZQ/8oYgLKszVvCkxdSBcpym39Ob/fdtdmKesVnxPLJjK/wB3WXVjbbUHqYy/ArkoK2FteYXLHzBmX89HOMuzofUYGbWbvQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBnVSSM4KJ0jO2R3AxF1IKY6orTbHXgl6/glZbSN0AT7Lpf7fduo7n1RnxToaKDQyRPumMPvPnHUvpkfg3BMjUGKFXJJYEc3pKoSrt/aoDsjPLxri+mjK8x0vasGbj7G+T2J6//TPVnGCGYJMRBG7LUfTCbFVVdIQy7agwE/rIzYSbQFaM1RoUAzIjWzkuDVmO6zWtVBUZgySO1dphPctgItPUmZEQUX+EiEK7Azg7wQmOiPv9Kwmj3SSxPmwCHxXScQLQdzicmaI6hMbOeCcFJSuxbGDc1xbJKd/WbLy1ZrDlcDPky8cJILFnZoh/y5+LX7YUEnIwxtE5Ohiwt78dz&amp;lt;/ds:X509Certificate&amp;gt;
        &amp;lt;/ds:X509Data&amp;gt;
      &amp;lt;/ds:KeyInfo&amp;gt;
    &amp;lt;/ds:Signature&amp;gt;
    &amp;lt;saml2:Subject xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"&amp;gt;
      &amp;lt;saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"&amp;gt;demo@workos-okta.com&amp;lt;/saml2:NameID&amp;gt;
      &amp;lt;saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"&amp;gt;
        &amp;lt;saml2:SubjectConfirmationData InResponseTo="_b464ac6d6621d7a1a814" NotOnOrAfter="2019-11-27T02:50:30.657Z" Recipient="https://api.workos-test.com/auth/okta/callback"/&amp;gt;
      &amp;lt;/saml2:SubjectConfirmation&amp;gt;
    &amp;lt;/saml2:Subject&amp;gt;
    &amp;lt;saml2:Conditions xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" NotBefore="2019-11-27T02:40:30.657Z" NotOnOrAfter="2019-11-27T02:50:30.657Z"&amp;gt;
      &amp;lt;saml2:AudienceRestriction&amp;gt;
        &amp;lt;saml2:Audience&amp;gt;https://api.workos-test.com/auth/okta/callback&amp;lt;/saml2:Audience&amp;gt;
      &amp;lt;/saml2:AudienceRestriction&amp;gt;
    &amp;lt;/saml2:Conditions&amp;gt;
  &amp;lt;/saml2:Assertion&amp;gt;
&amp;lt;/saml2p:Response&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Like we mentioned earlier, the SAML spec is complex and responses can get lengthy, so this example is comparatively quite terse. Keeping things to what you should know before we go into SAML vulnerabilities, let’s walk through what the response (&lt;code&gt;&amp;lt;saml2p:Response&amp;gt;&lt;/code&gt;) is communicating:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Line 2 begins the SAML response, which has the unique ID &lt;code&gt;id72697176167120131651975993&lt;/code&gt; and is intended for consumption by the &lt;code&gt;workos-test&lt;/code&gt; service provider’s Assertion Consumer Service (ACS) URL, i.e. the endpoint &lt;code&gt;https://api.workos-test.com/auth/okta/callback&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Line 3 specifies the issuer (&lt;code&gt;&amp;lt;saml2:Issuer&amp;gt;&lt;/code&gt;) and contains the unique URI (also referred to as EntityID) of the IdP that issued the response, in this case &lt;code&gt;http://www.okta.com/exk1klancwHzz1SNi357&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Line 7 begins the assertion (&lt;code&gt;&amp;lt;saml2:Assertion&amp;gt;&lt;/code&gt;) with the unique ID &lt;code&gt;id7269717616793800631152500&lt;/code&gt;. An assertion is a package of information asserting the identity of a user, often containing additional user attributes like first / last name, email, ID, etc.&lt;/li&gt;
&lt;li&gt;Line 8 specifies the issuer of the assertion itself, in this case also &lt;code&gt;http://www.okta.com/exk1klancwHzz1SNi357&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Lines 9 - 30 contains the digital signature (&lt;code&gt;&amp;lt;ds:Signature&amp;gt;&lt;/code&gt;) over the assertion, which should be validated to determine the authenticity of the assertion.&lt;/li&gt;
&lt;li&gt;Lines 31 - 36 specify the subject (&lt;code&gt;&amp;lt;saml2:Subject&amp;gt;&lt;/code&gt;) of the assertion, i.e. the authenticated principal / user corresponding to the unique identifier found in &amp;lt;&lt;code&gt;saml2:NameID&lt;/code&gt;&amp;gt;, who in this case is &lt;code&gt;demo@workos-okta.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Line 37 (&lt;code&gt;&amp;lt;saml2:Conditions&amp;gt;&lt;/code&gt;) defines the window of time for which the assertion should considered valid, i.e. from NotBefore (inclusive) to NotOnOrAfter (exclusive).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the purposes of readability, the SAML 2.0 XML snippets in the remainder of this blog will be simplified, use shorthand, and be stripped of nodes that would otherwise be required in reality but are not relevant to what’s being illustrated. We’ll use a mythical IssueTracker, ContractManager, and PayrollService as hypothetical SPs that have implemented SAML authentication, which you should think of as placeholders for your application or other SAML SSO-enabled apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disable DTD processing
&lt;/h2&gt;

&lt;p&gt;The first step in processing a SAML response is parsing the payload. Parsing and loading an XML document into memory is an inherently expensive set of operations, but can be unexpectedly costly due to a feature of XML that allows references to external or remote documents, i.e. &lt;a href="https://en.wikipedia.org/wiki/Document_type_definition"&gt;Document Type Definitions&lt;/a&gt; (DTDs).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;!DOCTYPE Response [&amp;lt;!ENTITY ggwp SYSTEM "http://workos.com/¯\_(ツ)_/¯.xml"&amp;gt;]&amp;gt;
&amp;lt;Response&amp;gt;&amp;amp;ggwp;&amp;lt;/saml2p:Response&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a DTD is encountered, parsers will try to fetch and load the referenced document as well. If the referenced document is large enough or results in infinitely looping references, your server can be slowed or even brought down trying to complete the process. The same holds true if the payload itself is very large, DTDs or not. &lt;/p&gt;

&lt;p&gt;Two low-hanging mitigations you should implement to prevent buffer overflows are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Limiting SAML payload size to &amp;lt; 1MB. 1MB is a generous upper limit and should be tuned down based on average received payload size.&lt;/li&gt;
&lt;li&gt;Configuring your XML parser to never fetch remote files or try to load and parse DTDs. Some XML parsers do so by default, for example, Python’s &lt;a href="https://docs.python.org/3/library/xml.etree.elementtree.html"&gt;&lt;code&gt;xml.etree.ElementTree&lt;/code&gt;&lt;/a&gt; module.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;XML processing, and thus by extension SAML response processing, is vulnerable to buffer overflow attacks from other scenarios described later on in this post. And unfortunately, protecting your application from a service outage is among the most mild of outcomes compared to the &lt;a href="https://www.vsecurity.com//download/papers/XMLDTDEntityAttacks.pdf"&gt;possibilities exploiting XML DTD&lt;/a&gt; allows - it is a dark and anxiety-inducing rabbit hole. &lt;/p&gt;

&lt;p&gt;So, if you’re not writing your own XML parser (generally not suggested), it’s important to &lt;strong&gt;vet the XML parser(s) your application and its dependencies use&lt;/strong&gt; - ensure they handle other exploits like &lt;a href="https://en.wikipedia.org/wiki/Billion_laughs_attack"&gt;Billion Laughs&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Zip_bomb"&gt;Zip Bombs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validate the SAML response schema first
&lt;/h2&gt;

&lt;p&gt;The primary security mechanism in the SAML handshake is the cryptographic validation of &lt;a href="https://en.wikipedia.org/wiki/XML_Signature"&gt;XML Signatures&lt;/a&gt; (XML-DSig) - which establishes the trust chain between IdPs and SPs. XML-DSig validation should always be done prior to executing business logic; however, the separation between signature verification and operating on the rest of a SAML payload opens up SAML authentication to vulnerabilities exposed by what are called XML Signature Wrapping (XSW) attacks.  These attacks have numerous permutations which can result in outcomes such as (but not limited to):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Denial-of-service by inserting arbitrary elements that that lead to buffer overflows.&lt;/li&gt;
&lt;li&gt;Escalating permissions by injecting assertions that allow an adversary to impersonate and be authenticated as another user, like an account admin.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The exploit here consists of modifying the payload without invalidating any signatures - think &lt;a href="https://owasp.org/www-community/attacks/SQL_Injection"&gt;SQL Injection&lt;/a&gt; or &lt;a href="https://owasp.org/www-community/attacks/xss/"&gt;Cross Site Scripting,&lt;/a&gt; but for for XML.&lt;/p&gt;

&lt;p&gt;Original response (pre-XSW):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Response ID="foo"&amp;gt;
  &amp;lt;Signature&amp;gt;
    &amp;lt;SignedInfo&amp;gt;
      &amp;lt;Reference URI="#foo"&amp;gt;...&amp;lt;/Reference&amp;gt;
    &amp;lt;/SignedInfo&amp;gt;
  &amp;lt;/Signature&amp;gt;
  &amp;lt;Assertion ID="bar"&amp;gt;
    &amp;lt;Signature&amp;gt;
      &amp;lt;SignedInfo&amp;gt;
        &amp;lt;Reference URI="#bar"&amp;gt;...&amp;lt;/Reference&amp;gt;
      &amp;lt;/SignedInfo&amp;gt;
    &amp;lt;/Signature&amp;gt;
  &amp;lt;/Assertion&amp;gt;
&amp;lt;/Response&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modified response (post-XSW):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Response ID="xsw"&amp;gt;
  &amp;lt;Signature&amp;gt;
    &amp;lt;SignedInfo&amp;gt;
      &amp;lt;Reference URI="#foo"&amp;gt;...&amp;lt;/Reference&amp;gt;
    &amp;lt;/SignedInfo&amp;gt;
    &amp;lt;Response ID="foo"&amp;gt;
      &amp;lt;Assertion ID="bar"&amp;gt;
        &amp;lt;Signature&amp;gt;
          &amp;lt;SignedInfo&amp;gt;
            &amp;lt;Reference URI="#bar"&amp;gt;...&amp;lt;/Reference&amp;gt;
          &amp;lt;/SignedInfo&amp;gt;
        &amp;lt;/Signature&amp;gt;
      &amp;lt;/Assertion&amp;gt;
    &amp;lt;/Response&amp;gt;
  &amp;lt;/Signature&amp;gt;
  &amp;lt;Assertion ID="snek"&amp;gt;&amp;lt;/Assertion&amp;gt;
&amp;lt;/Response&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The broadest countermeasure to XSW attacks is &lt;strong&gt;validating the schema&lt;/strong&gt; of the SAML XML document. Payloads for SAML responses of any given IdP should have a deterministic standard schema that can be used as a reference in a schema compliance validation module, which should be executed prior to XML-DSig verification. Here are &lt;a href="https://github.com/onelogin/python3-saml/tree/master/src/onelogin/saml2/schemas"&gt;example schemas&lt;/a&gt; used by OneLogin’s &lt;code&gt;python3-saml&lt;/code&gt; package to perform &lt;a href="https://pythonhosted.org/python-saml/library/saml2.html#saml2.utils.OneLogin_Saml2_Utils.validate_xml"&gt;XML schema validation&lt;/a&gt;. &lt;strong&gt;Schemas should be vetted local copies&lt;/strong&gt; as opposed to being fetched from 3rd party remote locations at runtime or on server start. &lt;/p&gt;

&lt;p&gt;All of that being said, &lt;a href="https://www.nds.ruhr-uni-bochum.de/media/nds/veroeffentlichungen/2013/03/25/paper.pdf"&gt;schema validation isn’t foolproof&lt;/a&gt;; there is room for error in the validation module logic itself, as well as in the syntactic rigor of the reference schema. A second low-hanging countermeasure to XSW attacks that should be employed for the sake of redundancy is to &lt;strong&gt;always use absolute XPath expressions&lt;/strong&gt; to select elements in processes post-schema validation. Explicit absolute XPath expressions set an unambiguous expectation for the location of elements. &lt;/p&gt;

&lt;p&gt;Here’s an example of a valid response that’s been modified in an XSW attack (specifically a signature exclusion attack, more on that later):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Response ID="foo"&amp;gt;
  &amp;lt;Assertion ID="snek"&amp;gt;...&amp;lt;/Assertion&amp;gt;
  &amp;lt;Assertion ID="bar"&amp;gt;
    &amp;lt;Signature&amp;gt;
      &amp;lt;SignedInfo&amp;gt;
        &amp;lt;Reference URI="#bar"&amp;gt;...&amp;lt;/Reference&amp;gt;
      &amp;lt;/SignedInfo&amp;gt;
    &amp;lt;/Signature&amp;gt;
  &amp;lt;/Assertion&amp;gt;
&amp;lt;/Response&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This modification also exploits the common, incorrect, but not unreasonable assumption that a well-formed SAML response will only ever have a single assertion. So while XML-DSig verification would succeed for the signature returned by &lt;code&gt;doc.getElementsByTagName(“Signature”)[0]&lt;/code&gt;, the assertion returned and processed by &lt;code&gt;doc.getElementsByTagName(“Assertion”)[0]&lt;/code&gt; would be the injected snek assertion. This attack would have been more likely to fail if the XPath expression &lt;code&gt;“/Response/Assertion[0]/Signature”&lt;/code&gt; was used in the assertion signature validation logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check that you’re the intended recipient
&lt;/h2&gt;

&lt;p&gt;This sounds obvious, but make sure to check that a SAML response is intended for your app. This is low-hanging fruit that can prevent attacks exploiting IdPs that use a shared private signing key for all integrated SPs of a given tenant, as opposed to issuing unique keys per application. The most common attack entails the unauthorized lateral movement by a malicious user across an enterprise’s IdP-integrated apps:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7hgbrohj8g5khwqcmsf7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7hgbrohj8g5khwqcmsf7.png" alt="lateral-access" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A second scenario would be a third party impersonating your app and gaining user access. The likelihood of this attack vector being exploited is pretty low because the malicious party would need to be in possession of the IdP’s private signing key (&lt;a href="https://www.cyberark.com/resources/threat-research-blog/golden-saml-newly-discovered-attack-technique-forges-authentication-to-cloud-apps"&gt;among other things&lt;/a&gt;) - but we’re mentioning it for the sake of completeness:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fj3kqda2lsfwwclrb74cz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fj3kqda2lsfwwclrb74cz.png" alt="mitm-sequence" width="800" height="721"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are Service Providers that don’t bother to check if they’re the intended recipient, relying only on the validity of assertion signatures to prove the sender is a trusted party and that the response is valid. But as we’ve illustrated above, valid signatures aren’t enough to prevent unwanted access.&lt;/p&gt;

&lt;p&gt;When dealing with security and authentication, stay paranoid my friend, and have some additional redundancies to catch edge cases. In this case, some easy-to-implement checks are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The response destination is present, non-empty, and refers to an ACS URL that you are expecting.&lt;/li&gt;
&lt;li&gt;The response and assertion issuers refer to an IdP EntityID you recognize.&lt;/li&gt;
&lt;li&gt;You are the specified audience for any assertions.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Response Destination="https://api.your-app.com/auth/okta/callback"&amp;gt;
  &amp;lt;Issuer&amp;gt;http://www.okta.com/abcd123&amp;lt;/Issuer&amp;gt;
  &amp;lt;Assertion&amp;gt;
    &amp;lt;Issuer&amp;gt;http://www.okta.com/abcd123&amp;lt;/Issuer&amp;gt;
  &amp;lt;/Assertion&amp;gt;
  &amp;lt;Conditions&amp;gt;
    &amp;lt;AudienceRestriction&amp;gt;
      &amp;lt;Audience&amp;gt;https://api.your-app.com/auth/okta/callback&amp;lt;/Audience&amp;gt;
    &amp;lt;/AudienceRestriction&amp;gt;
  &amp;lt;/Conditions&amp;gt;
&amp;lt;/Response&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Validate every signature
&lt;/h2&gt;

&lt;p&gt;Like we mentioned earlier, cryptographic validation of signatures is the primary mechanism for determining the authenticity of SAML payloads. It’s a good idea to read through the &lt;a href="https://www.w3.org/TR/xmldsig-core1/"&gt;W3C specs for XML signature processing&lt;/a&gt; because it anchors SAML security, but the pithy statement to remember when handling SAML responses is &lt;strong&gt;only process entities that are validly signed&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;There’s a class of attacks that exploit poorly implemented SP security logic known as signature exclusion attacks. These attacks will insert forged unsigned elements, banking on the possibility that the SP’s security logic will skip XML-DSig validation if no signature is found. Another common slipup is implementing validation logic that checks only the first assertion’s signature and then assumes remaining assertions are signed. Here are some rules to follow to avoid the most common oversights:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;→ The entire SAML response itself should be signed&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fm4egcndddzrg45mwlxdv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fm4egcndddzrg45mwlxdv.png" alt="saml-response-signed" width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;→ Every assertion should be signed&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Something to note is that you &lt;em&gt;should not&lt;/em&gt; assume a response will have only one assertion, and furthermore, each assertion should be signed in its entirety.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxrcjjuy3p24bkh5ooms9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxrcjjuy3p24bkh5ooms9.png" alt="Alt Text" width="800" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;→ Only accept encryption schemes from an explicitly defined set of algorithms&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/"&gt;JWT validation&lt;/a&gt; similarly can sometimes overlook this point, and in fact, there was a related &lt;a href="https://insomniasec.com/cdn-assets/Insomnia_Security_-_JWT_Validation_Bypass_in_Auth0_Authentication_API.pdf"&gt;Auth0 vulnerability&lt;/a&gt; exposed as recently as last year.  If possible, we suggest hardcoding your validation logic to only accept &lt;a href="https://stackoverflow.com/a/39239395"&gt;RS256&lt;/a&gt; as the encryption scheme. Otherwise, verify Algorithm attribute values are from a &lt;a href="https://www.w3.org/TR/xmldsig-core1/#sec-AlgID"&gt;recognized set of URIs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Bad:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;ds:SignatureMethod Algorithm=""/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;ds:SignatureMethod Algorithm="none"/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Good:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you’re planning on using a third party library to do SAML processing and XML-DSig validation, be sure to vet what it does under the hood - especially if it does XML parsing and processing as well. As much as possible, try to avoid libraries that depend directly or indirectly on &lt;a href="https://www.aleksey.com/xmlsec/"&gt;&lt;code&gt;xmlsec1&lt;/code&gt;&lt;/a&gt; or its dependency &lt;a href="http://xmlsoft.org/"&gt;&lt;code&gt;libxml2&lt;/code&gt;&lt;/a&gt; - many SAML and XML libraries are just language-specific bindings for &lt;code&gt;xmlsec1&lt;/code&gt; and &lt;code&gt;libxml2&lt;/code&gt;, and as a result, &lt;a href="https://security-tracker.debian.org/tracker/source-package/libxml2"&gt;inherit all the same security vulnerabilities&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We highly suggest doing &lt;strong&gt;XML-DSig validation in native code&lt;/strong&gt; - in fact, at WorkOS we built our SAML library from scratch for greater control, and so we can respond immediately to newly discovered &lt;a href="https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=SAML"&gt;SAML-related vulnerabilites&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Use the canonicalized XML
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/XML_Signature#XML_canonicalization"&gt;XML canonicalization&lt;/a&gt; is the process of transforming an XML document into a semantically identical but normalized representation (more on this later). The &lt;code&gt;CanonicalizationMethod&lt;/code&gt; specifies which &lt;a href="https://www.w3.org/TR/xmldsig-core1/#sec-c14nAlg"&gt;canonicalization algorithm&lt;/a&gt; to apply - the most commonly occurring one we’ve seen is &lt;code&gt;xml-exc-c14&lt;/code&gt;, which strips XML comments during transformation. This generally wouldn’t be a problem, except for the fact that most SAML libraries perform canonicalization &lt;em&gt;prior&lt;/em&gt; to doing XML-DSig validation on the canonicalized assertions. Why is this a concern? Here’s what can happen if the library’s underlying XML element text extraction logic doesn’t consider inner comment nodes. &lt;/p&gt;

&lt;p&gt;Suppose I’m a disgruntled developer who would dearly like a substantial raise from my company that uses Okta + PayrollService (this is all fictional, I’m not disgruntled). I used to work at PayrollService and so am pretty confident this exploit I’m about to attempt will work, because patching it never got prioritized in favor of feature work, and because no one external has noticed anything amiss, yet... Anyway. &lt;/p&gt;

&lt;p&gt;I know that WorkOS IT always uses &lt;code&gt;itadmin@workos.com&lt;/code&gt; as an administrative account for every app used within the organization (we don’t actually). So equipped with this knowledge and using my personal domain, I create a PayrollService account for a user &lt;code&gt;itadmin@workos.com.disgruntled.dev&lt;/code&gt; and set up SSO with Okta. Self-service free trials FTW. &lt;/p&gt;

&lt;p&gt;Here’s a simplified SAML assertion authenticating me as a PayrollService user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Assertion ID="id123"&amp;gt;
  &amp;lt;Signature&amp;gt;
    &amp;lt;SignedInfo&amp;gt;
      &amp;lt;CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/&amp;gt;
      &amp;lt;SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/&amp;gt;
      &amp;lt;Reference URI="#id123"&amp;gt;
    &amp;lt;/SignedInfo&amp;gt;
    &amp;lt;SignatureValue&amp;gt;...&amp;lt;/SignatureValue&amp;gt;
    &amp;lt;KeyInfo&amp;gt;...&amp;lt;/KeyInfo&amp;gt;
  &amp;lt;/Signature&amp;gt;
  &amp;lt;Subject&amp;gt;
    &amp;lt;NameID&amp;gt;itadmin@workos.com.disgruntled.dev&amp;lt;/NameID&amp;gt;
  &amp;lt;/Subject&amp;gt;
&amp;lt;/Assertion&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I can modify the SAML assertion by adding a comment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Assertion ID="id123"&amp;gt;
  &amp;lt;Signature&amp;gt;
    &amp;lt;SignedInfo&amp;gt;
      &amp;lt;CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/&amp;gt;
      &amp;lt;SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/&amp;gt;
      &amp;lt;Reference URI="#id123"&amp;gt;
    &amp;lt;/SignedInfo&amp;gt;
    &amp;lt;SignatureValue&amp;gt;...&amp;lt;/SignatureValue&amp;gt;
    &amp;lt;KeyInfo&amp;gt;...&amp;lt;/KeyInfo&amp;gt;
  &amp;lt;/Signature&amp;gt;
  &amp;lt;Subject&amp;gt;
    &amp;lt;NameID&amp;gt;itadmin@workos.com&amp;lt;!----&amp;gt;.disgruntled.dev&amp;lt;/NameID&amp;gt;
  &amp;lt;/Subject&amp;gt;
&amp;lt;/Assertion&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This modified assertion doesn’t invalidate the signature because canonicalization will strip comments before XML-DSig verification - it will have &lt;strong&gt;the same canonical representation as the unmodified assertion&lt;/strong&gt;. Great!&lt;/p&gt;

&lt;p&gt;So now, believing the assertion is authentic, PayrollService checks to see which user is being authenticated. Its SAML library grabs the user identifier from the NameID element, but it, incorrectly, only reads the inner text of the element’s first node, i.e. &lt;code&gt;itadmin@workos.com&lt;/code&gt;. Then PayrollService determines that &lt;code&gt;itadmin@workos.com&lt;/code&gt; is indeed a user, and just like that, I’m in and ready to approve raises for myself and all my friends! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://duo.com/blog/duo-finds-saml-vulnerabilities-affecting-multiple-implementations"&gt;Duo Labs discovered this vulnerability&lt;/a&gt; back in 2018, and while some of the more commonly used open source SAML libraries have since addressed it, there undoubtedly remain many internal or open source libraries that haven’t. So echoing recommendations from before, &lt;strong&gt;vet your SAML and XML libraries.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ideally, comments wouldn’t be purged prior to XML-DSig validation, so that injected comments would indeed cause validation to fail - but that’s unrealistic or inadvisable to try to enforce for a couple reasons, which we’ll leave for another time. Instead, you’ll want to make sure that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The canonicalized XML document is used in processes post-signature verification.&lt;/li&gt;
&lt;li&gt;Or, barring the first, that full text-extraction is handled gracefully when inner comments exist. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Avoiding replay attacks
&lt;/h2&gt;

&lt;p&gt;Replay attacks occur when a SAML response is captured and re-sent to the Service Provider for duplicate processing, which can have outcomes like denial-of-service for your users, or if the SP charges by API request, eating up request quotas. The most robust countermeasure against replay attacks is preventing the capture of SAML responses in the first place - which can be accomplished by &lt;strong&gt;using HTTPS&lt;/strong&gt; (should be a given already) and never exposing the SAML response to the browser. Here’s what the authentication flow could look like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4pvezd8whbaxlevk73gm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4pvezd8whbaxlevk73gm.png" alt="back-channel-sequence" width="800" height="635"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, very few IdPs actually support the &lt;a href="https://en.wikipedia.org/wiki/SAML_2.0#Artifact_Resolution_Protocol"&gt;Artifact Resolution Protocol&lt;/a&gt;, a requirement of back-channel SAML authentication. As a result, most SAML implementations rely entirely on the browser to relay SAML payloads between the SP and IdP:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fylm27ha1yyvt25x2mjef.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fylm27ha1yyvt25x2mjef.png" alt="front-channel-sequence" width="800" height="544"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because the SAML response is exposed to the user agent, it becomes trivial to capture (by inspecting the &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/troubleshoot_saml_view-saml-response.html"&gt;dev console&lt;/a&gt;, &lt;a href="https://owasp.org/www-community/attacks/xss/"&gt;XSS&lt;/a&gt;, or with malicious browser plugins) and replay a response. So another approach to mitigating replay attacks is to maintain a cache of previously seen assertion IDs, immediately rejecting responses containing any assertion with an ID that already exists in the cache. A cache item could have a TTL equal to the expiry datetime of the originating assertion, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Assertion&amp;gt;
  &amp;lt;Conditions NotBefore="2020-08-12T02:40:30.657Z" NotOnOrAfter="2020-08-12T02:50:30.657Z"&amp;gt;
    &amp;lt;AudienceRestriction&amp;gt;
      &amp;lt;Audience&amp;gt;https://api.foo.com/auth/callback&amp;lt;/Audience&amp;gt;
    &amp;lt;/AudienceRestriction&amp;gt;
  &amp;lt;/Conditions&amp;gt;
&amp;lt;/Assertion&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A third much less robust but much faster to implement countermeasure (which should be implemented regardless) is logic that &lt;strong&gt;strictly enforces the validation window for assertions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;One last thing to note is that most SPs that implement SAML SSO use 3rd party open source SAML libraries for speed to value, yet are not protected against replay attacks because the strongest countermeasures require additional architectural changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;As with most software engineering, building SAML SSO for enterprises follows the &lt;a href="https://en.wikipedia.org/wiki/Ninety-ninety_rule"&gt;90-90 rule&lt;/a&gt;. There’s a hill to climb to get to an MVP, and an entirely different hill if you’d like to sleep at night. SAML-based authentication is rife with sleeping dragons, of which this guide only introduces a very small subset - but hopefully it has been useful in helping you avoid some of them. &lt;strong&gt;If product requirements allow, try to avoid integrating with IdPs using SAML&lt;/strong&gt;; a more modern, safer, and simpler alternative protocol is &lt;a href="https://openid.net/connect/"&gt;OpenID Connect&lt;/a&gt;. And if you’re thinking twice about building SAML SSO yourself in-house, then consider using a 3rd party vendor that makes it their business to provide a safe, performant, highly available, and super fast to integrate SSO API... like &lt;a href="https://workos.com/features/sso"&gt;WorkOS&lt;/a&gt;! &lt;/p&gt;

&lt;h2&gt;
  
  
  Additional references and further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.pdf"&gt;(OASIS) Security Assertion Markup Language (SAML) V2.0 Technical Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oasis-open.org/security/saml/v2.0/saml-sec-consider-2.0-os.pdf"&gt;(OASIS) Security and Privacy Considerations forthe OASIS Security Assertion MarkupLanguage (SAML) V2.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf"&gt;(OASIS) Bindings for the OASIS Security Assertion Markup Language (SAML) V2.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/TR/xmldsig-bestpractices/"&gt;(W3C) XML Signature Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.researchgate.net/publication/221560863_XML_signature_element_wrapping_attacks_and_countermeasures#read"&gt;(IBM) XML signature element wrapping attacks and countermeasures&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/2008/xmlsec/papers/xmlEncCountermeasuresW3C.pdf"&gt;(W3C) Technical Analysis of Countermeasures against Attack on XML Encryption – or – Just AnotherMotivation for Authenticated Encryption&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.usenix.org/system/files/conference/usenixsecurity12/sec12-final91.pdf"&gt;(Horst Görtz Institute for IT-Security) On Breaking SAML: Be Whoever You Want to Be&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nds.ruhr-uni-bochum.de/media/nds/veroeffentlichungen/2013/03/25/paper.pdf"&gt;(Horst Görtz Institute for IT-Security) On the Effectiveness of XML Schema Validation for Countering XML Signature Wrapping Attacks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nds.ruhr-uni-bochum.de/media/ei/veroeffentlichungen/2014/11/11/SaaSSAMLSecurity_CCSW2014.pdf"&gt;(Horst Görtz Institute for for IT-Security) Your Software at my Service: Security Analysis of SaaS Single Sign-On Solutions in the Cloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://arxiv.org/ftp/arxiv/papers/1401/1401.7483.pdf"&gt;(OWASP) Secure SAML validation to prevent XML signature wrapping attacks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/SAML_Security_Cheat_Sheet.html"&gt;(OWASP) SAML Security Cheat Sheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.python.org/3/library/xml.html#xml-vulnerabilities"&gt;(Python stdlib) Vulnerabilities in Python XML processing modules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.stackallocated.com/blog/2020/saml-idp-no-shared-keys/"&gt;You need multiple SAML IDP signing keys&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.economyofmechanism.com/office365-authbypass.html"&gt;The road to hell is paved with SAML Assertions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.economyofmechanism.com/github-saml.html"&gt;The road to your codebase is paved with forged assertions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.samltool.com/online_tools.php"&gt;OneLogin SAML Developer Tools&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>security</category>
      <category>saml</category>
      <category>enterprise</category>
      <category>authentication</category>
    </item>
    <item>
      <title>The Developer’s Guide to SSO</title>
      <dc:creator>WorkOS Developer Relations</dc:creator>
      <pubDate>Fri, 22 Jan 2021 19:59:09 +0000</pubDate>
      <link>https://dev.to/workos/the-developer-s-guide-to-sso-5ae8</link>
      <guid>https://dev.to/workos/the-developer-s-guide-to-sso-5ae8</guid>
      <description>&lt;p&gt;If you want more people using your product, the easiest place to start is making it easier to actually sign up. &lt;strong&gt;Adding SSO to your app&lt;/strong&gt; will help you land those larger enterprise deals and decrease the signup friction that keeps causing your visitors to drop off. For modern developers though, the world of XML, SOAP, and OASIS standards can be opaque. Fear not: our guide will walk you through SSO: what it is, why it’s important, and best practices for getting it up, running and integrated with your app.&lt;/p&gt;

&lt;h2&gt;
  
  
  The basics: what SSO is and why you should care
&lt;/h2&gt;

&lt;p&gt;The easiest way to understand SSO quickly is to think about your app’s authentication as a service. Most developers build the service themselves: you take care of creating usernames and passwords, adding them into a database, and checking credentials every time someone logs in. But in the same way that you skip building payments infrastructure and use Stripe, you can “outsource” your auth and have someone else do it; and that’s what SSO is.&lt;/p&gt;

&lt;p&gt;If you’ve heard of SSO before, you’re probably thinking of it as a security feature, and that’s true; but where it really shines is through &lt;strong&gt;increasing engagement&lt;/strong&gt;. Making it easier to sign up for and sign into your product lowers friction for users, increases retention through smoother login flows, and helps you land those elusive enterprise deals (many enterprises can’t work with vendors who don’t support SSO).&lt;/p&gt;

&lt;p&gt;Apps with SSO enabled allow users to authenticate through someone else’s service: instead of you managing usernames and passwords, you integrate with a provider like Okta or OneLogin that does it for you. Those services – called Identity Providers, or IDPs if you want to save time – are generally more full featured and secure than what your typical growing startup would be able to build themselves.&lt;/p&gt;

&lt;p&gt;SSO is a given among everyone from high growth startups to more traditional enterprises. Here’s the Dropbox sign in page for SSO:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F86t9lco9t75pro96oadv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F86t9lco9t75pro96oadv.png" alt="Dropbox SSO Login Page" width="800" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Slack, Asana, Notion, Airtable, and Trello all support SSO too, and some even charge extra for it (you can find them on the &lt;a href="https://sso.tax/"&gt;SSO Wall of Shame&lt;/a&gt;). It’s pretty much part of the standard growth playbook.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learning the lingo: SAML, SPs, IDPs, and assorted acronyms
&lt;/h2&gt;

&lt;p&gt;Let’s get a little deeper into how SSO works. One thing worth noting: SAML isn’t the only protocol you can use to implement SSO. OAuth (1.0 and 2.0) are also popular, as well as WS-Fed and OpenID Connect. The broad concepts can carry over across protocols, too.&lt;/p&gt;

&lt;p&gt;If you’re integrating SSO into your app, you’re a &lt;strong&gt;service provider&lt;/strong&gt; (SP). Your app is the service. The provider that you’re “outsourcing” identity to – like Okta or OneLogin – is called the &lt;strong&gt;identity provider&lt;/strong&gt; (IDP). Simple enough, right? Where things start to get a bit more complex is when your app needs to communicate with identity providers to actually authenticate your users. SSO works with a communication protocol called &lt;strong&gt;SAML&lt;/strong&gt; (security assertion markup language) that governs these phone lines.&lt;/p&gt;

&lt;p&gt;SAML – and by extension, how you build and work with SSO – works through &lt;strong&gt;assertions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2d975u2qxmoczamedlpg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2d975u2qxmoczamedlpg.png" alt="SAML flow" width="800" height="687"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s walk through a typical SAML flow, starting with a user trying to sign in through your site.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The SAML request from the SP&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When a user navigates to your login page (to log in, of course), they’ll either enter their email or click a button that takes them to an IDP portal like Okta. Your app issues a SAML request (and a browser redirect) to the IDP: it’s basically saying “hey, this user wants to sign in, do me a favor and verify that I should let ‘em in.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The SAML assertion from the IDP&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At the IDP, they’ll enter their full credentials, and deal with more extensive security measures like 2FA. Once they’ve successfully authenticated with the IDP, the IDP sends your app a response containing an assertion: this user is good to go, and you can let them in.&lt;/p&gt;

&lt;p&gt;SAML works via XML (for all those SOAP fans out there. Nobody? Ok). Here’s an example of what an a response containing an assertion might look like (thanks to &lt;a href="https://www.samltool.com/generic_sso_res.php"&gt;OneLogin&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_8e8dc5f69a98cc4c1ff3427e5ce34606fd672f91e6" Version="2.0" IssueInstant="2014-07-17T01:01:48Z" Destination="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"&amp;gt;
  &amp;lt;saml:Issuer&amp;gt;http://idp.example.com/metadata.php&amp;lt;/saml:Issuer&amp;gt;
  &amp;lt;samlp:Status&amp;gt;
    &amp;lt;samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/&amp;gt;
  &amp;lt;/samlp:Status&amp;gt;

  &amp;lt;saml:Assertion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" ID="pfxa099680e-6fc0-2c7a-90fa-4202bb29faa4" Version="2.0" IssueInstant="2014-07-17T01:01:48Z"&amp;gt;
    &amp;lt;saml:Issuer&amp;gt;http://idp.example.com/metadata.php&amp;lt;/saml:Issuer&amp;gt;
      &amp;lt;ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"&amp;gt;
        &amp;lt;ds:SignedInfo&amp;gt;&amp;lt;ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/&amp;gt;
        &amp;lt;ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/&amp;gt;
        &amp;lt;ds:Reference URI="#pfxa099680e-6fc0-2c7a-90fa-4202bb29faa4"&amp;gt;
          &amp;lt;ds:Transforms&amp;gt;
            &amp;lt;ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/&amp;gt;
            &amp;lt;ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/&amp;gt;
          &amp;lt;/ds:Transforms&amp;gt;&amp;lt;ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/&amp;gt;

          &amp;lt;ds:DigestValue&amp;gt;YOCfzMPwhVQibcTRRyuCb5vlT DU=&amp;lt;/ds:DigestValue&amp;gt;
        &amp;lt;/ds:Reference&amp;gt;
      &amp;lt;/ds:SignedInfo&amp;gt;

      &amp;lt;ds:SignatureValue&amp;gt;VXQGwtQsc/rTuCFspZwD6k4i6fKr4ymYfCiI5Ve9JO5LYRG7VNPzIq5Mr/JW/0btpui4cmQVK//wA89nLe+g2wxDizx32CnOBsshoF3YTDOs586SJt+Ty/h/X886Xhqu8XsdMiD/spyU8rGhIQP2OL65k6HoSFxtPqKt1+KOdkE=&amp;lt;/ds:SignatureValue&amp;gt;

      &amp;lt;ds:KeyInfo&amp;gt;
        &amp;lt;ds:X509Data&amp;gt;
          &amp;lt;ds:X509Certificate&amp;gt;MIICajCCAdOgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBSMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lbG9naW4gSW5jMRcwFQYDVQQDDA5zcC5leGFtcGxlLmNvbTAeFw0xNDA3MTcxNDEyNTZaFw0xNTA3MTcxNDEyNTZaMFIxCzAJBgNVBAYTAnVzMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQKDAxPbmVsb2dpbiBJbmMxFzAVBgNVBAMMDnNwLmV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZx+ON4IUoIWxgukTb1tOiX3bMYzYQiwWPUNMp+Fq82xoNogso2bykZG0yiJm5o8zv/sd6pGouayMgkx/2FSOdc36T0jGbCHuRSbtia0PEzNIRtmViMrt3AeoWBidRXmZsxCNLwgIV6dn2WpuE5Az0bHgpZnQxTKFek0BMKU/d8wIDAQABo1AwTjAdBgNVHQ4EFgQUGHxYqZYyX7cTxKVODVgZwSTdCnwwHwYDVR0jBBgwFoAUGHxYqZYyX7cTxKVODVgZwSTdCnwwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQByFOl+hMFICbd3DJfnp2Rgd/dqttsZG/tyhILWvErbio/DEe98mXpowhTkC04ENprOyXi7ZbUqiicF89uAGyt1oqgTUCD1VsLahqIcmrzgumNyTwLGWo17WDAa1/usDhetWAMhgzF/Cnf5ek0nK00m0YZGyc4LzgD0CROMASTWNg==&amp;lt;/ds:X509Certificate&amp;gt;
        &amp;lt;/ds:X509Data&amp;gt;
      &amp;lt;/ds:KeyInfo&amp;gt;
    &amp;lt;/ds:Signature&amp;gt;

    &amp;lt;saml:Subject&amp;gt;
      &amp;lt;saml:NameID SPNameQualifier="http://sp.example.com/demo1/metadata.php" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"&amp;gt;_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7&amp;lt;/saml:NameID&amp;gt;
      &amp;lt;saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"&amp;gt;
      &amp;lt;saml:SubjectConfirmationData NotOnOrAfter="2024-01-18T06:21:48Z" Recipient="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"/&amp;gt;
      &amp;lt;/saml:SubjectConfirmation&amp;gt;
    &amp;lt;/saml:Subject&amp;gt;

    &amp;lt;saml:Conditions NotBefore="2014-07-17T01:01:18Z" NotOnOrAfter="2024-01-18T06:21:48Z"&amp;gt;
      &amp;lt;saml:AudienceRestriction&amp;gt;
        &amp;lt;saml:Audience&amp;gt;http://sp.example.com/demo1/metadata.php&amp;lt;/saml:Audience&amp;gt;
      &amp;lt;/saml:AudienceRestriction&amp;gt;
    &amp;lt;/saml:Conditions&amp;gt;

    &amp;lt;saml:AuthnStatement AuthnInstant="2014-07-17T01:01:48Z" SessionNotOnOrAfter="2024-07-17T09:01:48Z" SessionIndex="_be9967abd904ddcae3c0eb4189adbe3f71e327cf93"&amp;gt;
      &amp;lt;saml:AuthnContext&amp;gt;
        &amp;lt;saml:AuthnContextClassRef&amp;gt;urn:oasis:names:tc:SAML:2.0:ac:classes:Password&amp;lt;/saml:AuthnContextClassRef&amp;gt;
      &amp;lt;/saml:AuthnContext&amp;gt;
    &amp;lt;/saml:AuthnStatement&amp;gt;

    &amp;lt;saml:AttributeStatement&amp;gt;
      &amp;lt;saml:Attribute Name="uid" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"&amp;gt;
        &amp;lt;saml:AttributeValue xsi:type="xs:string"&amp;gt;test&amp;lt;/saml:AttributeValue&amp;gt;
      &amp;lt;/saml:Attribute&amp;gt;

      &amp;lt;saml:Attribute Name="mail" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"&amp;gt;
        &amp;lt;saml:AttributeValue xsi:type="xs:string"&amp;gt;test@example.com&amp;lt;/saml:AttributeValue&amp;gt;
      &amp;lt;/saml:Attribute&amp;gt;

      &amp;lt;saml:Attribute Name="eduPersonAffiliation" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"&amp;gt;
        &amp;lt;saml:AttributeValue xsi:type="xs:string"&amp;gt;users&amp;lt;/saml:AttributeValue&amp;gt;
        &amp;lt;saml:AttributeValue xsi:type="xs:string"&amp;gt;examplerole1&amp;lt;/saml:AttributeValue&amp;gt;
      &amp;lt;/saml:Attribute&amp;gt;
    &amp;lt;/saml:AttributeStatement&amp;gt;
  &amp;lt;/saml:Assertion&amp;gt;
&amp;lt;/samlp:Response&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The flow we just outlined is called &lt;strong&gt;SP-initiated&lt;/strong&gt;, because it started at your app, and you’re the service provider. There’s another way this can go down though: users can start at their IDP (like the Okta app directory), click on which app they want to sign into, and then authenticate and redirect. That’s called &lt;strong&gt;IDP-initiated&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting practical: how to actually add SSO to your app
&lt;/h2&gt;

&lt;p&gt;Like pretty much anything in software, there are two ways to add SSO to your app: you can build it yourself or pay someone else to do it for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Building SSO from scratch&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Building SSO yourself is all about handling and working with the protocol you choose: assuming you’re targeting larger companies, we’re talking about SAML here. This isn’t a technical tutorial, but here are a few high level components you’ll need to write:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A SAML controller for handling requests and providing responses to your integrated IDPs&lt;/li&gt;
&lt;li&gt;A SAML service to verify x509 certs, entity IDs, and IDP URLs, alongside parsing SAML assertions and creating and validating SAML responses. You’ll particularly enjoy the XML parsing and IDP-specific request formats&lt;/li&gt;
&lt;li&gt;A strategy to correctly authenticate users in your app based on the attributes that IDPs send back (you’ll need to normalize these if you’re supporting multiple customers)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If any of this sounds weirdly unfamiliar to you, that’s because it probably is: there’s a lot of upfront research required to understand the right way to do it. It’s not as simple as adding a new frontend library and skimming through the docs.&lt;/p&gt;

&lt;p&gt;Part of the challenge of building SSO from scratch is customization: you’ll need to build SAML flows for each IDP independently. SAML is a standard, and like any good standard it’s often fractured and can sometimes be a pain to work with. As usual, XKCD gets it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbyhpog1bsnf6nn8myb7s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbyhpog1bsnf6nn8myb7s.png" alt="xkcd standards" width="500" height="283"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Over the past few years, the web dev ecosystem has developed a few packages that take care of some of the repeatable work. Middleware like &lt;a href="http://www.passportjs.org/"&gt;passport.js&lt;/a&gt; can help you avoid building everything from scratch; or if your backend is in Python, OneLogin offers a &lt;a href="https://github.com/onelogin/python-saml"&gt;python-saml&lt;/a&gt; package.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Use an SSO provider&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you don’t want to build SSO yourself (I mean, why would you?), there are a bunch of great third party services that offer SDKs and packages to make integration as easy as a few lines of code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-&amp;gt; WorkOS (we’re biased)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//workos.com"&gt;WorkOS&lt;/a&gt; lets you add SSO to your app with just a few lines of code, and it’s completely free. Integrate once and you can support SAML with IDPs like Okta, GSuite, OneLogin, and more. Here’s what that same SAML flow above might look like with something like WorkOS:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fc7rtj2rrysch7v0o2yr8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fc7rtj2rrysch7v0o2yr8.png" alt="SAML Flow with WorkOS" width="800" height="526"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;WorkOS ships with something pretty cool – &lt;a href="https://workos.com/docs/admin-portal/guide"&gt;Admin Portal&lt;/a&gt; – that allows your end users to configure their own SSO connections to their IDP of choice.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbemlzpwvjhw0qlc5gh1h.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbemlzpwvjhw0qlc5gh1h.gif" alt="Admin Portal" width="720" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Doing this manually is rough: you need to coordinate with enterprise IT admins, exchange URLs and certificates, and build custom infrastructure like field mappers for SAML profiles. It’s not just frustrating; it drags out the integration cycle and takes up sales, support, and success time too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-&amp;gt; Auth0&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://auth0.com/pricing/"&gt;Auth0&lt;/a&gt; is identity as a service: they offer SSO (among other products) that easily integrates into your app and supports all of the IDPs you’d ever need. Auth0 for social connections is free up to 7K users, and plans start at $23/mo for 1K users. If you want enterprise connections (think: SAML), you’ll need to move into the Developer Pro pricing tier, which starts at $28/mo for 100 users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-&amp;gt; AWS Cognito&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/cognito/"&gt;Cognito&lt;/a&gt; is AWS’s identity-as-a-service product, and supports SSO with SAML, OAuth 2.0, and OpenIDConnect. Cognito supports IDPs like GSuite and Facebook, and pricing is...well, it’s AWS, so you’re on your own.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-&amp;gt; GCP Identity Platform&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;GCP’s identity-as-a-service goes by “&lt;a href="https://cloud.google.com/identity-platform"&gt;Identity Platform&lt;/a&gt;” (catchy), and supports the standard feature set. It also comes with some interesting built in Google features like Machine Learning based security measures (identifying compromised accounts). Pricing starts at $0.015 per SAML MAU when you’re over 50 MAUs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best practices from some engineers who have done it before
&lt;/h2&gt;

&lt;p&gt;Here are a few tips that might make your SSO integration process just a bit easier, whether you’re using a third party provider building it from scratch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Security&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disallow username and password logins, password resets, and email address changes&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If an organization is using SSO with your product, give admins the ability to disable username / password based auth for their users. It creates a more seamless SSO experience by avoiding false login starts and keeps things secure.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Enforce session timeouts&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Expire idle user sessions to make sure users aren't signed in indefinitely — it's good practice to grab the SAML response's session timeout value and use that, but there are cases where having a "time to live" setting for each account is useful too.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Force sign-in for active browser sessions&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If your app gets a new sign-in request, replace any currently active browser sessions with the newly authenticated session. This is particularly important for apps that lean toward multi tab use, like IDEs or CRMs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Routing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Ask users for info to determine the right IDP&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you plan on supporting multiple IDPs in your SSO implementation, ask users for their email address, account subdomain, or unique account URL to determine the correct identity provider for their login.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Make sure to deep link&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you’re asking users to authenticate from an existing product page or they’re expecting to land somewhere in particular in your product, you’ll want to implement deep linking in your SAML flows. You can use &lt;a href="https://stackoverflow.com/questions/34350160/what-is-exactly-relaystate-parameter-used-in-sso-ex-saml"&gt;SAML’s RelayState parameter&lt;/a&gt; to get this working. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. UX&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disable email verification for SSO users&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If your app sends verification emails on username / password signups, disable that for SSO authentication: it’s not necessary.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Use Just-In-Time (JIT) User Provisioning for first time sign-ins&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jumpcloud.com/blog/jit-provisioning-defined"&gt;JIT user provisioning&lt;/a&gt; automates the account creation process for users signing into your app for the first time via SAML. If they exist in their organization’s IDP, you’ll just create their account automatically instead of asking them to sign up from scratch. This lowers friction for new users significantly and helps make your app more attractive to larger orgs.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Prompt users for IDP logouts&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When users log out of your app, prompt them to see if they’d like to log out of their IDP as well. The two intents often overlap, and you can save your users some time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do next
&lt;/h2&gt;

&lt;p&gt;If you’re convinced that it’s time to add SSO into your app (you should be, hopefully), you’ll want to start by deciding how you’re going to do it. Building it yourself can give you better customization options, but using a third party service like WorkOS will save your engineering team a lot of time and effort. Here are a few resources that might help you steer in the right direction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For more details on SAML: Duo’s &lt;a href="https://duo.com/blog/the-beer-drinkers-guide-to-saml"&gt;The Beer Drinker’s Guide to SAML&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;For more details on Passport.js: freeCodeCamp’s &lt;a href="https://www.freecodecamp.org/news/learn-how-to-handle-authentication-with-node-using-passport-js-4a56ed18e81e/"&gt;guide to handling auth with passport.js&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;For more details on JIT: JumpCloud’s &lt;a href="https://jumpcloud.com/blog/jit-provisioning-defined"&gt;What is Just-In-Time User Provisioning?&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>security</category>
      <category>architecture</category>
      <category>sso</category>
      <category>guide</category>
    </item>
  </channel>
</rss>
