The Developer’s Guide to SSO

2,906
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 Relations
USA timezones
About WorkOS: WorkOS is a developer platform that helps make apps enterprise-ready. We provide pre-built features and APIs for SSO/SAML, Audit Trail, Directory/SCIM sync, and more. WorkOS is a well-funded, fully distributed team with employees across US time zones. About the role: WorkOS is looking to add an experienced Developer Relations (“DevRel”) advocate to our growing team.  Your mission will be to build a thriving and connected developer community around our platform. You'll work directly with developers on a daily basis to broaden awareness of WorkOS and gain key insights/feedback from developers. Some things we're looking for: <li>Prolific producer of high quality technical “how-to” content (blogs, webinars, demos, talks) addressing common user needs, latest technology advances, and emerging best practices</li><li>Proven ability to distribute content and leverage pieces to broaden awareness of the WorkOS brand,&nbsp;via existing connections </li> <li>Built a developer-focused community previously</li><li>Presented at meetups, conferences, and other ecosystem events</li><li>Created/produced events (podcasts, roundtables, hackathons)</li><li>Managed sponsorships of events, newsletters, etc.</li><div><br></div> <li>Existing relationships with fellow ecosystem influencers and open source leaders</li><li>Ongoing connections with user group members to build personal relationships and deeply understand their needs, usage, journeys, and barriers to adoption </li><div><br></div> <li>Written code where required to support how-to content, blog posts, presentations</li><li>Created sample applications, documentation, and tutorials</li>
Developer Success Engineer
USA timezones
About WorkOS: WorkOS is a developer platform that helps make apps enterprise-ready. We provide pre-built features and APIs for SSO/SAML, Audit Trail, Directory/SCIM sync, and more. WorkOS is a well-funded, fully distributed team with employees across US time zones. About the role: WorkOS is looking to build out our Developer Success Engineering team! As WorkOS is a technical product made by developers, for developers, Developer Success Engineers are critical to every step of the customer experience— from demoing to integration and ongoing support. You’ll be the expert on our products and main point of contact between our engineers and developers. You’ll spend approximately 60-70% of your time communicating with developers and 30-40% of your time diving into issues. This role is best suited for someone who is technically-minded, empathetic, passionate and loves solving problems under pressure. <li>Provide technical advice to developers on best practices for integrating with our API</li><li>Continuously engage users with product developments and new demo videos </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>Improve our documentation to increase product adoption </li> <li>2-3 years working in a technical role w/coding experience</li><li>Previous experience 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>Deeply involved in multiple developer communities </li><li>Experience in B2B SaaS</li><li>Experience in supporting Enterprise IT&nbsp;</li>
Software Engineer
USA timezones
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>Build new SDKs and third-party integrations</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 developers regularly to get feedback on new product development</li><li>Help recruit and assess future engineering team members&nbsp;</li> <li>3+ 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>
Engineering Manager
Americas and European Time Zones
About WorkOS: WorkOS is a developer platform that helps make apps enterprise-ready. We provide pre-built features and APIs for SSO/SAML, Audit Trail, Directory/SCIM sync, and more. WorkOS is a well-funded, fully distributed team with employees across US time zones. About the Role: WorkOS is looking for an Engineering Manager to empower and grow a team of highly skilled engineers. At WorkOS, we lead by serving our teams with empathy, knowledge, and coordination. In this role you would be focused on people and projects, and that excites you. As Engineering Manager, we're interested in your experience growing an engineering team, managing highly technical projects and building a safe environment where engineers can focus on what they love: shipping code. <li>Ensure proper team focus on priorities, milestones and deliverables.</li><li>Understand the team. You care about people and think they are the most exciting part of the job.</li><li>Work in-step with technical leads on architectural decisions and project scope.</li><li>Support your team through one-on-ones, setting goals, and facilitating conversations to ensure career progression, well-being, and performance.</li><li>Foster a healthy, thriving and inclusive environment.</li><li>Lead the effort to attract, recruit, and retain diverse engineering talent.</li> <li><b>Project management expertise.</b> You are highly organized, detail-oriented, and able to manage impactful projects across functional teams. You track KPIs, milestones and hit your deliverables.</li><li><b>Passion for people. </b>You're passionate about managing, mentoring, and crafting opportunities to support your teams’ success. You don’t shy away from challenging performance conversations, and you recognize the relationship between clear, direct feedback and career growth. Ideally, you’d have at least 2 to 3 years of experience managing a product-focused engineering team.</li><li><b>Low ego. </b>As a manager you understand your role isn’t to be the “smartest” engineer in the room nor is it to make big technical decisions. You aren’t focused on being right, you’re focused on what’s right for the team. You love a job where you help others do their best work and aren’t attracted to the spotlight. </li><li><b>Technical experience. </b>You’ve previously been in a highly-technical role and understand what it takes to be a great software engineer. The companies you have worked at aren’t companies with technical departments, they are high tech companies with technical products. You understand technology but you are more driven by organizing people and defining processes for team success. To lead these teams, you partner closely and build trust with engineers who deeply understand the product. </li><li><b>Recruiting know-how.</b> You love building teams (and know how challenging it is). You have experience partnering with internal recruiting teams to drive effective and fair hiring processes. You realize that recruiting is a large (and important!) portion of your role. </li><li><b>Distributed work or management experience.</b> You’ve been a remote employee or have managed people in remote settings before. You recognize managing a remote team comes with its own rewards and challenges, and are committed to putting frameworks in place to flourish in this setting.</li>
Verified by
Developer Success Engineer
You may also like