SAML SSO in Next.js (Supabase + enterprise IdPs)
SAML is the protocol that closes enterprise deals and the protocol that makes engineers groan. Most teams who try to implement it from scratch get stuck on XML signing, metadata exchange, and the surprisingly large surface of edge cases between IdP-initiated and SP-initiated flows. The pragmatic path in 2026 is to use a managed SAML layer (BoxyHQ Jackson, WorkOS, Stytch, or Supabase Auth's SSO product) and focus on the workspace-side wiring.
What today's SaaSForge Core ships, and what it does not
SaaSForge Core ships SAML-ready hooks: invocation points in the auth flow, an attribute-mapping configuration shape, and workspace-member provisioning logic that knows how to read a SAML assertion's email and group claims and turn them into the right `workspace_members` row. The UI exposes SSO configuration on the workspace settings page so an Admin can paste in their IdP metadata.
What SaaSForge Core does not ship today is the full IdP plumbing, parsing SAML XML, validating signatures, handling AuthnRequest generation, and the ACS endpoint that consumes the assertion. That layer is what the planned SaaSForge SAML SSO Kit module addresses. The honest read for buyers today: Core gives you the workspace-side scaffolding so adding a managed SAML provider (BoxyHQ Jackson, WorkOS, or Supabase Auth SSO) is straightforward; the dedicated module makes it turnkey.
The SAML 2.0 flow in plain terms
In SP-initiated SSO, the user clicks 'Sign in with SSO' on your app, your app generates an AuthnRequest (an XML blob saying 'authenticate this user'), and redirects the browser to the customer's IdP. The IdP authenticates the user (their own login page, possibly with MFA), then posts a signed SAML assertion to your ACS endpoint (`/api/auth/saml/callback`). Your app validates the signature, reads the assertion's attributes (email, name, groups), and creates or updates a session.
In IdP-initiated SSO, the user starts at their IdP's app launcher (Okta dashboard, Azure portal) and clicks your app's tile. The IdP posts an unsolicited assertion to your ACS endpoint. This flow is convenient for users but harder to secure, your ACS has to be careful about replay attacks and unexpected RelayState. Most teams support SP-initiated as the primary flow and treat IdP-initiated as opt-in per customer.
Attribute mapping and just-in-time provisioning
The SAML assertion carries a NameID (usually the user's email) and a set of attributes the IdP chose to release. Common attributes: `email`, `firstName`, `lastName`, `groups` or `memberOf`. Your app maps these to internal fields: email becomes the auth.users row, groups can map to roles in the workspace.
Just-in-time (JIT) provisioning means a user who has never signed in before is created on the first successful SAML assertion. SaaSForge Core's hook accepts a mapping function that decides which workspace the new user joins and which role they get based on group claims. The alternative, manual provisioning, requires admins to invite every user before SSO works, which defeats most of the operational appeal.
export const samlMapping = {
workspaceId: "{{workspace_id_from_subdomain}}",
email: (assertion) => assertion.attributes.email,
role: (assertion) => {
const groups = assertion.attributes.groups ?? [];
if (groups.includes("admins")) return "admin";
if (groups.includes("members")) return "member";
return "viewer";
},
};Why teams use BoxyHQ Jackson, WorkOS, or Supabase SSO
Implementing SAML from XML primitives is a tarpit: signature canonicalization, certificate rotation, metadata exchange formats, and twenty years of IdP-specific quirks. BoxyHQ Jackson is an open-source SAML-as-a-service you can self-host; WorkOS and Stytch are managed services with per-connection pricing; Supabase Auth has its own SSO product on paid plans. All three give you a normalized API ('here's a signed assertion, decoded into attributes') so your app code never touches raw SAML XML.
The honest trade-off: managed services cost per connection ($125-$250/month/connection on some vendors) but reclaim the engineering month you would otherwise spend on XML signing edge cases. Self-hosted Jackson is free but adds operational surface. SaaSForge Core's hooks are written to plug into any of these, the workspace-side logic does not change.