SSO Implementation Plan
Current Auth Setup
- Provider: Supabase Auth
- Existing methods: Magic link (email OTP), Google OAuth, LinkedIn OAuth
- JWT verification: Custom middleware in
workers/api/src/middleware/auth.ts(ES256 + HS256) - Multi-tenant: Users belong to a tenant via
profiles.tenant_id; tenant resolved by domain, header, or user profile
Goal
Allow enterprise tenants to require SSO login for their users via their organizationβs identity provider (IdP). Users visiting a tenantβs domain would authenticate through their companyβs IdP instead of (or in addition to) magic link / social OAuth.
Recommended Providers to Support
Tier 1 β Must Have
| Protocol | Provider Examples | Why |
|---|---|---|
| SAML 2.0 | Okta, Azure AD (Entra ID), OneLogin, Ping Identity, ADFS | Industry standard for enterprise SSO. Required by most Fortune 500 procurement teams. |
| OpenID Connect (OIDC) | Azure AD, Okta, Google Workspace, Auth0 | Modern alternative to SAML. Many IdPs support both. Simpler to implement. |
Tier 2 β Nice to Have
| Provider | Why |
|---|---|
| Google Workspace (domain-restricted) | Already have Google OAuth β restrict to a specific domain for a tenant |
| Microsoft Entra ID (Azure AD) | Dominant in government and education (key accessibility markets) |
| Okta | Most common dedicated IdP in mid-market enterprise |
Tier 3 β Future
| Provider | Why |
|---|---|
| PingFederate / PingOne | Common in healthcare and finance |
| OneLogin | Growing in mid-market |
| LDAP (via SAML bridge) | Legacy enterprises β handle via SAML rather than direct LDAP |
| SCIM provisioning | Auto-create/deactivate users when added/removed in IdP |
Implementation Approach
Option A: Supabase SSO (Recommended)
Supabase has built-in SAML 2.0 SSO support on the Pro plan and above. This is the path of least resistance since we already use Supabase Auth.
How it works:
- Register enterprise IdP via Supabase Management API or Dashboard
- Map the IdP to a tenant domain (e.g.,
acme.comusers β SAML via Okta) - Supabase handles SAML assertion parsing, user creation, and JWT issuance
- Our existing JWT verification middleware works unchanged
Supabase SSO API:
# Register a SAML provider for a tenantcurl -X POST https://vuvwmfxssjosfphzpzim.supabase.co/auth/v1/admin/sso/providers \ -H "Authorization: Bearer SERVICE_ROLE_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "saml", "metadata_url": "https://idp.acme.com/metadata.xml", "domains": ["acme.com"], "attribute_mapping": { "keys": { "email": { "name": "email" }, "full_name": { "name": "displayName" } } } }'Login flow:
User visits tenant domain β enters email β backend checks domain for SSO β if SSO: redirect to /auth/v1/sso with domain param β IdP login β callback β JWT if no SSO: normal magic link / OAuth flowFrontend change (login page):
- Add an email input step before showing auth methods
- On email submit, call a new endpoint
GET /api/auth/[email protected] - If SSO is configured for that domain, redirect to Supabase SSO endpoint
- If not, show the existing magic link / OAuth options
Option B: External SSO Service (BoxyHQ, WorkOS)
If Supabase SSO proves limiting (e.g., need OIDC federation, SCIM, or more IdP control):
- WorkOS β turnkey SSO + Directory Sync. $125/mo per connection. Good DX.
- BoxyHQ (jackson) β open-source SAML/OIDC proxy. Self-hostable. Free.
These act as a SAML/OIDC proxy: enterprise IdP β WorkOS/BoxyHQ β our app gets a standard OAuth token.
Recommendation
Start with Supabase SSO (Option A). It requires no new dependencies, our JWT middleware already works, and SAML covers 90%+ of enterprise IdP requirements. Move to Option B only if we hit limitations.
Database Changes
-- Add SSO configuration to tenantsALTER TABLE tenants ADD COLUMN sso_enabled boolean NOT NULL DEFAULT false;ALTER TABLE tenants ADD COLUMN sso_provider_id text; -- Supabase SSO provider IDALTER TABLE tenants ADD COLUMN sso_enforce boolean NOT NULL DEFAULT false; -- Force SSO (no magic link fallback)ALTER TABLE tenants ADD COLUMN sso_domains text[]; -- Email domains routed to this SSO provider
-- Track SSO provider metadata for admin UICREATE TABLE tenant_sso_config ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id uuid NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, provider_type text NOT NULL CHECK (provider_type IN ('saml', 'oidc')), provider_name text NOT NULL, -- "Okta", "Azure AD", etc. metadata_url text, -- IdP metadata URL (SAML) issuer_url text, -- OIDC issuer client_id text, -- OIDC client ID (encrypted) supabase_provider_id text, -- ID returned by Supabase SSO API status text NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'active', 'disabled')), created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), UNIQUE(tenant_id));
ALTER TABLE tenant_sso_config ENABLE ROW LEVEL SECURITY;API Endpoints
| Method | Path | Purpose |
|---|---|---|
GET | /api/auth/sso-check?email={email} | Check if email domain has SSO configured; return redirect URL if so |
POST | /api/tenant-admin/sso | Configure SSO for a tenant (admin only) |
GET | /api/tenant-admin/sso | Get current SSO config for a tenant |
DELETE | /api/tenant-admin/sso | Remove SSO config for a tenant |
POST | /api/tenant-admin/sso/test | Initiate a test SSO login |
Frontend Changes
Login Page (/auth/login)
- Email-first flow: Show email input before auth method buttons
- SSO detection: On email blur/submit, call
/api/auth/sso-check - SSO redirect: If SSO configured, show βSign in with [Company] SSOβ button that redirects to Supabase SSO endpoint
- Fallback: If
sso_enforceis false, also show magic link / OAuth options - Direct URL: Support
?sso=domain.comquery param for bookmarkable SSO login links
Tenant Admin (/tenant-admin/sso)
- Upload IdP metadata XML or enter metadata URL
- Select provider type (SAML or OIDC)
- Test SSO connection
- Toggle enforcement (SSO-only vs SSO + magic link)
- View SSO login activity / errors
Onboarding Flow for Enterprise Customers
- Customer purchases Enterprise credit pack or contacts sales
- We create a tenant with their domain in
tenant_domains - Customer provides IdP metadata URL (or XML file)
- We register the SAML provider via Supabase SSO API (or admin UI does it)
- Test login with customerβs IT admin
- Enable
sso_enforceif customer requires SSO-only access - Customerβs users visit the tenant domain and authenticate via their IdP
Security Considerations
- Domain verification: Verify tenant owns the domain before allowing SSO (DNS TXT record or email to domain admin). Already partially handled by
tenant_domains.verified. - SSO enforcement: When
sso_enforce = true, reject magic link and OAuth logins for users with matching email domains. - JIT provisioning: Auto-create user profiles on first SSO login (Supabase handles this). Set
tenant_idbased on the SSO providerβs tenant. - Session duration: Enterprise tenants may want shorter session TTLs. Add
session_ttl_hoursto tenant config. - Audit logging: Log all SSO login attempts (success/failure) for enterprise compliance.