The Developer’s Guide to SSO

3,031
WorkOS
APIs for enterprise features like SSO/SAML

If you want more people using your product, the easiest place to start is making it easier to actually sign up. Adding SSO to your app 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.

The basics: what SSO is and why you should care

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.

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 increased engagement. 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).

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.

SSO is a given among everyone from high growth startups to more traditional enterprises. Here’s the Dropbox sign-in page: they’ve built SSO with Google and Apple as two headline IDPs (they also support Okta).

Slack, Asana, Notion, Airtable, and Trello all support SSO too, and some even charge extra for it (you can find them on the SSO Wall of Shame). It’s pretty much part of the standard growth playbook.

Learning the lingo: SAML, SPs, IDPs, and assorted acronyms

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.

If you’re integrating SSO into your app, you’re a ​service provider​ (SP). Your app is the service. The provider that you’re “outsourcing” identity to – like Okta or OneLogin – is called the ​identity provider ​(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 ​SAML ​(Security Assertion Markup Language) that governs these phone lines.

SAML – and by extension, how you build and work with SSO – works through assertions.

Let’s walk through a typical SAML flow, starting with a user trying to sign in through your site.

1. The SAML request from the SP

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.”

2. The SAML assertion from the IDP

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.

SAML works via XML (for all those SOAP fans out there. Nobody? Ok). Here’s an example of what a response containing an assertion might look like (thanks to OneLogin):


    <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">
        <saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer>
        <samlp:Status>
            <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
        </samlp:Status>

        <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">
            <saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer>
            <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                <ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
                <ds:Reference URI="#pfxa099680e-6fc0-2c7a-90fa-4202bb29faa4">
                <ds:Transforms>
                    <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
                    <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                </ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>

                <ds:DigestValue>YOCfzMPwhVQibcTRRyuCb5vlT DU=</ds:DigestValue>
                </ds:Reference>
            </ds:SignedInfo>

            <ds:SignatureValue>VXQGwtQsc/rTuCFspZwD6k4i6fKr4ymYfCiI5Ve9JO5LYRG7VNPzIq5Mr/JW/0btpui4cmQVK//wA89nLe+g2wxDizx32CnOBsshoF3YTDOs586SJt+Ty/h/X886Xhqu8XsdMiD/spyU8rGhIQP2OL65k6HoSFxtPqKt1+KOdkE=</ds:SignatureValue>

            <ds:KeyInfo>
                <ds:X509Data>
                <ds:X509Certificate>MIICajCCAdOgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBSMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lbG9naW4gSW5jMRcwFQYDVQQDDA5zcC5leGFtcGxlLmNvbTAeFw0xNDA3MTcxNDEyNTZaFw0xNTA3MTcxNDEyNTZaMFIxCzAJBgNVBAYTAnVzMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQKDAxPbmVsb2dpbiBJbmMxFzAVBgNVBAMMDnNwLmV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZx+ON4IUoIWxgukTb1tOiX3bMYzYQiwWPUNMp+Fq82xoNogso2bykZG0yiJm5o8zv/sd6pGouayMgkx/2FSOdc36T0jGbCHuRSbtia0PEzNIRtmViMrt3AeoWBidRXmZsxCNLwgIV6dn2WpuE5Az0bHgpZnQxTKFek0BMKU/d8wIDAQABo1AwTjAdBgNVHQ4EFgQUGHxYqZYyX7cTxKVODVgZwSTdCnwwHwYDVR0jBBgwFoAUGHxYqZYyX7cTxKVODVgZwSTdCnwwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQByFOl+hMFICbd3DJfnp2Rgd/dqttsZG/tyhILWvErbio/DEe98mXpowhTkC04ENprOyXi7ZbUqiicF89uAGyt1oqgTUCD1VsLahqIcmrzgumNyTwLGWo17WDAa1/usDhetWAMhgzF/Cnf5ek0nK00m0YZGyc4LzgD0CROMASTWNg==</ds:X509Certificate>
                </ds:X509Data>
            </ds:KeyInfo>
            </ds:Signature>

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

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

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

            <saml:AttributeStatement>
            <saml:Attribute Name="uid" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
                <saml:AttributeValue xsi:type="xs:string">test</saml:AttributeValue>
            </saml:Attribute>

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

            <saml:Attribute Name="eduPersonAffiliation" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
                <saml:AttributeValue xsi:type="xs:string">users</saml:AttributeValue>
                <saml:AttributeValue xsi:type="xs:string">examplerole1</saml:AttributeValue>
            </saml:Attribute>
            </saml:AttributeStatement>
        </saml:Assertion>
        </samlp:Response>

The flow we just outlined is called ​SP-initiated​, 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 ​IDP-initiated​.

Getting practical: how to add SSO to your app

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.

1. Building SSO from scratch

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:

  • A SAML controller for handling requests and providing responses to your integrated IDPs.
  • 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.
  • 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).

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.

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.

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 ​passport.js​ can help you avoid building everything from scratch; or if your backend is in Python, OneLogin offers a python-saml​ package.

2. Use an SSO provider

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.

➞ WorkOS (we're biased)

WorkOS 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:

WorkOS ships with something pretty cool – WorkOS.js – that allows your end-users to configure their own​ SSO connections to their IDP of choice.

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.

➞ Auth0

Auth0​ 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.

➞ AWS Cognito

Cognito 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.

➞ GCP Identity Platform

GCP’s identity-as-a-service goes by “​Identity Platform​” (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.

Best practices from some engineers who have done it before

Here are a few tips that might make your SSO integration process just a bit easier, whether you’re using a third-party provider or building it from scratch.

1. Security

Disallow username and password logins, password resets, and email address changes

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.

Enforce session timeouts

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.

Force sign-in for active browser sessions

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.

2. Routing

Ask users for the information to determine the right IDP

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.

Make sure to deep link

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 ​SAML’s RelayState parameter​ to get this working.

3. UX

Disable email verification for SSO users

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

Use Just-In-Time (JIT) User Provisioning for first time sign-ins

JIT user provisioning​ automates the account creation process for users signing in to 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 organizations.

Prompt users for IDP logouts

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.

What to do next

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 steer you in the right direction:

WorkOS
APIs for enterprise features like SSO/SAML
Tools mentioned in article
Open jobs at WorkOS
Developer Success Engineer
USA timezones
About WorkOS: WorkOS is a developer platform that helps make apps enterprise-ready. We build tools and services for developers to help them implement features like Single Sign-On, Directory Sync, Multi-Factor Auth, and Audit Logs. We're a fully-distributed team with employees across US, UK and EU time zones. We're well-funded, having recently raised an $80M Series B. Our fast growing customer base includes thousands of rapidly growing SaaS companies like Webflow, Vercel, Brex, PlanetScale, Loom, and Drata. About the role: WorkOS is looking to continue to build our Developer Success Engineering team. As WorkOS is a technical product made by developers, for developers, Developer Success Engineers are critical to the customer experience. You will be on WorkOS’s front line, providing technical support, talking with developers and building developer relationships. You will be the expert on our products and main point of contact between our engineers and developers (customers). You will spend your time communicating with developers, and troubleshooting their issues. This role is best suited for someone who is technically-minded, empathetic, passionate, and loves solving problems under pressure. <li>Understand and be an expert in WorkOS’s products and code base </li><li>Provide technical support to developers on best practices for integrating with our API</li><li>Troubleshoot any issues that arise from the developer and diagnose any blockers</li><li>Engage customers for feedback to help shape product development</li><li>Work cross-functionally within the company</li><div><br></div> <li>2+ years experience in a technical support role with coding experience</li><li>Working with customers on a technical product</li><li>Exceptional verbal and written communication skills</li><li>Passion for educating and breaking down complicated concepts</li><li>Quick learner and relentlessly inquisitive</li>
Software Engineer
Americas and European Time Zones
About WorkOS 🚀 WorkOS is a developer platform that helps make apps enterprise-ready. We build tools and services for developers to help them implement features like Single Sign-On, Directory Sync, Multi-Factor Auth, and Audit Logs. We're a fully-distributed team with employees across US and EU time zones. We're well-funded, having recently raised an $80M Series B. Our fast growing customer base includes thousands of rapidly growing SaaS companies like Webflow, Vercel, Brex, PlanetScale, Loom, and Drata. About the role 💭 WorkOS is growing rapidly and building out our core team of full-stack engineers! We’re searching for passionate engineers who obsess over developer tools and documentation. We’re also looking for teammates who have profound empathy with other developers, and actively seek different perspectives and feedback to inform the products you’ll help build. Successful candidates will love taking ideas from zero to one, creating beautiful software experiences with modern tools, and care deeply about creating the best possible products. <li>Scope projects, document decisions, and flesh out epics and stories</li><li>Spearhead development of tooling and instrumentation that improves our API reliability</li><li>Debug user issues and adapt product to remove points of friction</li><li>Write technical blog posts (e.g., “How to implement SSO with <a href="http://Node.js" class="postings-link">Node.js</a>”)</li><li>Speak with users regularly to get feedback on new product development</li><li>Help recruit and assess future engineering team members&nbsp;</li> <li>Intellectually curious, independently driven, and passionate about great products</li><li>Expert-level skill in at least one programming language and familiarity with others</li><li>Experience working on small teams in fast-paced environments</li><li>Excellent prioritization skills</li><li>Experience building and shipping web applications and/or API services end-to-end</li><li>Our stack is TypeScript, React, and Postgres (bonus points if you are familiar with these, but it's not required)</li><li>A growth mindset, high emotional intelligence, and strong communication skills</li><li>Our stack is TypeScript, React, and Postgres (bonus points if you are familiar with these, but it's not required)</li>
Experienced Software Engineer - Product
Americas and European Time Zones
About WorkOS 🚀 WorkOS is a developer platform that helps make apps enterprise-ready. We build tools and services for developers to help them implement features like Single Sign-On, Directory Sync, Multi-Factor Auth, and Audit Logs. We're a fully-distributed team with employees across US and EU time zones. We're well-funded, having recently raised an $80M Series B. Our fast growing customer base includes thousands of rapidly growing SaaS companies like Webflow, Vercel, Brex, PlanetScale, Loom, and Drata. About the role 💭 WorkOS builds tools for developers that unlock equal access to the enterprise market. We’re growing rapidly and expanding our team of full-stack product engineers. Our engineers obsess over the developer experience, actively seeking out feedback and new perspectives to inform the products we build. We’re searching for engineers who share this empathetic approach to solving problems. Successful candidates will love taking ideas from zero to one, creating beautiful software experiences with modern tools, and care deeply about creating the best possible products. <li>Inspire new ways to build and think about how we build our products</li><li>Scope projects and document decisions with a developer-focused in mind</li><li>Understand market needs and map decisions to strategy</li><li>Debug user issues and adapt products to remove points of friction</li><li>Speak with customers regularly to get feedback on new product development</li><li>Build new SDKs and third-party integrations</li><li>Spearhead development of tooling and instrumentation that improves our API reliability</li><li>Collaborate with sales and solutions engineering for customer feedback</li><li>Work directly with leadership on greenfield ideas</li><li>Help recruit and assess future engineering team members</li> <li>5+ years of industry software development experience</li><li>Intellectually curious, independently driven, and passionate about great products</li><li>Expert-level skill in at least one programming language and familiarity with others</li><li>Experience working on small teams in fast-paced environments</li><li>Excellent prioritization skills</li><li>Experience building and shipping web applications and/or API services end-to-end</li><li>Our stack is TypeScript, React, and Postgres (bonus points if you are familiar with these, but it's not required)</li><li>A growth mindset, high emotional intelligence, and strong communication skills</li><li>Empathy for developers and technical audiences</li><li>Bonus: Experienced in B2B SaaS, developer tools, or a related industry</li>
Experienced Software Engineer - Frontend
Americas and European Time Zones
WorkOS builds tools for developers that unlock equal access to the enterprise market. We’re growing rapidly and are looking for an experienced front-end engineer to join our team to lead large-scale and nebulous projects. We obsess over the developer experience and talk directly to the engineers at our customers to inform the products we build. We’re searching for people who share this empathetic approach to solving problems. Successful candidates will love staying up-to-date on the latest in server rendering, data fetching, new approaches to routing, component architecture patterns, DOM and CSS updates and also feel comfortable dipping into the logic and API layer. We’re the team that builds Radix UI and we care deeply about world-class design, accessibility, and interactivity. <li>Build and document reusable component patterns that allow other engineers to ship their features with good UX</li><li>Design and implement complex routing and data fetching systems at scale</li><li>Articulate trade-offs of changes to complex systems and provide insight on technical decision-making across the company</li><li>Own and organize multi-person projects, delegate work, and drive projects forward while bringing others with you</li><li>Help set the tone and raise the bar for UI quality</li><li>Speak with customers regularly to get feedback on new product development</li><li>Help recruit and assess future engineering team members</li><li>Help represent the team publicly through blog posts, public docs, public design system resources</li><li>Help contribute to new Radix components and maintenance</li> <li>7+ years of industry software development experience with interactive apps</li><li>Prior EM, TLM, tech lead, or product experience that demonstrates driving toward business outcomes</li><li>Has built, shipped, and supported a feature-rich dashboard or app</li><li>Experience owning projects and driving work based on customer impact</li><li>A keen sense of balancing pragmatism and quality</li><li>Understanding common a11y patterns, ability to implement and educate others</li><li>High level of design/UX taste</li><li>Expert-level skill in React, CSS, TypeScript</li><li>Familiarity with backend fundamentals and database concepts</li><li>Experience building and shipping web applications end-to-end</li><li>Intellectually curious, independently driven, and passionate about great products</li><li>Bonus: Experienced in B2B SaaS, developer tools, or a related industry</li>
Verified by
Developer Success Engineer
You may also like