Enterprise SSO
Configure SAML and OIDC single sign-on connections.
Overview
Enterprise Single Sign-On (SSO) allows users to authenticate using their organization's identity provider (IdP). Transactional Auth supports both SAML 2.0 and OpenID Connect (OIDC) protocols.
Supported Protocols
| Protocol | Use Case | Common Providers |
|---|---|---|
| OIDC | Modern IdPs, social login | Okta, Auth0, Azure AD |
| SAML 2.0 | Enterprise IdPs | Okta, Azure AD, OneLogin, ADFS |
Pre-configured Providers
Quick setup for popular providers:
| Provider | Protocols | Setup Time |
|---|---|---|
| Okta | OIDC, SAML | 5 minutes |
| Azure AD | OIDC, SAML | 5 minutes |
| Google Workspace | OIDC | 3 minutes |
| Auth0 | OIDC, SAML | 5 minutes |
| OneLogin | OIDC, SAML | 5 minutes |
Creating an SSO Connection
Via Dashboard
- Go to Auth > SSO Connections
- Click Add Connection
- Select provider or protocol
- Follow the configuration wizard
Via API
TypeScript:
// OIDC Connection
const connection = await client.auth.connections.create({
name: 'Acme Corp SSO',
type: 'OIDC',
provider: 'OKTA',
config: {
issuerUrl: 'https://acme.okta.com',
clientId: 'your-okta-client-id',
clientSecret: 'your-okta-client-secret',
scopes: ['openid', 'profile', 'email'],
},
domains: ['acme.com'],
jitProvisioning: true,
});Python:
connection = client.auth.connections.create(
name="Acme Corp SSO",
type="OIDC",
provider="OKTA",
config={
"issuer_url": "https://acme.okta.com",
"client_id": "your-okta-client-id",
"client_secret": "your-okta-client-secret",
"scopes": ["openid", "profile", "email"],
},
domains=["acme.com"],
jit_provisioning=True,
)OIDC Configuration
Required Settings
| Setting | Description |
|---|---|
issuerUrl | IdP's issuer URL (discovery endpoint) |
clientId | OAuth client ID from IdP |
clientSecret | OAuth client secret from IdP |
Optional Settings
| Setting | Description |
|---|---|
scopes | Additional scopes (default: openid, profile, email) |
responseType | OAuth response type |
authorizationEndpoint | Custom auth endpoint (if no discovery) |
tokenEndpoint | Custom token endpoint |
Example: Okta OIDC
const connection = await client.auth.connections.create({
name: 'Okta SSO',
type: 'OIDC',
provider: 'OKTA',
config: {
issuerUrl: 'https://your-domain.okta.com',
clientId: '0oaxxxxxxxxxxxx',
clientSecret: 'your-client-secret',
scopes: ['openid', 'profile', 'email', 'groups'],
},
});SAML Configuration
Required Settings
| Setting | Description |
|---|---|
idpEntityId | IdP's entity ID |
ssoUrl | IdP's SSO URL |
idpCertificate | IdP's signing certificate (X.509) |
Optional Settings
| Setting | Description |
|---|---|
sloUrl | Single Logout URL |
requestBinding | HTTP-POST or HTTP-Redirect |
signRequest | Sign SAML requests |
attributeMapping | Map IdP attributes to user profile |
Example: Azure AD SAML
const connection = await client.auth.connections.create({
name: 'Azure AD SSO',
type: 'SAML',
provider: 'AZURE_AD',
config: {
idpEntityId: 'https://sts.windows.net/{tenant-id}/',
ssoUrl: 'https://login.microsoftonline.com/{tenant-id}/saml2',
idpCertificate: `-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIEAgAAu...
-----END CERTIFICATE-----`,
attributeMapping: {
email: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress',
firstName: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname',
lastName: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname',
},
},
});Domain-Based Routing
Automatically route users to SSO based on email domain:
const connection = await client.auth.connections.create({
// ... connection config
domains: ['acme.com', 'acme.org'],
domainVerified: true,
});When a user enters user@acme.com:
- Check if domain has SSO connection
- Redirect to Acme's IdP
- User authenticates with IdP
- Return to your application
Verify Domain Ownership
// Get verification record
const verification = await client.auth.connections.getDomainVerification('conn_xxx', 'acme.com');
// Add TXT record to DNS
// _transactional-verify.acme.com TXT "verify-xxx"
// Verify
await client.auth.connections.verifyDomain('conn_xxx', 'acme.com');Just-In-Time (JIT) Provisioning
Automatically create user accounts on first SSO login:
const connection = await client.auth.connections.create({
// ... connection config
jitProvisioning: true,
defaultRole: 'MEMBER',
defaultOrganizationId: 'org_xxx', // Optional
});JIT provisioning will:
- Create a user account with IdP-provided attributes
- Link the SSO identity to the user
- Optionally add user to a default organization
Attribute Mapping
Map IdP attributes to user profile:
attributeMapping: {
// Standard attributes
email: 'email',
firstName: 'given_name',
lastName: 'family_name',
displayName: 'name',
picture: 'picture',
// Custom attributes (stored in metadata)
'metadata.department': 'department',
'metadata.employeeId': 'employee_id',
}Managing Connections
List Connections
const connections = await client.auth.connections.list();
for (const conn of connections) {
console.log(`${conn.name}: ${conn.type} (${conn.status})`);
}Get Connection
const connection = await client.auth.connections.get('conn_xxx');Update Connection
await client.auth.connections.update('conn_xxx', {
name: 'Updated Name',
config: {
scopes: ['openid', 'profile', 'email', 'groups'],
},
});Delete Connection
await client.auth.connections.delete('conn_xxx');Enable/Disable Connection
// Disable (users can't log in via this connection)
await client.auth.connections.update('conn_xxx', {
isActive: false,
});
// Enable
await client.auth.connections.update('conn_xxx', {
isActive: true,
});SSO Login Flow
Initiate SSO Login
// Option 1: By connection ID
const authUrl = `https://auth.usetransactional.com/sso/authorize?` + new URLSearchParams({
connection_id: 'conn_xxx',
client_id: 'your_client_id',
redirect_uri: 'https://yourapp.com/callback',
});
// Option 2: By email domain (auto-discovery)
const authUrl = `https://auth.usetransactional.com/auth?` + new URLSearchParams({
client_id: 'your_client_id',
redirect_uri: 'https://yourapp.com/callback',
login_hint: 'user@acme.com', // Domain triggers SSO
});Handle SSO Callback
app.get('/callback', async (req, res) => {
const { code, state } = req.query;
// Exchange code for tokens (same as regular OAuth)
const tokens = await exchangeCode(code);
// User info includes SSO identity
const user = await getUserInfo(tokens.accessToken);
console.log('SSO Provider:', user.identities[0]?.provider);
req.session.user = user;
res.redirect('/dashboard');
});Organization SSO
Connect SSO to specific organizations:
// Create organization-specific SSO
const connection = await client.auth.connections.create({
name: 'Acme Internal SSO',
type: 'SAML',
organizationId: 'org_xxx', // Specific to this org
// ... config
});Users who authenticate through this connection are automatically added to the organization.
Testing SSO
Test Connection
const result = await client.auth.connections.test('conn_xxx');
if (result.success) {
console.log('Connection working');
console.log('Sample user:', result.sampleUser);
} else {
console.log('Connection failed:', result.error);
}Test with Specific User
const result = await client.auth.connections.testUser('conn_xxx', {
email: 'test@acme.com',
});Troubleshooting
Common Issues
| Issue | Cause | Solution |
|---|---|---|
| Invalid signature | Certificate mismatch | Re-import IdP certificate |
| User not found | JIT disabled | Enable JIT provisioning |
| Attribute missing | Mapping incorrect | Check attribute names |
| Redirect loop | Misconfigured URIs | Verify redirect URIs |
Debug Mode
// Enable debug logging
const connection = await client.auth.connections.update('conn_xxx', {
debugMode: true,
});
// Check logs
const logs = await client.auth.connections.getLogs('conn_xxx');Next Steps
- Organizations - B2B multi-tenancy
- Security - Security policies
- API Reference - Complete API documentation
On This Page
- Overview
- Supported Protocols
- Pre-configured Providers
- Creating an SSO Connection
- Via Dashboard
- Via API
- OIDC Configuration
- Required Settings
- Optional Settings
- Example: Okta OIDC
- SAML Configuration
- Required Settings
- Optional Settings
- Example: Azure AD SAML
- Domain-Based Routing
- Verify Domain Ownership
- Just-In-Time (JIT) Provisioning
- Attribute Mapping
- Managing Connections
- List Connections
- Get Connection
- Update Connection
- Delete Connection
- Enable/Disable Connection
- SSO Login Flow
- Initiate SSO Login
- Handle SSO Callback
- Organization SSO
- Testing SSO
- Test Connection
- Test with Specific User
- Troubleshooting
- Common Issues
- Debug Mode
- Next Steps