Skip to main content
Articles

How to add SSO and SAML to my SaaS Product - Part 2

Author: Roy Anger
Published: (last updated )

This is Part 2 of our series on adding SSO and SAML to your SaaS product. In Part 1, we explored the business value of SSO, what the "SSO tax" means today, and how SSO fits into a modern B2B authentication strategy. Now, we'll dive into the technical decisions: choosing between SAML and OIDC, understanding how SAML and SCIM actually work, evaluating implementation options, and taking the first steps to integrate Clerk.

When to choose SAML vs OIDC for your B2B SaaS

The procurement reality: most enterprise workforce RFPs still name SAML by protocol. SoftwareFinder's 2025 SaaS Security Report finds 68% of enterprise RFPs require MFA plus SSO in base plans (not premium add-ons) and 43% of buyers have disqualified vendors for failing to provide verifiable credentials. Okta's public market position and Microsoft's Entra ID scale — discussed below — mean that when your customer IdP is Okta, Entra ID, Ping, JumpCloud, OneLogin, or ADFS, your customer's IT admins will expect SAML. If you ship one protocol first, ship SAML.

Definitively choose SAML when — any one of the following is sufficient:

  • The customer's procurement or security questionnaire names "SAML 2.0 SSO" explicitly. This is near-universal in enterprise RFPs for workforce identity.
  • The customer's IdP is Okta Workforce, Ping, ADFS, JumpCloud, or OneLogin. ADFS is OIDC-incapable in most deployed configurations — treat an ADFS customer as SAML-only.
  • The customer requires IdP-initiated SSO from a corporate tile — Okta dashboard, Microsoft MyApps, Google Cloud Identity portal. Microsoft Entra does not expose RelayState for OIDC connections, and Okta OIDC apps do not populate the corporate tile launcher the way SAML apps do.
  • The customer's SIEM, log shipper, or audit pipeline only parses SAML assertions. Mature enterprises have years of investment in Splunk SAML parsers and custom XML IOC rules; switching them to OIDC silently breaks their audit trail.
  • The customer requires deep-linking via RelayState — post-login redirect to a specific intranet URL is a first-class SAML feature and brittle-to-nonexistent in OIDC.
  • You're selling into US federal or defense (FedRAMP), healthcare (HIPAA), financial services, or any procurement template that pre-names SAML. NIST SP 800-63C (final, July 31, 2025) treats assertion formats as equivalent in terms of Federation Assurance Level protections — but procurement language isn't something you should fight on day one.

Definitively choose OIDC (or EASIE) when — any one is sufficient:

  • The customer's IdP is Google Workspace or Microsoft Entra and tenant isolation is not a hard requirement → EASIE. One connection in your dashboard handles every Google and Microsoft customer tenant.
  • You ship native mobile, SPA, or CLI clients — Authorization Code + PKCE is the native flow; SAML's browser-POST round trip is a poor fit.
  • You're authenticating service-to-service or machine-to-machine workloads. Okta's developer guidance is explicit: "Okta does not support service-to-service authentication scenarios with SAML." Use OAuth 2.0 client credentials.
  • You want .well-known autodiscovery plus JWKS rotation instead of emailed X.509 certs that silently rotate over weekends (Scalekit's #1 cited cause of production SSO outages).
  • The customer is a modern SMB on a developer-friendly OIDC-only IdP — Authentik, Keycloak, custom OIDC.
  • Your product is API-first and every downstream service expects a JWT access token. SAML's XML assertion is a dead end for API authorization.
  • Auto-deprovisioning matters and you can't ship SCIM yet. EASIE polls the IdP for membership, so removing a user from the Google or Microsoft tenant deprovisions them in your app without a separate protocol.

EASIE has a real architectural trade-off. Per Clerk's enterprise-connections docs: "The primary security difference between EASIE SSO and SAML SSO is that EASIE depends on a multi-tenant identity provider, while SAML depends on a single-tenant identity provider." Multi-tenant means one Google or Microsoft connection trusts every customer tenant — domain-claim verification (via Google's hd and Microsoft's xms_edov claims) is the boundary. NIST SP 800-63C applies the same FAL framework regardless of tenancy model, but FAL2 requires pre-established, per-RP trust agreements and injection-protected assertions, and FAL3 requires holder-of-key (or bound-authenticator) binding on top of that. Those requirements are straightforwardly satisfied by a dedicated single-tenant SAML deployment and harder to satisfy with a shared multi-tenant IdP like a Google-Workspace-wide EASIE connection — so customers who require per-tenant cryptographic isolation (financial-services audits, FedRAMP Moderate/High with dedicated signing keys, "no shared trust roots") should use SAML. This is analytical inference about tenancy and FAL, not a tenancy-specific statement in 800-63C.

The decision tree, in order:

  1. Does the buyer's RFP literally say "SAML 2.0," or name an IdP whose admins only speak SAML (Okta Workforce, Ping, ADFS, JumpCloud, OneLogin)? → SAML.
  2. Does the customer require IdP-initiated SSO from a corporate tile (Okta dashboard, Microsoft MyApps, Google Cloud Identity portal)? → SAML.
  3. Selling into US federal, defense, healthcare, financial services, or any environment requiring NIST 800-63-4 FAL2 or FAL3? → SAML.
  4. Is the buyer's IdP exclusively Google Workspace or Microsoft Entra and tenant isolation is not a hard requirement? → EASIE.
  5. Are you authenticating native mobile, SPA, CLI, or service-to-service workloads? → OIDC.
  6. None of the above? → SAML by default, because that's what most enterprise workforce RFPs name.

Why most mature B2B SaaS supports both. Workforce SSO is SAML-first; SMB and developer tenants prefer OIDC. Clerk's enterprise_sso strategy abstracts the protocol choice behind a single API — the app code is identical regardless of whether the connection underneath is SAML, OIDC, or EASIE. Plan for supporting both once you move past the first enterprise deal.

Note

The OpenID Foundation is publishing OIDC Federation 1.0 as a final spec in Q1–Q2 2026 (final review closed February 2026). This is OIDC's answer to SAML's federation model, but real-world IdP adoption is years out. Don't time your 2026 roadmap to it.

How SAML works in practice

The Service Provider (SP) and Identity Provider (IdP)

Your SaaS is the Service Provider (SP). The customer's Okta, Entra ID, or Google Workspace tenant is the Identity Provider (IdP). The SP generates a metadata document announcing its Entity ID and ACS URL. The IdP generates a metadata document announcing its SSO URL and signing certificate. Both sides exchange metadata once, and from then on the protocol runs as HTTP POSTs carrying signed XML.

SAML assertions and the Assertion Consumer Service (ACS)

A SAML assertion is a signed XML document containing a NameID (the user's stable identifier), AttributeStatement elements (email, first name, last name, and any custom claims), and Conditions elements that bound the time window and intended audience. The signature uses XML-DSIG, not JWT — the algorithms and canonicalization rules are different, and every CVE you'll read about in this article exploits a difference between what the signature covers and what the rest of your code reads.

The ACS is an HTTP endpoint on your SP that accepts POST requests containing base64-encoded SAML responses. A managed auth service owns that endpoint for you. A DIY implementation has to build it.

SP-initiated vs IdP-initiated flows

In an SP-initiated flow, the user lands on your app first, enters an email, you route them to their customer IdP, the IdP authenticates them, and the IdP POSTs a signed assertion back to your ACS. This is the safer default because your app controls the flow — there's a login CSRF cookie, a RelayState round-trip, and a request ID (InResponseTo) to match the response against.

In an IdP-initiated flow, the user clicks a tile in the Okta dashboard or Microsoft MyApps, and the IdP POSTs an unsolicited assertion to your ACS without any prior request from you. This has no login CSRF protection, no InResponseTo to validate, and is the vector behind several of the 2025–2026 CVEs discussed later. Scott Brady, IdentityServer, and Teleport all recommend disabling IdP-initiated unless a specific customer use case requires it. NIST SP 800-63C (final, July 31, 2025) requires RP-initiated federation transactions at FAL2 and FAL3 (a SHALL), which effectively rules out unsolicited IdP-initiated SSO at those assurance levels; at FAL1 the requirement is a SHOULD.

Metadata, certificates, and trust establishment

The IdP metadata XML declares the IdP's EntityID, its SingleSignOnService URL, and one or more X.509 signing certificates. Certificate key requirements: RSA 2048+ or ECC 256+, SHA-256 or stronger for signing. The tripwire is rotation: IdPs rotate signing certs on schedules you cannot control, typically with 14–30 days of notice, sometimes on a Friday afternoon with no notice at all. Scalekit's analysis of production SSO outages identifies certificate rotation mismatches as the single most common cause of production failures. Design for rotation from day one: accept a metadata URL (not a pasted certificate), cache it with a TTL, and support two active signing certificates during rotation windows.

SCIM: the other half of enterprise identity

Automated provisioning and deprovisioning

SAML creates a user on first login via Just-in-Time (JIT) provisioning. SCIM creates, updates, and — most importantly — deletes users proactively via a REST API. RFC 7643 defines the core schema; RFC 7644 defines the protocol. The IdP (Okta, Entra ID, Google Workspace) is the SCIM client; your SaaS is the SCIM service provider. When an employee is deactivated in the IdP's directory, a PATCH /Users/{id} with active: false fires to your SCIM endpoint within seconds.

When you need SCIM (and when you don't)

You need SCIM the moment a customer asks about deprovisioning. SAML alone leaves orphaned accounts when an employee is offboarded mid-session — the session cookie keeps working until it expires, and audit logs will show access events after the HR system says the employee left. Enterprise procurement increasingly mandates SCIM alongside SAML. HIPAA-aligned best practice is a 1-hour revocation SLA from IdP deactivation to session termination in your app.

You can defer SCIM for smaller customers. If your first enterprise customer has 15–50 seats, JIT-only is usually acceptable. Ask: "What's your deprovisioning SLA, and is SAML session-timeout acceptable for now?" If the answer is "24 hours is fine," you can ship JIT first and enable SCIM in the next sprint. Most enterprise customers will eventually want SCIM, though.

Multi-tenancy: SSO is per-organization, not per-app

Every customer tenant has its own IdP connection. The idiom is per-organization, not per-app: your app has one deployment; each customer has their own organization in your data model and their own SAML connection inside that organization. The routing is typically email-domain-based Home Realm Discovery — when a user enters alice@acme.com, your app looks up which organization owns the acme.com domain and routes the sign-in flow to that organization's SAML connection.

In Clerk, the Organization primitive is literally this model. In Auth0, it's called Organizations. In WorkOS, it's called Organizations too. The primitive is so universal across managed auth services that architecting without it is a tell that you're going to replatform later.

Implementation options

You have three realistic paths.

Option 1: build it yourself with open-source libraries

This is the "we'll ship it in a sprint" option that almost always turns into a 3–9 month timeline once admin UI, certificate-rotation handling, and the 2025–2026 CVE patch load are factored in. Here's why.

Common Node.js / TypeScript libraries (with CVE-safe minimum versions)

LibraryMinimum safe version (April 2026)Notes
@node-saml/passport-saml≥ 5.1.0Passport.js strategy; ~423k weekly downloads
@node-saml/node-saml≥ 5.1.0Framework-agnostic; ~564k weekly
samlify≥ 2.10.0TypeScript; ~488k weekly. 2.10.0 patches CVE-2025-47949 (CVSS 9.9 signature wrapping).
xml-crypto≥ 6.0.1Foundational XML signature layer; ~2.6M weekly. 6.0.1 fixes SAMLStorm (CVE-2025-29774 / CVE-2025-29775).

Not recommended in 2026: Clever saml2-js (CoffeeScript, maintenance mode) and any samlify below 2.10.0.

Across all four libraries, the pin-to-minimum-version discipline is non-negotiable. Each of the five critical CVEs cited below was disclosed, exploited in limited scope, and patched within the last twelve months. Running a library three minor versions behind is not "production" — it's a pending incident.

Security pitfalls you must handle yourself

XML Signature Wrapping (XSW) attacks are the defining SAML vulnerability class. The original Somorovsky et al. paper "On Breaking SAML: Be Whoever You Want to Be" (USENIX Security, 2012) showed that 11 of 14 major SAML frameworks were exploitable at the time. GitHub Security Lab's 2025 "Sign in as Anyone" research (CVE-2025-25291 and CVE-2025-25292 in ruby-saml) demonstrated the class of bug is not solved thirteen years later — it just mutates. The attack: inject a forged assertion outside the signed element, rely on parser differentials so the signature validator sees the real (signed) node and the attribute extractor sees the forged (unsigned) node. Result: alice@acme.com signs in as admin@target.com.

Signature and certificate validation. Always schema-validate before signature-verify — CVE-2025-54369 and CVE-2025-54419 in node-saml exploit exactly this ordering mistake. Use absolute XPath, not relative XPath. Never extract attributes from the original document — only from signed and canonicalized content.

Replay and clock-skew attacks. Validate InResponseTo against the request ID you stored at flow initiation (it's disabled by default in node-saml — flag this during review). Enforce NotBefore and NotOnOrAfter with 1–5 minutes of clock-skew tolerance. If you run more than one SP instance, share an InResponseTo cache in Redis or similar so an assertion used against one instance can't be replayed against another.

XML External Entity (XXE) injection. Disable DTDs entirely. Cap payload size. The OWASP XXE Prevention Cheat Sheet has the rules; xml-crypto ≥ 6.0.1 and @node-saml/node-saml ≥ 5.1.0 default to safe parsing, but older or wrapping code can still be vulnerable.

Parser differentials. CVE-2025-25291 and CVE-2025-25292: one parser for signature validation, a different parser for attribute extraction, both present on the same request. The signature parser sees the signed node and approves; the attribute extractor sees a forged sibling node with a different email. Mitigation: one parser for both stages, reject malformed XML strictly, treat any disagreement as an error.

Ongoing maintenance burden

Libraries handle the protocol. Everything else is on you.

  • IdP metadata rotation is the #1 cause of SSO outages. Build monitoring for certificate expiry at 60, 30, and 7 days. Support two active signing certificates during rotation windows.
  • Admin UI is not optional. You need metadata URL or XML upload, entity ID and ACS URL display, attribute mapping configuration, verified-domain management, per-connection enable/disable, cert expiry dashboards, a test-login flow, structured redact-on-PII logs, and a fallback non-SSO admin path for when the IdP is down.
  • Support load is non-trivial. First-time customer IT admins routinely paste the wrong field into the wrong form, copy the IdP cert into the SP cert slot, and then file a P1 saying "SSO is broken." You will become a part-time SAML support engineer.

Cost estimate — SSOJet's illustrative DIY SSO breakdown (vendor analysis, not primary research): $27,000 year-one engineering build + $10,000/year maintenance + $40,000 opportunity cost + $20,000 security and compliance reserve + $15,000 sales and support drag = approximately $112,000 for year one. Slipping timelines push it to $150,000+. Expected timeline when teams kick off: 1–3 months. Typical actual: 3–9 months once admin UI, certificate rotation, and CVE patching land.

When DIY makes sense. Regulated industries with on-premises-only identity infrastructure. Teams with existing identity specialists. Products with open-source philosophical commitments. For everyone else: buy. The math doesn't close on DIY unless the non-financial requirements are non-negotiable.

Option 2: managed authentication services

There are roughly a dozen viable managed auth services for B2B SaaS in 2026. This article covers the three most procurement-friendly options at length and names the others briefly.

Clerk

B2B-first auth platform. SAML, OIDC, and EASIE multi-tenant OIDC as first-class primitives. The Organizations primitive is built in. Prebuilt React components (<OrganizationSwitcher />, <OrganizationProfile />, <SignIn />). Dashboard-driven connection setup plus a unified /enterprise_connections Backend API (unified March 2026). Directory Sync (SCIM 2.0) went GA on April 16, 2026. Native Next.js 16 proxy.ts support via @clerk/nextjs v7.x. Pricing after the February 2026 restructure: Pro $20/month annual ($25/month monthly), 1 enterprise connection included, $75/month per additional connection (scaling down to $15/month at 500+ connections). 50,000 free monthly active users.

Auth0

Okta-owned general-purpose CIAM. SAML plus OIDC enterprise connections. Organizations are on all plans. Next.js 16 is supported via @auth0/nextjs-auth0 v4.18.0 which honors proxy.ts. Pricing (post the February 12, 2026 B2B plan upgrade): 25,000 free MAU with 1 Enterprise Connection, Self-Service SSO, and Inbound SCIM included on the Free tier; B2B Essentials $150/month includes 3 SSO connections; B2B Professional $800/month includes 5 SSO connections; $100/month per additional connection. Self-Service SSO on Auth0 is a ticket-URL-driven guided assistant the customer admin completes in a one-time flow, not a standing admin portal the way WorkOS Admin Portal works.

WorkOS

SSO-first "drop on top of existing auth." The Admin Portal — a hosted, white-labeled customer-facing UI for self-serve SSO configuration — is the differentiator. SSO is $125 per connection per month (tiered down with scale); Directory Sync is $125 per connection per month separately. AuthKit, WorkOS's newer full-auth product, is free to 1M MAU. Next.js 16 support via @workos-inc/authkit-nextjs v3 using authkitProxy.

Also consider (one line each)

  • Stytch — B2B; acquired by Twilio in November 2025.
  • Descope — Drag-and-drop Flows; self-serve SCIM portal; good for B2C-adjacent B2B.
  • PropelAuth — B2B-only; unique Bring-Your-Own-Auth sidecar pattern.
  • Frontegg — Mature admin portal; more established mid-market focus.
  • Ory Polis (formerly BoxyHQ SAML Jackson) — Open-source SAML bridge; Apache 2.0.
  • Scalekit — Recently pivoting toward AI-agent-first authentication.
  • SuperTokens / FusionAuth — Self-hostable options; more engineering effort to operate.

Not considered (with reason)

  • Okta / OneLogin / JumpCloud / Entra ID — these are IdPs, not SPs. Your SaaS consumes them; you don't replace them with them.
  • Kinde — no SCIM GA at time of writing.
  • SSOJet / Userfront — too small for most enterprise procurement checks.

How to choose: a decision framework

Speed to production

OptionTime to first production SAML login
Clerk (Dashboard + IdP round-trip)1–3 hours with the API + existing tests
WorkOS (Admin Portal)"Days" per marketing; 1 day realistic
Auth0 (with Actions customization)1–2 weeks
DIY with libraries4–8 weeks MVP; 3–9 months production-hardened

Multi-tenant complexity

All three managed services support per-organization SSO connections. Clerk's Organizations primitive is the most ergonomic for teams that haven't built an org model yet — <OrganizationSwitcher /> and <OrganizationProfile /> are drop-in React components. Auth0 Organizations and WorkOS Organizations are more API-first; you'll spend more time wiring UI.

Pricing model and the "SSO tax" debate

Clerk no longer charges a per-connection premium over base subscription after the November 2024 EASIE launch and February 2026 restructure; the $75/month per-additional-connection price is straightforward. WorkOS is transparent at $125/month per connection. Auth0 has the steepest tier cliff in the market: 3 → 5 SSO connections forces a move from Essentials ($150/month) to Professional ($800/month) — a 5.3× price jump for two additional connections. Procurement readers will flag Auth0 pricing explicitly.

Developer experience and existing stack fit

Clerk leads for Next.js App Router plus React plus B2B. The @clerk/nextjs v7 package is Next.js 16 proxy.ts-native and ships prebuilt components for every SSO surface. Auth0 leads for broadest language and framework coverage — if your stack is Ruby, PHP, Go, Java, or .NET, Auth0's SDK catalog is deepest. WorkOS leads for "we already have auth, we just need SSO on top" — @workos-inc/authkit-nextjs and the Admin Portal are designed to bolt onto an existing user model rather than replace it.

Implementing SAML SSO with Clerk

This is the meaty section. It's a complete walkthrough on Next.js 16 with @clerk/nextjs v7.x, not a set of snippets — every step ends with a verification hook so you can confirm you're where you're supposed to be before moving on.

Prerequisites and architecture overview

Before you start, have:

Checklist

The request flow at a high level:

[User in Next.js app]
  → (identifier-first sign-in, "alice@acme.com")
  → [Clerk Frontend API routes to Acme's IdP based on verified domain]
  → [Okta authenticates alice@acme.com]
  → [Okta POSTs signed SAML assertion to Clerk's ACS]
  → [Clerk Backend verifies signature, creates or updates User, mints session]
  → [User lands on /dashboard with orgId set and orgRole populated]

You don't build most of the middle. You configure metadata on both sides, wire up the Next.js app, and let Clerk's Frontend + Backend APIs handle the protocol.

Step 1: Enable Enterprise SSO on your Clerk application

In the Clerk Dashboard: AuthenticationSSO connectionsAdd connectionFor specific domains or organizations. Pick whether you're configuring at the instance level (all organizations share the connection, rare) or at the organization level (per-customer, the default for B2B).

Pricing to know: Development instances (pk_test_* / sk_test_* keys) include all paid functionality for free — you can stage enterprise SSO for every customer before production cutover without paying per connection. Production instances on Pro include 1 enterprise connection; additional connections are $75/month each, scaling down to $60 at 16–100, $30 at 101–500, and $15/month at 500+.

An alternative first step: EASIE. If your customer uses Google Workspace or Microsoft Entra ID as their IdP, EASIE lets you skip SAML metadata exchange entirely. One EASIE connection in your Clerk dashboard trusts every Google and Microsoft tenant and uses domain verification as the tenant boundary. It's multi-tenant OIDC under the hood; setup is roughly one form submission. EASIE is the "0–30-day-to-first-enterprise-deal" option; full SAML is the "we're committed to enterprise long-term" option. Many teams ship EASIE first and add per-customer SAML connections as contracts demand it.

Tip

A customer's first question when you propose EASIE will usually be: "Does this mean you trust every Google tenant?" The honest answer is yes — with domain verification as the boundary. If they require per-tenant cryptographic isolation (FAL2+, financial-services audits, "no shared trust roots"), go straight to SAML.

Frequently asked questions

Is SAML more secure than OIDC? Neither is inherently more secure, but they have different architectural models. SAML is traditionally deployed as a single-tenant integration, making it straightforward to satisfy strict isolation frameworks (like FedRAMP or financial audits). OIDC can be single-tenant but is often deployed multi-tenant (like EASIE), which some strict compliance standards may require extra controls to approve.

Do I absolutely need SCIM for enterprise customers? Not necessarily on day one. You can use SAML's Just-In-Time (JIT) provisioning for smaller organizations or initial rollouts. As your customers grow and require strict deprovisioning SLAs, they will eventually mandate SCIM.

What is EASIE? EASIE is Clerk's streamlined multi-tenant OIDC implementation for Google Workspace and Microsoft Entra ID. It allows you to establish a single connection that inherently trusts any tenant on those IdPs, using domain-claim verification as the security boundary, significantly reducing the onboarding friction of manual SAML setup.

With the foundational concepts clear and your first enterprise SSO connection configured in Clerk, you're ready to integrate the authentication flow into your application code. In Part 3, we will cover the exact steps to wire up Next.js, handle the SAML metadata exchange, and build the required admin UI for your customers.

In this series

  1. How to add SSO and SAML to my SaaS Product
  2. How to add SSO and SAML to my SaaS Product - Part 2 (you are here)
  3. How to add SSO and SAML to my SaaS Product - Part 3
  4. How to add SSO and SAML to my SaaS Product - Part 4