Skip to main content

SSO & SCIM

Enterprise Single Sign-On (SSO) and automated user provisioning (SCIM) for centralized identity management.

SSO Overview

Connect Deeployd to your identity provider:

User → Identity Provider → SAML/OIDC → Deeployd

Supported Providers

ProviderSAMLOIDC
OktaYesYes
Azure ADYesYes
Google WorkspaceYesYes
OneLoginYesYes
Ping IdentityYesYes
Auth0YesYes
JumpCloudYesYes
Custom SAML IdPYes-
Custom OIDC IdP-Yes

Setting Up SSO

SAML Configuration

  1. Create SSO Connection
const connection = await client.sso.create({
name: 'Corporate Okta',
providerType: 'saml',
wellKnownProvider: 'okta', // Optional: for guided setup

samlConfig: {
// From your IdP
idpEntityId: 'http://www.okta.com/exk123',
idpSsoUrl: 'https://yourcompany.okta.com/app/123/sso/saml',
idpSloUrl: 'https://yourcompany.okta.com/app/123/slo/saml', // Optional
idpCertificate: '-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----',

// Attribute mapping
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',
groups: 'http://schemas.xmlsoap.org/claims/Group'
}
},

// Settings
allowedDomains: ['company.com', 'subsidiary.com'],
enforceForDomains: true, // Require SSO for these domains
jitProvisioning: true, // Create users on first login
defaultRole: 'member'
});

// Get SP metadata for your IdP
console.log(connection.spEntityId);
console.log(connection.spAcsUrl);
console.log(connection.spMetadataUrl);
  1. Configure Your IdP

Use the Service Provider (SP) details from Deeployd:

SettingValue
SP Entity IDhttps://api.deeployd.com/sso/saml/{connection-id}
ACS URLhttps://api.deeployd.com/sso/saml/{connection-id}/acs
SP Metadatahttps://api.deeployd.com/sso/saml/{connection-id}/metadata
  1. Test the Connection
const result = await client.sso.test(connection.id);

if (result.success) {
console.log('SSO connection working!');
} else {
console.log('Error:', result.error);
}

OIDC Configuration

const connection = await client.sso.create({
name: 'Corporate Azure AD',
providerType: 'oidc',
wellKnownProvider: 'azure-ad',

oidcConfig: {
// From Azure AD app registration
issuer: 'https://login.microsoftonline.com/{tenant-id}/v2.0',
authorizationEndpoint: 'https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/authorize',
tokenEndpoint: 'https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token',
userInfoEndpoint: 'https://graph.microsoft.com/oidc/userinfo',
jwksUri: 'https://login.microsoftonline.com/{tenant-id}/discovery/v2.0/keys',

clientId: 'your-app-client-id',
clientSecret: 'your-app-client-secret',

scopes: ['openid', 'profile', 'email'],

claimMapping: {
email: 'email',
firstName: 'given_name',
lastName: 'family_name',
groups: 'groups'
}
},

allowedDomains: ['company.com'],
jitProvisioning: true
});

Role Mapping

Map IdP groups to Deeployd roles:

await client.sso.update(connection.id, {
roleMapping: [
{ idpGroup: 'Deeployd-Admins', role: 'admin' },
{ idpGroup: 'Deeployd-Users', role: 'member' },
{ idpGroup: 'Deeployd-Viewers', role: 'viewer' }
],
defaultRole: 'viewer' // If no group matches
});

SSO Login Flow

Initiate Login

// Redirect user to initiate SSO
const loginUrl = await client.sso.initiateLogin({
connectionId: 'sso-123',
redirectUri: 'https://your-app.com/auth/callback'
});

window.location.href = loginUrl;

Handle Callback

// In your callback handler
app.get('/auth/callback', async (req, res) => {
const { code, state } = req.query;

const tokens = await client.sso.handleCallback({
connectionId: 'sso-123',
code,
state
});

// Set session and redirect
req.session.accessToken = tokens.accessToken;
res.redirect('/dashboard');
});

SCIM Overview

System for Cross-domain Identity Management (SCIM) enables automatic user provisioning:

Identity Provider → SCIM API → Deeployd

Benefits

  • Automated provisioning: Users created when added in IdP
  • Automated deprovisioning: Users removed when deleted in IdP
  • Sync groups: Team membership from IdP groups
  • Real-time updates: Changes sync immediately

Setting Up SCIM

Enable SCIM

const scimConfig = await client.scim.enable();

console.log(scimConfig.baseUrl); // SCIM endpoint URL
console.log(scimConfig.token); // Bearer token for authentication

SCIM Endpoints

EndpointMethodDescription
/scim/v2/UsersGETList users
/scim/v2/UsersPOSTCreate user
/scim/v2/Users/{id}GETGet user
/scim/v2/Users/{id}PATCHUpdate user
/scim/v2/Users/{id}DELETEDelete user
/scim/v2/GroupsGETList groups
/scim/v2/GroupsPOSTCreate group
/scim/v2/Groups/{id}PATCHUpdate group
/scim/v2/Groups/{id}DELETEDelete group

Configure Your IdP

In Okta, Azure AD, or other SCIM-capable IdP:

SettingValue
SCIM Base URLhttps://api.deeployd.com/scim/v2
AuthenticationBearer Token
Token{your-scim-token}
Provisioning FeaturesCreate, Update, Delete

SCIM User Schema

{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName": "jdoe@company.com",
"name": {
"givenName": "John",
"familyName": "Doe"
},
"emails": [
{
"value": "jdoe@company.com",
"primary": true
}
],
"active": true,
"groups": [
{
"value": "group-123",
"display": "Engineering"
}
]
}

SCIM Group Schema

{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"displayName": "Engineering",
"members": [
{
"value": "user-123",
"display": "John Doe"
}
]
}

SCIM Logs

Monitor SCIM operations:

const logs = await client.scim.getLogs({
startDate: '2024-01-01',
operation: 'create', // create, update, delete
status: 'success' // success, error
});

for (const log of logs.data) {
console.log({
timestamp: log.timestamp,
operation: log.operation,
resourceType: log.resourceType,
status: log.status,
request: log.requestBody,
response: log.responseBody
});
}

Rotate SCIM Token

// Generate new token
const newToken = await client.scim.rotateToken();

// Update your IdP with the new token
console.log(newToken.token);

// Old token remains valid for 24 hours to allow migration

Domain Verification

Verify domain ownership for SSO:

// Start verification
const verification = await client.sso.verifyDomain('company.com');

// Add DNS record
console.log(verification.recordType); // TXT
console.log(verification.recordName); // _deeployd-verification
console.log(verification.recordValue); // deeployd-verify=xxxxx

// Check verification status
const status = await client.sso.checkDomainVerification('company.com');
console.log(status.verified); // true/false

SSO Enforcement

Require SSO for specific domains:

await client.sso.update(connection.id, {
enforceForDomains: true,
allowedDomains: ['company.com', 'subsidiary.com']
});

// Users with these email domains must use SSO
// Password login will be disabled for them

Troubleshooting

"SAML Response Invalid"

  1. Check IdP certificate is correct
  2. Verify ACS URL matches
  3. Check system clocks are in sync (SAML has time constraints)

"User Not Provisioned"

  1. Check JIT provisioning is enabled
  2. Verify email domain is in allowed list
  3. Check attribute mapping for email claim

"Group Sync Not Working"

  1. Verify groups claim is in SAML/OIDC response
  2. Check role mapping configuration
  3. Ensure IdP is sending group memberships

SCIM Errors

// Check SCIM logs for details
const errors = await client.scim.getLogs({
status: 'error',
limit: 10
});

for (const error of errors.data) {
console.log(error.errorMessage);
console.log(error.requestBody);
}

Best Practices

1. Test Before Enforcement

// Start with SSO optional
enforceForDomains: false

// Test with pilot users
// Then enable enforcement
enforceForDomains: true

2. Keep Backup Admin

Always have at least one admin who can login without SSO (for emergencies).

3. Map Groups Carefully

// Be explicit about role mappings
roleMapping: [
{ idpGroup: 'Deeployd-Admins', role: 'admin' },
{ idpGroup: 'Deeployd-Users', role: 'member' }
],
defaultRole: 'viewer' // Safe default

4. Monitor SCIM Sync

// Set up alerts for SCIM failures
await client.webhooks.create({
url: 'https://your-app.com/alerts',
events: ['scim.sync_failed']
});

Next: Learn about Approvals for human oversight of sensitive operations.