Skip to main content

ADR-017: WorkOS as Authentication Provider

Status

Proposed (2026-04-09)

Pairs with: ADR-016: Multi-Tenant Authentication Architecture — ADR-016 defines the vendor-independent architecture; this ADR selects the specific vendor that fills the "external identity provider" role in that architecture.

Research input: 2026-04-09 Provider comparison — full evaluation of six options across twelve criteria.


Context

ADR-016 commits us to an external identity provider as the source of truth for users, organizations, memberships, and roles. This ADR picks which one.

The candidates evaluated in the research artifact were:

  1. WorkOS — B2B-focused, AuthKit + SSO + Directory Sync + Admin Portal
  2. Clerk — Developer-experience-first, drop-in React components, B2B via Organizations
  3. Stytch B2B — API-first, granular per-organization auth policies
  4. Auth0 (Okta) — Legacy enterprise leader
  5. Cloudflare Access (extend existing) — Network-layer identity gateway we already have
  6. Roll our own — openid-client + Mongo + Resend, as proposed in ADR-012

Evaluation criteria (full matrix in the research doc):

CriterionWhy it matters for sv0
Enterprise SSO (SAML/OIDC) per organizationNon-negotiable for Fortune 500 installs
Self-serve SSO onboarding (Admin Portal)Prevents every new enterprise customer from becoming an engineering ticket
SCIM / Directory SyncEnterprise expectation; fast-follow after initial SSO
Magic link / passwordlessRequired for evaluations and demos
Native B2B data model (Organizations, memberships)Avoid reinventing user↔tenant↔role tables
Cross-tenant super-admin supportSecurityV0 team requirement
React SDK qualityUI is React + React Router
Pricing at 0–10 enterprise customersWe are pre-revenue
Vendor lock-in / exit costWe may migrate later
CISO sellabilityWe sell to security buyers who will review our auth stack

sv0's distinctive usage shape

One factor materially changes the economics of this decision: sv0 has a high-ACV, low-seat usage pattern. A 1,000-person enterprise customer typically has 20–50 actual users of the product (CISOs, security officers, product owners). This inverts the pricing math compared to a typical B2C or employee-facing SaaS:

  • MAU is effectively unbounded. With ~30 users per customer, reaching 1M MAU requires ~33,000 enterprise customers — beyond any realistic planning horizon.
  • Connection count is the real cost driver. Each enterprise customer who brings their own SAML IdP = one SSO connection = whatever per-connection fee the provider charges.
  • Not every customer requires SAML. Evaluation customers, POCs, and small buyers happily use magic link. SSO cost scales with enterprise paid customer count, not total customer count.

This reframing changes which trade-offs matter. Providers that charge per-MAU are almost free for us. Providers that charge per-SSO-connection scale linearly with enterprise revenue. Providers that charge both matter less on the MAU side and more on the connection side.


Decision

Adopt WorkOS as the identity provider for sv0-platform. Specifically:

  • AuthKit for the hosted login experience (email, magic link, social, passkeys, MFA, and SAML/OIDC — all from one login page).
  • Organizations as the tenant primitive, 1:1 mapped to the tenants collection defined in ADR-016.
  • Single Sign-On (SAML/OIDC connections) enabled per-organization, on demand, only after a paid contract. WorkOS supports any SAML 2.0 or OIDC-compliant IdP — including PingFederate, PingOne, Okta, Entra ID, Google Workspace, OneLogin, JumpCloud, CyberArk, and 15+ others with dedicated integration guides. No custom IdP connector code on our side; WorkOS acts as the broker.
  • Admin Portal for customer IT admins to self-serve their SAML configuration (select their IdP, upload metadata, test the connection — all without involving sv0 engineering).
  • Directory Sync (SCIM) as a fast-follow, enabled per-customer on request.
  • A dedicated securityv0-internal Organization for the SecurityV0 team. Same login flow as customers. Members of this organization get is_super_admin = true in the local mirror.
  • Webhooks from WorkOS to sv0's /api/v1/webhooks/workos endpoint to keep the local user/membership mirror in sync.
  • WorkOS Node SDK (@workos-inc/node) in the backend, integrated through iron-session (or equivalent) for encrypted session cookies.
  • AuthKit React SDK (@workos-inc/authkit-react) on the frontend for the login callback handling.

Rationale

Five factors drove the choice. In order of weight:

1. The Admin Portal is the decisive feature

Every other provider requires us to configure each customer's SAML connection on their behalf. The WorkOS Admin Portal is a hosted page we generate a one-time link to; the customer's IT admin walks through their own SAML setup without ever talking to an sv0 engineer.

At 5 enterprise customers this saves us maybe a day of work. At 50 it saves us weeks. At 200 it is the difference between a functioning sales motion and an operationally broken one. This feature alone justifies picking WorkOS over Clerk or Stytch, both of which have nascent or less-mature equivalents.

2. Native B2B data model

WorkOS was built B2B-first. Its Organizations, Memberships, Connections, and Directory Sync primitives map 1:1 to what ADR-016 requires:

ADR-016 conceptWorkOS concept
TenantOrganization
UserUser
Membership (user↔tenant)Organization Membership
Tenant role (admin, member)Organization Role
Per-tenant SAML/OIDC configConnection (attached to Organization)
Self-serve SAML setupAdmin Portal
Auto-deprovision on terminationDirectory Sync
SecurityV0 internal tenantA dedicated Organization with domain restriction to @securityv0.com
Cross-tenant super-adminDerived locally from internal-org membership

Contrast with Auth0, where "Organizations" were added in 2021 and still feel bolted on; and with Clerk, where B2B is a newer product line layered on a historically B2C foundation.

3. Pricing is aligned to our revenue shape

WorkOS bills on two independent axes:

AxisChargeMeter for sv0
AuthKit (user management layer)Free up to 1M MAU, then $2,500 per additional 1MEffectively $0 forever (we will not realistically reach 1M MAU)
SSO connections$125 each/month (1–15), tiered down to $50 at 101–200Scales linearly with enterprise customer count
Directory Sync connectionsSame tiered pricingOptional, per customer
Admin PortalIncluded free$0
Magic Auth (magic link)Included in AuthKit$0

Modeled against our go-to-market:

StageEnterprise customers w/ SAMLMagic-link POCsWorkOS monthly cost
Pilot / early14$125
Post-launch1010$1,250 (SSO only)
Scale-up3515~$3,900 (volume tier 2)
Mature15050~$11,250 (volume tier 3)

At the mature stage, $11k/month ≈ $135k/year, which is ~1.4% of a $10M ARR (150 enterprise × $66k average ACV). Industry-normal. At the pilot stage it is $125/month, which is below the threshold of any budget review.

Critically: the meter only starts when a customer signs. Evaluations, POCs, demos, and magic-link-only customers cost $0. WorkOS cost correlates 1:1 with enterprise revenue events.

COGS reality check (added 2026-04-10)

sv0's actual go-to-market pricing changes the math from "rounding error" to "needs attention":

ChannelACV20% COGS targetSSO ($1,500/yr) as % of COGS
Partner (channel)$45,000/yr$9,000/yr16.7%
Direct (mid)$60,000/yr$12,000/yr12.5%
Direct (high)$90,000/yr$18,000/yr8.3%

At $45k channel ACV with a 20% COGS rule of thumb, $1,500/yr for SSO alone consumes 16.7% of the COGS budget — a genuine scaling concern if every client requires SSO. In practice, they won't.

Not every client needs SAML SSO

WorkOS AuthKit includes free authentication methods that are production-ready for real paying clients:

  • Google OAuth ("Sign in with Google") — $0. Works for any client on Google Workspace.
  • Microsoft OAuth — $0. Works for any client on Microsoft 365.
  • GitHub OAuth — $0. Works for developer-focused clients.
  • Magic link — $0. Works for any client.

SAML SSO is only required when a client's IT team demands IdP-level control — enforcement of login through their corporate Okta/Entra ID, conditional access policies, SCIM auto-provisioning. Smaller companies, mid-market clients without strict IT policies, and channel deals often do not have this requirement.

For social-login clients, we build a small domain-match auto-join in our middleware (~20 lines): when a user signs in via Google with @acme.com and Acme has verified that domain on their tenant, we auto-create a membership. This gives the "just sign in and you're in" experience without a paid SSO connection.

Blended COGS model

SSO adoption rate is unknown. The sensitivity table below shows COGS impact at four scenarios (see full analysis):

SSO adoptionBlended auth cost/customer/yrAs % of Partner COGS ($9k)As % of Direct Mid COGS ($12k)
35%$5255.8%4.4%
50%$7508.3%6.3%
70%$1,05011.7%8.8%
100%$1,50016.7%12.5%

At 50% or below, SSO COGS is under 10% across all channels. Even at 70%, volume discounts (tier 4: $600/yr) bring it back under 10%. The 16.7% worst-case requires literally every $45k client to demand SAML.

Additional cost levers

  1. Volume tiers. At 101+ SSO connections, WorkOS drops to $50/connection ($600/yr = 6.7% of COGS even for 100%-SSO clients).
  2. This is not WorkOS-specific. Stytch charges identically ($125/connection). Clerk is cheaper per-connection ($75→$15 at volume) but has higher fixed costs and no SCIM. See full COGS analysis.
  3. Startup program and volume negotiation are the highest-leverage action. A 15-minute WorkOS sales call may reshape the cost curve.
  4. Hybrid approach (WorkOS for AuthKit + open-source SAML proxy) could eliminate per-connection cost entirely. See cost optimization strategies.

This ADR's recommendation holds with high confidence. The blended COGS picture is healthy, and multiple levers exist to improve it further.

4. OIDC compliance → low lock-in

WorkOS is a standards-based provider. User sessions are OIDC-compliant tokens; Organizations, Users, and Memberships are available via a well-documented API; webhooks are standard HTTPS with HMAC signatures. If we ever need to migrate off WorkOS — because of pricing changes, acquisition, a customer mandate, or any other reason — the exit work is:

  1. Spin up the new provider.
  2. Rewrite the ~500 lines of src/api/middleware/workos-auth.ts and the webhook receiver against the new provider.
  3. Backfill the local users and memberships mirror from the new provider's API.
  4. Ask customers to reconfigure their SAML connections in the new provider's Admin Portal (if the new provider has one).
  5. Flip DNS / SDK config and switch over.

The domain layer (findings, entities, evaluator, UI) is untouched. Contrast this with Auth0 (Rules, custom claims, proprietary session model, custom DB connections — all hard to extract) or roll-our-own (migrating off is trivial but the cost was building it in the first place).

5. CISO sellability

WorkOS is a known name in the B2B security-tools ecosystem. In a customer security review, "we use WorkOS for authentication" reads as "we made a grown-up choice." Compare to:

  • Clerk: reads as B2C; some CISOs will not have heard of it; probably requires a security questionnaire explanation.
  • Stytch: similar profile — newer, less enterprise inertia.
  • Rolled our own: a red flag in almost every security review; they will want to audit our session code, our token rotation, our CSRF protection, our SAML signing verification. This is a meaningful deal-friction cost.
  • Auth0: universally recognized, but our pricing-tier position and the Auth0 DX make this a weaker fit.

For a product whose buyer is a CISO, the "what do they think when they see our auth stack" signal has non-trivial value.


Alternatives Considered

Full comparison and matrix in the research doc. Summary here.

Clerk

Strengths: Best React SDK of any option. Drop-in <OrganizationSwitcher />, <SignIn />, and <UserButton /> components would save days of frontend work. Generally 2–3 days faster to first login than WorkOS.

Why rejected:

  1. No Admin Portal equivalent. Clerk does not have a hosted page for customer IT admins to self-configure SAML connections. This means we either build our own SSO config UI or handle each enterprise onboarding manually — increasing per-customer ops cost.
  2. SCIM/Directory Sync is not GA. As of April 2026, Clerk's SCIM support is on their roadmap but not generally available. Enterprise customers who require auto-provisioning would need a workaround.
  3. B2B feels bolted on. Clerk's foundation is B2C; B2B (Organizations, enterprise SSO add-on) was added in 2023 and some features are still catching up.
  4. Session model is more proprietary. Migrating off Clerk is meaningfully more work than migrating off WorkOS.
  5. CISO recognition is weaker. Not a blocker but a small headwind.

However: Clerk has the best SSO volume pricing of any provider ($75→$60→$30→$15/connection at volume tiers). If WorkOS pricing negotiation fails and we project 50+ enterprise customers within 18 months, Clerk becomes the cost-optimal fallback despite the ops and feature gaps. See pricing deep-dive.

Stytch B2B

Strengths: Most granular per-organization auth policy controls of any option (e.g., "require SSO for this org, allow magic link for that one, enforce MFA for a third"). API-first, minimal UI lock-in.

Updated assessment (April 2026): Stytch is stronger than originally evaluated on two axes: (1) SCIM/Directory Sync is GA and included free (5 connections in free tier), and (2) the Admin Portal is an embeddable, customizable UI that customers can self-serve through. Both are stronger than Clerk on these dimensions.

Why rejected: SSO connection pricing is $125/connection/month — identical to WorkOS with no published volume tiers. The cost advantage we hoped for does not exist. Additionally: no hosted login UI means more code for us to own, MAU overage is $0.10/MAU (5-8x Clerk), smaller ecosystem. The free tier (5 SSO connections) is excellent for the pilot phase, but doesn't help at scale.

Auth0 (Okta)

Strengths: Universally recognized, feature-complete, our existing JWKS middleware is closest to usable out of the box.

Why rejected: Pricing. Enterprise plan with Organizations starts at ~$240/month and scales aggressively with MAU, making it non-viable pre-revenue. Also: DX is dated, Organizations feels bolted on, lock-in is high (Rules, custom claims, etc.).

Cloudflare Access (extend existing)

Strengths: Already in place as a network gate. ~$3/user/month is by far the cheapest option. Zero new vendors.

Why rejected: No self-serve SSO onboarding. Every enterprise customer's SAML would be configured by us in the CF dashboard. This scales to ~20 customers before it operationally collapses. CF Access also lacks native Organization/membership primitives — we would still need to build the users/memberships/tenants tables ourselves, do it without webhooks, and do it without the Admin Portal.

We keep CF Access as a network-layer perimeter. It continues to gate app.securityv0.com and dev.securityv0.com at the edge. The app-layer identity (WorkOS) runs inside that perimeter. The two compose cleanly; see the auth architecture doc for the layering.

Roll our own (per ADR-012)

Strengths: Zero vendor cost. Complete control. No lock-in.

Why rejected: The scope ADR-012 contemplates (GitHub OAuth + magic link) is a small subset of what we need. Reaching parity with WorkOS requires adding: SAML XML signing (not OIDC), SCIM parsing, Admin Portal-equivalent hosted setup flow, connection management, org-scoped auth policies, session rotation, password reset flows, MFA, passkey support. Estimated 6–8 weeks of engineering for the initial build, plus ongoing security maintenance forever. Every one of those features is a place to have a CVE.

The math is simple: $125/month per enterprise customer is a rounding error against ACV; 6–8 weeks of senior engineering time is not. Buy, don't build.


Consequences

Positive

  1. Enterprise sales unblocked. We can respond to "do you support SAML SSO?" with "yes, your IT admin can configure it in 5 minutes — here's the Admin Portal link."
  2. Zero cost during evaluation phase. Every POC and demo runs on magic link with no WorkOS billing. First $125/month hits books the same week the first enterprise contract does.
  3. SecurityV0 team unblocked. We use WorkOS ourselves via the securityv0-internal Organization; the tenant switcher dropdown shows all tenants to super-admins.
  4. Standard-compliant, low lock-in. OIDC sessions, documented APIs, standard webhook signatures. Exit work is mechanical, not architectural.
  5. Aligned with ADR-016. Every concept in ADR-016 has a direct WorkOS equivalent, so the implementation is a translation, not an interpretation.
  6. Google Workspace SSO for our own team is free and trivial. The SecurityV0 internal organization uses Google Workspace social login through AuthKit — no $125 SSO connection fee, because social login is bundled free. This is the practical answer to "how do SecurityV0 admins log in without paying $125 for ourselves."
  7. Provider abstraction enables multi-deployment. The middleware accesses auth through an AuthProvider interface (see architecture doc §2.1). WorkOS is the SaaS implementation; a direct OIDC implementation supports partner-deployed or single-tenant installations where clients manage their own IdP without a WorkOS dependency. Swapping providers is a deployment-time config change (AUTH_PROVIDER=oidc), not a code change.

Negative

  1. New vendor relationship. We sign up with WorkOS, accept their ToS, take on operational dependency.
  2. Vendor outage = new logins fail. Existing sessions keep working (session cookies don't require a live WorkOS connection during their lifetime), but during a WorkOS outage no new users can log in. Mitigation: monitor status.workos.com, document in runbook, set reasonable session lifetime (7 days by default).
  3. Webhook receiver is a new operational surface. ~200 lines of code that must be signature-verified, idempotent, and reconciled. Test coverage and runbook required.
  4. Per-connection cost at scale. Non-zero once we start closing enterprise customers. Modeled above; negligible against ACV but not free.
  5. No service-token primitive built-in. WorkOS is oriented around interactive user sessions. Programmatic API access (seed scripts, CI/CD, connector workers) currently uses API keys (X-API-Key) and must migrate to a different pattern. See "Programmatic access" below.
  6. Vendor risk. WorkOS is VC-funded and not yet publicly profitable. If they were acquired or shut down, we would need to migrate auth — estimated 2-3 weeks of backend work, mitigated by our OIDC-compliant design and local user/membership mirror.
  7. Pricing-page ambiguity on the SSO-vs-AuthKit relationship. Our reading is that AuthKit is free up to 1M MAU, and SSO connections are charged separately from $125/month each. We are ~85% confident in this reading based on two passes through the pricing page, one blog post, and the WorkOS product structure. Before signing a contract, verify with WorkOS sales directly using the exact scenario: "we have 20 enterprise customers, ~30 users each, 20 SAML connections — what do we pay?" Also ask about the startup program, which historically waives connection fees for early-stage companies.

Programmatic access — resolved approach

Programmatic and machine access uses two WorkOS primitives: personal API keys (human-attributable, via the API Keys widget) and M2M Applications (per-service OAuth2 client_credentials). The middleware accepts three auth sources (cookie, bearer JWT, bearer API key) and resolves all to a unified AuthContext with role-bounded permissions — no "god tokens."

Full design: implementation plan §1.7 and architecture doc §13.7.

Pricing note

M2M Applications and API Keys widget pricing is not documented on the public pricing page as of this ADR. Add to the "confirm with WorkOS sales" list in the open questions below. Our working assumption: both are bundled with AuthKit at no additional cost for reasonable volume (which is the industry standard for this category of feature). If either turns out to be separately metered, this does not change the decision — the alternatives would be to build our own or use a different vendor, neither of which is a realistic option given the cost of rebuilding this surface area.

Graduation criteria

This ADR is considered successfully implemented when:

  • A WorkOS account exists in production mode with two environments: staging and production.
  • The securityv0-internal Organization is configured with a domain restriction to @securityv0.com, and all SecurityV0 team members can log in through AuthKit using Google Workspace.
  • The sv0-platform backend has @workos-inc/node integrated, with the webhook receiver verifying signatures, upserting the local mirror, and surviving the reconciliation job.
  • The sv0-platform UI has @workos-inc/authkit-react (or equivalent) integrated, with the login/logout flow fully working against WorkOS.
  • A prospect can be provisioned via scripts/provision-eval-tenant.ts (creates WorkOS Organization → invites via magic link → seeds demo data) and log in successfully.
  • A paid enterprise customer can be converted by generating an Admin Portal link from a super-admin page, handing it to their IT admin, and watching them configure SAML successfully against a test IdP.
  • The SSO-required policy is verified: once a WorkOS Organization has an active SSO connection and sso_enforced=true, magic-link login for that org is blocked.
  • All old auth code (X-API-Key header for user auth, X-Tenant-Id header as auth source, SettingsPage tenant form, REQUIRE_AUTH=false bypass) is removed or converted to the service-token path.

Open questions

  1. Exact WorkOS billing for SSO+AuthKit at low MAU — to be confirmed with WorkOS sales before committing, per the "negative consequences" note above. If the answer differs from our reading, this ADR's cost model changes but the decision does not (Clerk is the only alternative that could be cheaper at scale, and the Admin Portal gap still dominates).
  2. WorkOS Startup Program terms — worth a 15-minute sales call. If WorkOS waives connection fees for 12 months, the effective cost during Phase 1 is $0.
  3. Connection disable = billing stop? Confirm with WorkOS that disabling an SSO connection (e.g., when a customer churns) actually stops the per-connection charge within the current billing cycle. Most vendors do this; worth explicit confirmation.
  4. M2M Applications and API Keys widget pricing — confirm these are bundled with AuthKit at no additional cost, or priced per-application / per-key. Not documented on the public pricing page.
  5. API Keys widget customization — confirm the widget can be themed to match sv0 branding and what CSS/slot hooks are available. If it's rigid, budget time for custom styling.
  6. MCP CIMD support maturity — verify CIMD (Client ID Metadata Document) works end-to-end with a real MCP client (e.g., Claude Desktop) before promising this feature to customers. It is listed in WorkOS docs but we should dry-run it on staging.