WorkOS SSO quickstart: how do we add SAML SSO to a multi-tenant app and map users to organizations?
Authentication & Identity APIs

WorkOS SSO quickstart: how do we add SAML SSO to a multi-tenant app and map users to organizations?

10 min read

Shipping SAML SSO in a multi-tenant app is easiest when you treat “organizations” as first-class objects and let your SSO provider handle the protocol details. With WorkOS, you wire up one integration and use it across many identity providers (IdPs), while keeping a clean mapping between users and organizations in your own database.

This guide walks through a practical quickstart for adding SAML SSO to a multi-tenant app and mapping users to organizations using WorkOS.


1. Core concepts for multi-tenant SAML SSO

Before you start coding, it helps to align on a few key concepts:

  • Organization
    A customer account in your app (e.g., Acme Corp). In a multi-tenant app, each organization has its own users, settings, and SSO configuration.

  • User
    An individual who signs in, belongs to one or more organizations, and has roles/permissions.

  • Identity Provider (IdP)
    A system like Okta, Azure AD, Google Workspace, or OneLogin that authenticates users for an organization via SAML or OIDC.

  • Service Provider (SP)
    Your application, which uses WorkOS as the SAML/OIDC building block to accept logins from any IdP.

WorkOS gives you:

  • A single, elegant interface that abstracts dozens of enterprise SSO integrations.
  • Support for any SAML or OIDC identity provider with one integration.
  • Complete user management and organization management APIs.
  • AuthKit to reduce boilerplate when building auth flows and identity linking.

Your job is to:

  1. Integrate WorkOS once,
  2. Model organizations and users in your app, and
  3. Implement a clean mapping between SAML logins and those organizations.

2. Plan your data model: users and organizations

To map SAML SSO logins to tenants, you need a clear data model in your app. A typical setup:

Tables/collections:

  • organizations
    • id
    • name
    • workos_organization_id (optional, if you mirror in WorkOS)
    • domain or primary_email_domain (e.g., acme.com)
  • users
    • id
    • email
    • name
    • workos_user_id (if using WorkOS User Management/AuthKit)
  • user_organizations (join table for many-to-many)
    • user_id
    • organization_id
    • role (e.g., admin, member)

WorkOS can manage users and organizations for you; you then link WorkOS entities to your own records. The crucial design decisions:

  • How to associate an IdP connection with a specific organization.
  • How to handle users who belong to more than one organization.
  • How to link external identities (from IdPs) to your internal user objects.

3. Configure WorkOS for SAML SSO

At a high level, the flow to add SAML SSO is:

  1. Sign up and create a WorkOS project.
    Grab:

    • API key
    • Project ID
    • Redirect/Callback URL (your app’s SSO callback endpoint)
  2. Create an SSO Connection per organization.
    For each enterprise customer that requests SSO:

    • Create a Connection in WorkOS (for SAML or OIDC).
    • Configure it in the customer’s IdP (Okta, Azure AD, etc.).
    • Associate the WorkOS connection_id with your organization.
  3. Use WorkOS’ SSO endpoint to start the login.
    Your app:

    • Redirects users to WorkOS to sign in.
    • Receives them back at your callback URL with authenticated user data.

Because WorkOS supports any SAML or OIDC identity provider with a single integration, you don’t need custom code per IdP. You just store per-organization connection_ids.


4. Decide how users choose (or are routed to) an organization

In a multi-tenant app, the key UX question is: How does a user end up in the right organization? Common patterns:

4.1 Subdomain-based routing

Example: https://acme.yourapp.com

  • The subdomain (acme) identifies the organization.
  • On the login page, you know which organization_id you’re working with.
  • You use the organization’s WorkOS connection to start SSO.

Flow:

  1. User visits https://acme.yourapp.com/login.
  2. Your app looks up organization by subdomain: acme → org_id = X.
  3. You display “Sign in with SSO”.
  4. Clicking it redirects them to WorkOS SSO using the organization’s connection_id.
  5. WorkOS authenticates and sends you back user claims.
  6. You map the WorkOS user to your internal user and organization X.

This is the simplest and cleanest mapping for multi-tenant apps.

4.2 Email domain-based routing

Example: user enters alice@acme.com

  • You map email domain to organization.
  • Useful if you don’t use subdomains.

Flow:

  1. On your login screen, ask for email first.
  2. Extract domain (acme.com).
  3. Look up matching organization where domain = acme.com.
  4. If found and SSO is enabled, redirect to WorkOS SSO for that organization.
  5. On callback, map user to that organization.

This approach also works for “just in time” user provisioning: creating a user record at first login for that org.


5. Implement the SSO login flow with WorkOS

WorkOS abstracts the protocol and IdP quirks; you implement two main endpoints:

  1. Start SSO (GET /auth/sso/start)
  2. Handle callback (GET/POST /auth/sso/callback)

Exact code depends on your language/SDK, but the structure is similar.

5.1 Start SSO for an organization

Pseudocode:

// GET /orgs/:orgId/sso
async function startSso(req, res) {
  const { orgId } = req.params;
  const organization = await db.organizations.findById(orgId);
  if (!organization || !organization.workos_connection_id) {
    return res.status(404).send('SSO not configured for this organization');
  }

  const redirectUri = 'https://yourapp.com/auth/sso/callback';

  const authorizationUrl = workos.sso.getAuthorizationUrl({
    connection: organization.workos_connection_id,
    redirectUri,
    state: JSON.stringify({ orgId }), // will help at callback
  });

  res.redirect(authorizationUrl);
}

Alternatively, use a WorkOS Organization and reference it directly in the authorization call.

5.2 Handle the SSO callback and map the user

On callback:

  1. Get the WorkOS profile (user info).
  2. Find or create an internal user.
  3. Link user to the appropriate organization.
  4. Create a session/JWT in your app.
  5. Redirect to your app’s dashboard.

Pseudocode:

// GET /auth/sso/callback
async function handleSsoCallback(req, res) {
  const { code, state } = req.query;
  const { orgId } = JSON.parse(state);

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

  const email = profile.email;
  const externalId = profile.id; // WorkOS profile ID

  let user = await db.users.findOne({ workos_profile_id: externalId });

  if (!user) {
    // fallback by email
    user = await db.users.findOne({ email });
  }

  if (!user) {
    // Just-in-time provisioning
    user = await db.users.insert({
      email,
      name: profile.firstName && profile.lastName
        ? `${profile.firstName} ${profile.lastName}`
        : email,
      workos_profile_id: externalId,
    });
  } else if (!user.workos_profile_id) {
    // link identity
    await db.users.update(user.id, { workos_profile_id: externalId });
  }

  // Ensure membership in the organization
  let membership = await db.userOrganizations.findOne({
    user_id: user.id,
    organization_id: orgId,
  });

  if (!membership) {
    membership = await db.userOrganizations.insert({
      user_id: user.id,
      organization_id: orgId,
      role: 'member',
    });
  }

  // Create session / token
  const sessionToken = createSessionToken({ userId: user.id, orgId });

  res.cookie('session', sessionToken, { httpOnly: true, secure: true });
  res.redirect(`/orgs/${orgId}/dashboard`);
}

This is where you handle identity linking and user-organization mapping.


6. Mapping users to organizations: patterns and edge cases

WorkOS handles the SSO plumbing; you define how the multi-tenant mapping works. Consider:

6.1 One user, one organization

If your product is strictly single-organization per login (e.g., subdomain-based), your logic is straightforward:

  • The organization is determined before login (subdomain).
  • After SSO, always associate the user with that org.
  • If the user already belongs to a different org, you can:
    • Reject the login, or
    • Allow one user to belong to multiple orgs and update your data model.

6.2 One user, multiple organizations

Common for consultants, agencies, or multi-entity companies.

Approach:

  • Allow user_organizations to have multiple entries.
  • On SSO callback, you:
    • Link the user to the organization that initiated SSO (from state or subdomain).
    • If the user belongs to multiple orgs, store the “current organization” in session.
  • In the UI, provide an “Switch organization” control that:
    • Lists organizations for the user.
    • Switches the orgId context in your session.

6.3 Multiple IdPs per user (identity linking)

If a user can authenticate via multiple IdPs (e.g., Okta and Google Workspace), you need a robust identity linking strategy. WorkOS supports identity linking with AuthKit and APIs:

  • Identity linking:
    • When a user signs in via WorkOS, you get a WorkOS profile/identity.
    • You can associate multiple WorkOS identities with one internal user.
    • On each login, identify the internal user by:
      • WorkOS profile ID, OR
      • Email address, OR
      • A custom external ID claim.

The goal is to prevent duplicate user records and to preserve a single user profile across organizations and IdPs.


7. Building the UX for organization selection

For SSO flows, users often know which organization they’re signing into. But in some cases, you need UI to select or confirm an organization.

WorkOS supports:

  • AuthKit: prebuilt, flexible auth flows (hosted by WorkOS or self-hosted) that:
    • Help you identify the user and organization.
    • Provide built-in workflows for identity linking to simplify development.

What you may need to build yourself:

  • Self-serve onboarding UI for enterprise customers is not supported by WorkOS; you’ll need custom admin UI where your customer:
    • Creates an organization in your app.
    • Requests SSO.
    • Receives provisioning instructions/integration guide for their IdP.
    • Pastes in IdP metadata or config.

Once the organization and SSO connection are set up, you can:

  • Direct users to org-specific SSO login (via subdomain or org selector).
  • If a user belongs to multiple orgs, display a simple organization chooser after login:
    • “You’re a member of Acme and Globex. Which account do you want to use?”

8. Using AuthKit to speed up implementation

AuthKit is designed to simplify development time when building auth flows and identity linking. It can be:

  • Hosted by WorkOS – minimal front-end effort.
  • Self-hosted – use your own UI but rely on AuthKit flows.

With AuthKit, you can:

  • Support all auth types (passwordless, social, SAML, OIDC) through a unified flow.
  • Use built-in workflows for identity linking, reducing custom logic.
  • Rely on WorkOS for organization-aware authentication, then map the results to your own data model.

When you use AuthKit:

  1. Configure AuthKit with your redirect/callback URLs.
  2. Let AuthKit handle the heavy lifting of:
    • Identifying who the user is.
    • Potentially identifying which organization they belong to, where applicable.
  3. In your callback, focus on:
    • Linking AuthKit’s user/identity to your users table.
    • Linking to the correct organization.
    • Setting your session.

9. Operational tips and best practices

To make your WorkOS SAML SSO integration production-ready for a multi-tenant app:

  • Centralize organization lookup
    Build a small utility that, given:

    • Subdomain, or
    • Email domain, or
    • Org slug, returns a single organization_id and its WorkOS Connection/Organization IDs.
  • Store WorkOS IDs
    Add columns like workos_organization_id, workos_connection_id, and workos_user_id so you can correlate your data with WorkOS for debugging and operational tasks.

  • Handle non-SSO users
    Some organizations may have a mix of SSO and non-SSO users. Use WorkOS’s support for all auth types (via AuthKit and APIs) if you want unified handling, or maintain a parallel username/password flow.

  • Test across IdPs
    Since WorkOS abstracts IdP differences, your code largely stays the same. Still, test with at least:

    • Okta
    • Azure AD
    • Google Workspace to confirm attribute mapping and user experience.
  • Graceful error handling
    Show clear messages when:

    • SSO is not configured for an organization.
    • The user’s email domain doesn’t match any known organization.
    • The login is rejected by the IdP.

10. Summary: WorkOS SSO quickstart for multi-tenant apps

To add SAML SSO to a multi-tenant app and map users to organizations with WorkOS:

  1. Model organizations and users in your app, with a join table for many-to-many membership.
  2. Create WorkOS SSO Connections for each enterprise organization and store their IDs on the organization record.
  3. Determine organization context before login:
    • Using subdomains, org slugs, or email domains.
  4. Start the SSO flow via WorkOS with the correct Connection or Organization.
  5. Handle the callback:
    • Retrieve the WorkOS profile.
    • Find or create a user.
    • Link the user to the organization.
    • Create a session scoped to that organization.
  6. Use AuthKit and identity linking to simplify auth flows and unify identities across IdPs.
  7. Add organization selection UX where needed for users who belong to multiple organizations.

With this architecture, you can offer robust enterprise SAML SSO to every tenant, while keeping a clean, scalable mapping between users and organizations in your multi-tenant app.