In this article
January 15, 2026
January 15, 2026

How to add SSO to your homegrown auth in a day

A practical guide for adding enterprise SSO to an existing auth system without rebuilding everything

You already have auth. It works. Users can sign in with email/password, maybe Google, maybe GitHub. You have sessions, refresh tokens, “forgot password,” rate limits, and all the other little auth gremlins, mostly tamed.

Then your first B2B buyer says: “Do you support SSO? Procurement won’t sign unless our employees can use Okta.”

And suddenly, your perfectly fine login page needs to speak fluent enterprise.

This tutorial is for exactly that situation: you have an existing auth system and want to add SSO without rebuilding everything. We’ll use the WorkOS standalone SSO API (because it’s designed to plug into an existing auth stack) and have SSO up-and-running in a day.

!!If, instead of the standalone SSO API, you want to consider a full user management solution, complete with The World’s Best Login Box ™️, powered by Radix, check out our docs.!!

Why adding SSO is hard (and why you shouldn’t custom-build it)

There’s a reason so many companies make very good money selling authentication infrastructure. Roughly half of that reason has four letters: SAML.

On paper, SSO looks like a checkbox feature. In practice, it’s a long-running relationship with every identity provider your customers have ever configured, inherited, or duct-taped together. Let’s unpack why deciding to add SSO by yourself is a choice you will live to regret.

How I Met Your Mother meme saying You Chose Poorly

The reality of IdP diversity

Supporting SSO often starts with a confident internal statement: “We support SAML.”

That confidence usually lasts until the second or third enterprise customer. That’s because enterprise customers don’t all use the same identity provider (IdP). You’ll see Okta, Microsoft Entra ID (Azure AD), Google Workspace, Ping, OneLogin, and a long tail of “we set this up in 2014 and it cannot be touched.”

You build SSO for your first IdP and everything works fine.

The next one needs a slightly different NameID.

Another insists on a different binding.

Suddenly, “SAML support” isn’t a single implementation; it’s a growing pile of exceptions. Each new IdP quietly expands the scope of what “done” means, and every assumption baked into the first implementation eventually comes back to renegotiate terms.

WorkOS’s SSO is compatible with any IdP that support SAML or OIDC. This means that you add one single integration and you get the superpower of supporting dozens of identity providers.

The brittle XML monster behind the curtain

Underneath the SAML hood is verbose, brittle XML with namespaces, transforms, and signature semantics that make most engineers’ eyes glaze over. An errant namespace here, a canonicalization difference there, and a signature that looks right but fails cryptographic validation. This is not a happy path; it’s a hairpin turn that every homegrown implementation eventually revisits when the IdP rotates certificates or changes metadata formats.

Opaque failure modes that eat your day

When things go wrong, SSO failures don’t hand you clean HTTP errors. You get base64 blobs, subtle assertion validation errors, mismatches between signed elements and expected transforms, or silent drops due to clock skew. These aren’t quick to debug, and they’re especially unfriendly to automated testing. Engineers find themselves chasing logs, XML dumps, and metadata differences instead of building product features.

Security checks that are shockingly easy to miss

Security in SSO isn’t just “use HTTPS.” There are subtle, critical validations that, if omitted or implemented incorrectly, can introduce serious vulnerabilities: XML signature verification, replay prevention, proper clock skew handling, strict artifact resolution, and certificate pinning. Skipping or misconfiguring any of these can silently make an implementation unsafe, even if it appears to work for most logins.

The hidden support and maintenance cost

The real cost of SSO shows up after launch. IdPs rotate certificates, change metadata endpoints, enforce new signing requirements, and occasionally have outages of their own. Homegrown SAML codebases that looked fine in staging start failing at 8:57 a.m. in production, with a support queue full of “it worked yesterday” tickets and no structured way to triage them.

Debugging SSO becomes its own discipline: you’re decoding assertions, reviewing XML canonicalization, and reconciling clock drift between systems you don’t control. What started as a feature now feels like a relentless maintenance treadmill.

!!Not convinced? For more on how SAML sucks and you should pay someone to deal with it on your behalf, see Why implementing SAML from scratch is a terrible idea.!!

What we will build

Now that you know the perils of building your own SSO solution, let’s continue by adding the WorkOS integration to your app and check SSO off your list once and for all.

In this tutorial we will build a classic SP-initiated flow:

  1. User clicks “Sign in with SSO” in your app.
  2. Your app redirects them to WorkOS.
  3. WorkOS redirects them to the customer’s IdP (e.g., Okta or Azure AD).
  4. After authentication, WorkOS redirects back to your callback URL with a short-lived code.
  5. Your backend exchanges the code for a WorkOS SSO Profile, then creates/links a local user and starts your normal session.
Flow diagram showing a SAML SSO flow using WorkOS

It doesn’t matter whether the IdP you want to configure uses SAML or OIDC. The WorkOS SSO service is designed to conform to the OAuth 2.0 framework specification, abstracting away the underlying authentication handshakes between different IdPs. This single integration with WorkOS will allow your app to talk with any identity provider, no matter what protocol they speak, SAML or OIDC.

Step 1: Install the WorkOS Node SDK

We will use Node in this tutorial. If your app uses a different language, follow the SSO Quickstart and choose the SDK that works for you.

	
npm install @workos-inc/node
	

Step 2: Set your secrets

To make calls to WorkOS, you need to provide the API key and the client ID. You can find these values in the dashboard.

Screenshot of the WorkOS dashboard landing page

Store these values as managed secrets and pass them to the SDK when needed.

	
WORKOS_API_KEY='sk_example_123456789'
WORKOS_CLIENT_ID='client_123456789'
	

Step 3: Configure your callback URL

Next, you need to configure where in your app the user should be redirected after successfully authenticating with their Identity Provider (IdP).

Add your callback URL on the WorkOS dashboard Redirects page.

Screenshot of the WorkOS dashboard Redirect URIs page

In Sandbox environments, http and localhost are allowed, but in Production the redirect URIs must always be https.

Store your callback URL also as a managed secret and pass it to the SDK when needed.

	
WORKOS_REDIRECT_URI='https://dashboard.my-app.com'
	

Step 4: Add an endpoint to initiate SSO

This endpoint generates an authorization URL and redirects the browser to WorkOS. When a user clicks Login with SSO this is the endpoint they will be redirected to.

The Next.js example below uses the Test Organization that is available in your staging environment and uses a mock identity provider. It’s created to help you test your SSO integration without having to go through the process of setting up an account with a real identity provider.

	
import type { NextApiRequest, NextApiResponse } from 'next';
import { WorkOS } from '@workos-inc/node';

const workos = new WorkOS(process.env.WORKOS_API_KEY);
const clientId = process.env.WORKOS_CLIENT_ID;

export default (_req: NextApiRequest, res: NextApiResponse) => {
  // Use the Test Organization ID to get started. Replace it with
  // the user’s real organization ID when you finish the integration.
  const organization = 'org_test_idp';

  // The callback URI WorkOS should redirect to after the authentication
  const redirectUri = process.env.WORKOS_REDIRECT_URI;

  const authorizationUrl = workos.sso.getAuthorizationUrl({
    organization,
    redirectUri,
    clientId,
  });

  res.redirect(authorizationUrl);
};
	

Update your UI to call this endpoint when the user clicks “Login with SSO”.

Step 5: Add the callback endpoint

Next, let’s add the redirect endpoint, which will handle the callback from WorkOS after a user has authenticated with their identity provider. This endpoint should exchange the authorization code returned by WorkOS with the authenticated user’s profile. The authorization code is valid for 10 minutes.

	
import type { NextApiRequest, NextApiResponse } from 'next';
import { WorkOS } from '@workos-inc/node';

const workos = new WorkOS(process.env.WORKOS_API_KEY);
const clientId = process.env.WORKOS_CLIENT_ID;

export default async (req: NextApiRequest, res: NextApiResponse) => {
  const { code } = req.query;

  const { profile } = await workos.sso.getProfileAndToken({
    code,
    clientId,
  });

  // Use the Test Organization ID to get started. Replace it with
  // the user’s real organization ID when you finish the integration.
  const organization = 'org_test_idp';

  // Validate that this profile belongs to the organization used for authentication
  if (profile.organizationId !== organization) {
    return res.status(401).send({
      message: 'Unauthorized',
    });
  }

  // Use the information in `profile` for further business logic.

  res.redirect('/');
};
	

You are now ready to test end-to-end using the Test IdP. Make sure that AuthKit is disabled to ensure Test IdP redirects back to your application.

Step 6: Create an Organization + SSO Connection for your B2B customer

For each B2B customer you want to add to your app, you need to create an Organization. Go to the Organizations page on the WorkOS dashboard and create a new org.

Screenshot of the WorkOS dashboard Organizations page

You can associate each organization with one or more domains. By doing that, any user signing in with SSO using these domains will automatically be considered verified. For more on this see our guide on how to model your B2B SaaS with organizations.

After that, you can configure your customer’s IdP yourself or invite your customer’s admin to set it up in the Admin Portal.

Screenshot of the WorkOS dashboard showing the options to configure an org or invite an admin

For detailed steps on how to configure each IdP see our Integrations page.

The end

At this point, you’ve done the hard part once and only once. Your app can now speak SSO fluently without learning every dialect of enterprise identity. Whether your next customer shows up with Okta, Azure AD, Google Workspace, or something held together by legacy hope, your integration doesn’t change. You didn’t build SAML. You didn’t debug XML signatures at 2 a.m. You didn’t sign up to become an IdP compatibility engineer. You added one integration, kept your existing auth intact, and moved on. Which is exactly how SSO should feel: not like a multi-quarter project, but like a box you check, confidently, and never have to reopen.

Sign up for WorkOS and let us worry about auth while you build your product.

This site uses cookies to improve your experience. Please accept the use of cookies on this site. You can review our cookie policy here and our privacy policy here. If you choose to refuse, functionality of this site will be limited.