Enterprise Single Sign-On (SSO)
Enable enterprise-grade SSO authentication with WorkOS integration supporting SAML, OAuth 2.0, and OpenID Connect
Enterprise Single Sign-On (SSO)
The .do platform provides enterprise-grade Single Sign-On (SSO) capabilities through WorkOS AuthKit, allowing your customers' employees to authenticate using their organization's identity provider.
Overview
Enterprise SSO enables:
- Centralized Identity Management - Users authenticate through their company's IdP (Okta, Microsoft Entra, Google Workspace, etc.)
- Streamlined Onboarding - New employees automatically get access through their organization's provisioning
- Enhanced Security - Enforce MFA, conditional access, and security policies at the IdP level
- Reduced Support - IT admins manage access through familiar tools
- Compliance - Meet SOC 2, HIPAA, and enterprise security requirements
Supported Identity Providers
WorkOS supports 50+ identity providers including:
SAML 2.0 Providers
- Microsoft Entra ID (formerly Azure AD)
- Okta
- OneLogin
- Ping Identity
- JumpCloud
- Google Workspace
- Auth0
- Duo
- VMware Workspace ONE
- Rippling
- Generic SAML 2.0
OAuth 2.0 / OpenID Connect
- Google OAuth
- Microsoft OAuth
- GitHub OAuth
- GitLab OAuth
Architecture
How It Works
1. Connection Setup
Each customer organization gets a unique SSO Connection:
import { WorkOS } from '@workos-inc/node'
const workos = new WorkOS(process.env.WORKOS_API_KEY)
// Customer's IT admin configures their IdP
const connection = await workos.sso.getConnection('conn_01HXYZ...')
console.log(connection)
// {
// id: 'conn_01HXYZ...',
// organizationId: 'org_01ABC...',
// name: 'Acme Corp Okta',
// type: 'OktaSAML',
// state: 'active',
// domains: [
// { domain: 'acme.com' }
// ]
// }2. User Authentication Flow
When a user signs in with SSO:
// Step 1: Initiate SSO
const authorizationUrl = workos.sso.getAuthorizationURL({
organization: 'org_01ABC...', // or domain: 'acme.com'
clientId: process.env.WORKOS_CLIENT_ID,
redirectUri: 'https://yourapp.do/auth/callback',
state: 'optional-state-value'
})
// Redirect user to authorizationUrl
// Step 2: Handle callback
const { profile } = await workos.sso.getProfileAndToken({
code: 'authorization_code',
clientId: process.env.WORKOS_CLIENT_ID
})
console.log(profile)
// {
// id: 'prof_01XYZ...',
// organizationId: 'org_01ABC...',
// connectionId: 'conn_01HXYZ...',
// connectionType: 'OktaSAML',
// email: '[email protected]',
// firstName: 'Jane',
// lastName: 'Doe',
// idpId: 'okta-user-id',
// rawAttributes: { ... }
// }3. Domain-Based Routing
Automatically route users to their organization's SSO:
// User enters email: [email protected]
const email = '[email protected]'
const domain = email.split('@')[1]
// Find organization by domain
const organizations = await workos.organizations.listOrganizations({
domains: [domain]
})
if (organizations.data.length > 0) {
const org = organizations.data[0]
// Redirect to SSO for this organization
const authUrl = workos.sso.getAuthorizationURL({
organization: org.id,
clientId: process.env.WORKOS_CLIENT_ID,
redirectUri: 'https://yourapp.do/auth/callback'
})
// Redirect user to authUrl
} else {
// Fall back to password authentication
}Admin Portal
WorkOS provides a self-service Admin Portal where your customers' IT admins can configure SSO without developer involvement.
Generating Portal Links
// Generate Admin Portal link for customer
const { link } = await workos.portal.generateLink({
organization: 'org_01ABC...',
intent: 'sso', // or 'dsync' for Directory Sync
returnUrl: 'https://platform.do/settings'
})
// Send link to customer's IT admin
console.log(link)
// https://id.workos.com/portal/launch?secret=secret_01XYZ...Portal Features
The Admin Portal allows IT admins to:
-
Configure SSO Connection
- Select IdP type (Okta, Entra, etc.)
- Upload SAML metadata or enter OAuth credentials
- Test connection before going live
-
Manage Domains
- Add verified domains (e.g.,
acme.com) - Enable domain-based routing
- Add verified domains (e.g.,
-
Monitor Activity
- View recent authentication events
- Troubleshoot connection issues
-
Manage Settings
- Enable/disable connection
- Rotate credentials
- Configure JIT provisioning
Just-In-Time (JIT) User Provisioning
Automatically create user accounts on first SSO login:
on($.User.sso.authenticated, async ({ profile }) => {
// Check if user exists
let user = await db.find($.User, { email: profile.email })
if (!user) {
// Create user on first login (JIT provisioning)
user = await $.User.create({
email: profile.email,
firstName: profile.firstName,
lastName: profile.lastName,
organizationId: profile.organizationId,
idpId: profile.idpId,
authMethod: 'sso'
})
// Assign default role
await $.User.assign.Role({
userId: user.id,
roleId: 'default-employee-role'
})
await send($.Email.send, {
to: user.email,
template: 'welcome-sso',
data: { user }
})
}
// Create session
return { userId: user.id }
})Multi-Factor Authentication (MFA)
SSO inherits MFA from the customer's IdP:
- Customer-Controlled - IT admins configure MFA policies in their IdP
- Transparent - Your app doesn't handle MFA logic
- Compliance - Meet enterprise security requirements automatically
Common MFA methods supported by IdPs:
- SMS/Phone verification
- Authenticator apps (Google Authenticator, Duo, etc.)
- Hardware tokens (YubiKey, etc.)
- Biometrics (Touch ID, Face ID)
- Push notifications
Security Best Practices
1. Validate Organization Membership
Always verify users belong to the expected organization:
const { profile } = await workos.sso.getProfileAndToken({ code })
// Verify organization
if (profile.organizationId !== expectedOrgId) {
throw new Error('User does not belong to this organization')
}2. Handle Connection State
Check connection status before initiating SSO:
const connection = await workos.sso.getConnection(connectionId)
if (connection.state !== 'active') {
// Show error: "SSO is not configured for your organization"
return
}3. Implement Session Management
Create secure, time-limited sessions after SSO authentication:
const session = await createSession({
userId: user.id,
organizationId: profile.organizationId,
authMethod: 'sso',
expiresIn: '24h'
})
// Set secure cookie
setCookie('session', session.token, {
httpOnly: true,
secure: true,
sameSite: 'lax',
maxAge: 86400
})4. Log Authentication Events
Track all SSO authentication for audit trails:
await send($.Event.log, {
type: 'auth.sso.success',
userId: user.id,
organizationId: profile.organizationId,
metadata: {
connectionType: profile.connectionType,
ipAddress: request.ip,
userAgent: request.headers['user-agent']
}
})Pricing
WorkOS SSO pricing (as of 2025):
| Tier | Connections | Price per Connection/Month |
|---|---|---|
| 1-15 | 15 or fewer | $125 |
| 16-30 | 16-30 | $100 |
| 31-50 | 31-50 | $80 |
| 51-100 | 51-100 | $65 |
| 101+ | 101+ | Custom pricing |
Note: Automatic volume discounts apply as you grow.
Testing SSO
Development Environment
WorkOS provides a Magic Link SSO for testing:
// Use magic link SSO in development
const authUrl = workos.sso.getAuthorizationURL({
provider: 'authkit', // Magic link provider
clientId: process.env.WORKOS_CLIENT_ID,
redirectUri: 'http://localhost:3000/auth/callback'
})Test Organizations
Create test organizations with different IdPs:
# Create test organization
curl -X POST https://api.workos.com/organizations \
-H "Authorization: Bearer $WORKOS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Test Corp",
"domains": ["testcorp.local"]
}'Migration from Other Auth Systems
From Auth0
// Auth0
const auth0 = new AuthenticationClient({
domain: 'your-domain.auth0.com',
clientId: '...'
})
// WorkOS equivalent
const workos = new WorkOS(process.env.WORKOS_API_KEY)
const authUrl = workos.sso.getAuthorizationURL({
organization: 'org_...',
clientId: process.env.WORKOS_CLIENT_ID,
redirectUri: 'https://yourapp.do/callback'
})From Okta
// Okta
const authClient = new OktaAuth({
issuer: 'https://dev-12345.okta.com',
clientId: '...'
})
// WorkOS equivalent
const workos = new WorkOS(process.env.WORKOS_API_KEY)
// Customers configure Okta via Admin Portal
// You just handle the standardized WorkOS responseMonitoring & Analytics
Track SSO usage and health:
// Get connection events
const events = await workos.events.listEvents({
events: ['connection.activated', 'connection.deactivated'],
organizationId: 'org_01ABC...'
})
// Monitor authentication success/failure rates
const metrics = await db.query(`
SELECT
date_trunc('day', timestamp) as day,
count(*) FILTER (WHERE event_type = 'auth.sso.success') as successes,
count(*) FILTER (WHERE event_type = 'auth.sso.failure') as failures
FROM events
WHERE organization_id = $1
GROUP BY day
ORDER BY day DESC
`, [organizationId])Troubleshooting
Common Issues
"SSO configuration not found"
- Connection not created or inactive
- Domain not verified
- Check Admin Portal configuration
"Invalid SAML response"
- IdP metadata outdated
- Certificate expired
- Clock skew between systems
"User not found after SSO"
- JIT provisioning not enabled
- User creation failed
- Check application logs
Debug Mode
Enable detailed logging for troubleshooting:
const workos = new WorkOS(process.env.WORKOS_API_KEY, {
https: {
timeout: 30000
}
})
// Log all requests
workos.on('request', (req) => {
console.log('WorkOS Request:', req)
})
workos.on('response', (res) => {
console.log('WorkOS Response:', res)
})Related Documentation
- Directory Sync (SCIM) - Automatic user provisioning
- Organizations - Multi-tenant organization management
- Audit Logs - Compliance and security monitoring
- Admin Portal - Self-service configuration
- Authentication API - Authentication methods
- WorkOS Documentation - Official WorkOS SSO docs