# SAML 2.0 single sign-on

> Let your business customers sign in through their own identity provider (Okta, Microsoft Entra ID, Google Workspace) with SAML 2.0 SSO on a SupaHost project.

Source: https://supahost.app/docs/auth/sso-saml · Updated 2026-11-11

SAML 2.0 single sign-on lets people sign in to your app through their company's identity provider (IdP), such as Okta, Microsoft Entra ID, Google Workspace, OneLogin or JumpCloud. You add one SAML provider per customer organisation and link it to their email domains. Their users then sign in with their work account, and Supabase Auth creates or updates the user in your project.

SAML is switched on for every SupaHost project, on every plan. There's no extra charge per connection or per user.

## How it works

1. Your app calls `signInWithSSO` with the user's email domain (or a provider ID).
2. Auth finds the matching SAML provider and returns a URL that sends the user to their IdP.
3. The user signs in at their IdP, which posts a signed SAML response back to your project.
4. Auth checks the response, creates or updates the user, and redirects to your app with a session, like any other sign-in.

In the SAML setup, your project is the **service provider (SP)**, and your customer's IdP is the **identity provider**.

## Step 1: give the IdP your project's SP details

In the dashboard, open your project's **Auth** tab and scroll to **SAML 2.0 single sign-on**. The card shows three values to send to your customer's IdP admin:

| Field | Value |
|---|---|
| SP metadata URL | `https://app-one.acme.supahost.space/auth/v1/sso/saml/metadata?download=true` |
| Entity ID (audience) | `https://app-one.acme.supahost.space/auth/v1/sso/saml/metadata` |
| ACS (reply) URL | `https://app-one.acme.supahost.space/auth/v1/sso/saml/acs` |

Most IdPs can import everything from the metadata URL. If theirs asks for values one at a time, use the entity ID and ACS URL. Have them send the user's email as the NameID (or as an `email` attribute).

These addresses always use your project's `supahost.space` host, even after you add a custom domain. That way the IdP setup keeps working if your domains change.

## Step 2: add the provider in SupaHost

Your customer's IdP admin will give you a **metadata URL** or a **metadata XML file**. On the Auth tab, click **Add SAML provider**, then:

1. Choose **Metadata URL** and paste it, or choose **Paste XML** and paste the file's contents. A URL is better if the IdP rotates its certificates, because Auth re-reads it.
2. Under **Email domains**, list the domains whose users should use this provider, such as `acme.com, acme.co.uk`.
3. Optionally add an **Attribute mapping** (see below), then click **Add provider**.

The provider appears in the list with its ID. Only the instance owner can add, edit or remove providers. Team members can see them.

## Step 3: sign users in from your app

Sign in by email domain:

```ts
const { data, error } = await supabase.auth.signInWithSSO({
  domain: 'acme.com',
  options: { redirectTo: 'https://app.example.com/auth/callback' },
})
if (data?.url) window.location.href = data.url
```

Or by provider ID, if a customer has several domains or you store the ID per organisation:

```ts
await supabase.auth.signInWithSSO({ providerId: '7f1c2a4e-…' })
```

The `redirectTo` URL must be allowed under **Redirect URLs** on the Auth tab, the same as for OAuth. On the server side, handle the callback the same way as other redirect sign-ins (see [Server-side auth and SSR](/docs/auth/server-side)).

A common pattern is a single "Continue with SSO" field on your sign-in page: take the user's email, use the part after `@` as the domain, and fall back to a password sign-in if `signInWithSSO` returns an error saying no provider matches.

## Attribute mapping

By default Auth stores the user's email. To copy other SAML attributes into `user_metadata`, add a mapping as JSON:

```json
{
  "keys": {
    "name": { "name": "displayName" },
    "first_name": { "name": "givenName" },
    "department": { "name": "department", "default": "unknown" }
  }
}
```

Each key is the field name in `user_metadata`, and `name` is the attribute the IdP sends. Attribute names differ between IdPs; Entra ID, for example, often uses long URIs such as `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname`.

Treat these values like any other user metadata: fine for display, but don't use them for authorization. See [Roles, custom claims and RBAC](/docs/auth/custom-claims-rbac).

## Users and identities

- SSO users are ordinary Auth users with `is_sso_user = true`, plus an identity whose provider is `sso:<provider id>`.
- An SSO user is kept separate from a password or OAuth user with the same email. Plan which sign-in method each customer uses.
- Row Level Security works the same: `auth.uid()` is the SSO user's ID.
- To list a provider's users, query `auth.identities` in Studio's SQL editor: `select user_id from auth.identities where provider = 'sso:<provider id>'`.

## Removing or changing a provider

**Edit** lets you change the domains, attribute mapping or metadata. **Remove** deletes the provider: its users can no longer sign in with SSO, but their accounts stay in your project.

## Limits and notes

- SAML providers are managed in the dashboard. The Supabase CLI's `supabase sso` commands don't work with SupaHost, because they use Supabase's Management API. On your server, you can also call Auth's admin endpoint `/auth/v1/admin/sso/providers` with the `service_role` key.
- IdP-initiated sign-in (the user starts from their IdP's app dashboard) is supported. Users land on your **Site URL**, or on an allowed redirect URL if the IdP sends one as its RelayState.
- Encrypted SAML assertions aren't supported. Ask the IdP to sign assertions without encrypting them, which is the default for most IdPs.
- Each project has its own SAML signing certificate, which is included in the SP metadata.

## Testing without a real IdP

[Mock SAML](https://mocksaml.com) is a free test IdP. Add a provider with the metadata URL `https://mocksaml.com/api/saml/metadata` and the domain `example.com`. Then call `signInWithSSO({ domain: 'example.com' })` and sign in as any `@example.com` user. Remove the provider when you're done.
