# How Single Sign-On (SSO) Works in Twenty CRM: OIDC and SAML Implementation

> Discover how Twenty CRM utilizes OIDC and SAML for secure Single Sign-On (SSO). Learn how admins register identity providers and users authenticate seamlessly.

- Repository: [Twenty/twenty](https://github.com/twentyhq/twenty)
- Tags: how-to-guide
- Published: 2026-03-27

---

**Twenty CRM supports enterprise Single Sign-On (SSO) through OpenID Connect (OIDC) and SAML protocols, featuring a billing-entitlement-gated architecture where administrators register identity providers via GraphQL and users authenticate through secure, guard-protected HTTP endpoints that generate login tokens upon successful IdP callback.**

Twenty CRM implements enterprise-grade SSO using standard authentication protocols to streamline workspace access for corporate users. The system is built on a modular NestJS architecture with strict feature gating via the `SSO` billing entitlement in the `twentyhq/twenty` repository. This technical guide explains how the platform handles identity provider registration, URL generation, and secure callback processing for both OIDC and SAML flows.

## Architecture and Feature Gating

### Billing Entitlement Enforcement

Every SSO operation begins with an entitlement check. The `SSOService.isSSOEnabled()` method verifies whether the workspace has the `SSO` billing entitlement active, throwing an `SSOException` if the feature is unavailable. This gate appears in [`packages/twenty-server/src/engine/core-modules/sso/services/sso.service.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/core-modules/sso/services/sso.service.ts) at lines 39-51, ensuring that only eligible workspaces can access SSO functionality.

### Identity Provider Storage

Identity providers are persisted as `WorkspaceSSOIdentityProviderEntity` records in the `workspaceSSOIdentityProvider` table. The entity stores protocol-specific fields: `clientID` and `clientSecret` for OIDC providers, or `ssoURL` and `certificate` for SAML configurations. The entity definition resides in [`packages/twenty-server/src/engine/core-modules/sso/workspace-sso-identity-provider.entity.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/core-modules/sso/workspace-sso-identity-provider.entity.ts).

## The SSO Authentication Flow

### Step 1: URL Generation and Flow Initiation

The `SSOService` constructs two critical URLs for each provider. The **issuer URL** serves as the entry point for IdP redirects, while the **callback URL** receives the authentication response. These are generated by `buildIssuerURL()` and `buildCallbackUrl()` methods using the server's base `SERVER_URL` environment variable. Users initiate login by requesting an authorization URL via the `getAuthorizationUrlForSSO` query, which returns a URL pointing to either `/auth/oidc/login/:identityProviderId` or `/auth/saml/login/:identityProviderId`.

### Step 2: Guard-Protected Endpoints

The HTTP routes in `SSOAuthController` are protected by protocol-specific guards. `OIDCAuthGuard` and `SAMLAuthGuard` (located in `src/engine/core-modules/auth/guards`) trigger the underlying Passport strategies (`passport-openid-client` for OIDC, `passport-saml` for SAML). These guards validate the request context and redirect the user to the corporate IdP login page.

### Step 3: Callback Processing and Token Generation

Upon successful authentication, the IdP redirects to `/auth/oidc/callback` or `/auth/saml/callback/:id`. The `SSOAuthController.authCallback()` method handles these routes by:
- Looking up the `WorkspaceSSOIdentityProviderEntity` from the request context
- Resolving the workspace via `authService.findWorkspaceForSignInUp()`
- Generating a login token through `generateLoginToken()` which uses `LoginTokenService`
- Redirecting the browser to the UI with the token appended as `/verify?loginToken=...`

If the **connected-account** feature flag is enabled, the system also creates a `ConnectedAccount` record linking the user to the external IdP via `authService.createSSOConnectedAccountIfFeatureFlagIsOn()`.

## Configuring SSO Providers via GraphQL

Administrators manage identity providers through the `SSOResolver` GraphQL API. The resolver exposes mutations for `createOIDCIdentityProvider`, `createSAMLIdentityProvider`, `editSSOIdentityProvider`, and `deleteSSOIdentityProvider`, plus the `getSSOIdentityProviders` query.

### Registering an OIDC Provider

To create an OIDC identity provider, administrators call the `createOIDCIdentityProvider` mutation:

```graphql
mutation CreateOIDC($input: SetupOIDCSsoInput!) {
  createOIDCIdentityProvider(input: $input) {
    id
    type
    name
    status
    issuer
  }
}

```

```json
{
  "input": {
    "issuer": "https://login.microsoftonline.com/common/v2.0",
    "clientID": "abc123",
    "clientSecret": "super-secret",
    "name": "Azure AD"
  }
}

```

The `SSOService.createOIDCIdentityProvider()` method validates the SSO entitlement, discovers the issuer metadata via `openid-client`, persists the entity, and returns a sanitized DTO.

### Querying Authorization URLs

Client applications retrieve login URLs using the `getAuthorizationUrlForSSO` query:

```typescript
// Client side (e.g., React)
const { data } = await apolloClient.query({
  query: GET_SSO_URL,
  variables: { providerId: "1234", search: { redirect: "/dashboard" } },
});

const { authorizationURL } = data.getAuthorizationUrlForSSO;
window.location.href = authorizationURL; // redirects to IdP login

```

This is implemented in `SSOService.getAuthorizationUrlForSSO()` at lines 200-228, which internally calls `buildIssuerURL()`.

## Implementation Details and Code Structure

### Core Service Layer

The `SSOService` class in [`sso.service.ts`](https://github.com/twentyhq/twenty/blob/main/sso.service.ts) encapsulates all business logic for provider CRUD operations, URL construction, and entitlement verification. Key methods include `isSSOEnabled()`, `buildIssuerURL()`, `buildCallbackUrl()`, and `getAuthorizationUrlForSSO()`.

### Controller and Guards

The `SSOAuthController` in [`sso-auth.controller.ts`](https://github.com/twentyhq/twenty/blob/main/sso-auth.controller.ts) handles HTTP transport. It coordinates with `OIDCAuthGuard` and `SAMLAuthGuard` to initiate flows, then processes callbacks through `authCallback()` and `generateLoginToken()`. The controller ensures secure user creation and workspace association before issuing tokens.

### Frontend Integration

Client applications implement SSO by querying the authorization URL and performing a full-page redirect:

```tsx
import { useQuery, gql } from '@apollo/client';
import { useEffect } from 'react';
import { useParams } from 'react-router-dom';

const GET_AUTH_URL = gql`
  query($id: String!) {
    getAuthorizationUrlForSSO(identityProviderId: $id, searchParams: { redirect: "/home" }) {
      authorizationURL
    }
  }
`;

export function SSOLogin() {
  const { id } = useParams(); // identityProviderId from route
  const { data, loading } = useQuery(GET_AUTH_URL, { variables: { id } });

  useEffect(() => {
    if (!loading && data?.getAuthorizationUrlForSSO?.authorizationURL) {
      window.location.href = data.getAuthorizationUrlForSSO.authorizationURL;
    }
  }, [loading, data]);

  return <p>Redirecting to your SSO provider…</p>;
}

```

## Summary

- **Feature Gating**: All SSO functionality requires the `SSO` billing entitlement, enforced by `SSOService.isSSOEnabled()` in [`sso.service.ts`](https://github.com/twentyhq/twenty/blob/main/sso.service.ts).
- **Provider Storage**: Identity providers are stored as `WorkspaceSSOIdentityProviderEntity` records with protocol-specific configurations in the `workspaceSSOIdentityProvider` table.
- **URL Construction**: The system dynamically builds issuer and callback URLs using `SERVER_URL` via `buildIssuerURL()` and `buildCallbackUrl()`.
- **Authentication Flow**: Guard-protected endpoints (`/auth/oidc/login/:id`, `/auth/saml/login/:id`) initiate flows, while callback endpoints generate login tokens and redirect users to the application.
- **Management API**: Administrators configure providers through GraphQL mutations in `SSOResolver`, while clients retrieve login URLs via `getAuthorizationUrlForSSO`.

## Frequently Asked Questions

### What protocols does Twenty CRM support for SSO?

Twenty CRM supports **OpenID Connect (OIDC)** and **SAML** protocols for Single Sign-On. The implementation uses `passport-openid-client` for OIDC flows and `passport-saml` for SAML integrations, with dedicated guards (`OIDCAuthGuard` and `SAMLAuthGuard`) handling each protocol in `src/engine/core-modules/auth/guards`.

### How is the SSO feature enabled in a Twenty CRM workspace?

SSO is controlled by a billing entitlement. The `SSOService.isSSOEnabled()` method checks this entitlement before any SSO operation and throws an `SSOException` if the feature is not active for the workspace. This ensures only eligible workspaces can register identity providers or initiate SSO flows.

### What happens after a user authenticates with the corporate IdP?

After successful IdP authentication, the user is redirected to `/auth/oidc/callback` or `/auth/saml/callback/:id`. The `SSOAuthController.authCallback()` method processes this response by looking up the provider configuration, resolving the workspace, creating or reusing the user account, and generating a login token via `LoginTokenService`. The user is then redirected to the application with the token appended to the URL.

### Can administrators configure multiple identity providers for one workspace?

Yes, administrators can register multiple identity providers per workspace. Each provider is stored as a separate `WorkspaceSSOIdentityProviderEntity` record in the `workspaceSSOIdentityProvider` table, and the `getSSOIdentityProviders` GraphQL query returns all configured providers for management or display in login interfaces.