Set Up SSO for Your Organization

Configure SAML single sign-on with your identity provider, set up SCIM automated provisioning, and manage the user lifecycle.

advanced20 min read

What You'll Build

This guide walks you through configuring enterprise single sign-on (SSO) for your organisation on Lifestream Vault.

By the end you will have:

  • A SAML 2.0 integration between Lifestream Vault (the Service Provider) and your Identity Provider — Okta, Azure Active Directory, OneLogin, or any SAML 2.0-compliant IdP
  • Service Provider metadata downloaded and uploaded to your IdP so it trusts assertion requests from Lifestream Vault
  • A tested SSO login flow where users click one button and land inside their vault with no separate password
  • SCIM 2.0 automated provisioning so that creating or deactivating a user in your IdP automatically mirrors that state in Lifestream Vault
  • A clear understanding of the user lifecycle — how accounts are created, updated, and suspended through SCIM

This setup is entirely API-driven. You can automate every step with the SDK or CLI, making it easy to reproduce across staging and production environments.

Plan Required

SAML SSO and SCIM provisioning require a Business plan. Upgrade in Settings → Subscription. Standard email/password login and MFA (TOTP, passkeys) are available on all tiers.

Prerequisites

Before starting, make sure you have the following in place:

Identity Provider (IdP)

You need admin access to one of the following (or any SAML 2.0-compliant IdP):

Identity ProviderNotes
OktaUse the "SAML 2.0" app template; SCIM is natively supported
Azure Active DirectoryCreate an Enterprise Application with SAML login; SCIM uses Azure AD provisioning
OneLoginUse a custom SAML connector; SCIM requires a OneLogin provisioning profile
Google WorkspaceCustom SAML app in Admin Console; SCIM support varies
Any other SAML 2.0 IdPMust support HTTP-Redirect binding for login and HTTP-POST for assertion

Lifestream Vault

  • A Business tier account
  • Admin role in Lifestream Vault (the account that runs POST /api/v1/admin/sso-configs must be an admin)
  • The SDK installed: npm install @lifestreamdynamics/vault-sdk
  • Or the CLI installed: npm install -g @lifestreamdynamics/vault-cli

Networking

  • Your IdP must be able to POST SAML assertions to https://vault.lifestreamdynamics.com/api/v1/auth/saml/:slug/callback
  • Lifestream Vault must be able to redirect users to your IdP's SSO URL

Clock skew: SAML assertions include NotBefore and NotOnOrAfter timestamps. Lifestream Vault allows up to 5 minutes of clock skew. Ensure your IdP server time is synchronised with NTP. Clock skew is one of the most common causes of assertion validation failures.

Configure the SAML Identity Provider

The first step is to create an SSO configuration in Lifestream Vault. This stores the IdP connection details and returns the information you need to configure the other side (your IdP).

SSO config fields:

FieldDescription
domainYour organisation's email domain (e.g. acme.com). Users with this email domain are redirected to SSO at login.
slugA URL-safe identifier used in SAML endpoint paths (e.g. acme). Must be unique across all organisations.
entityIdThe IdP's Entity ID (also called Issuer) — a URI that identifies your IdP. Found in your IdP's SAML metadata.
ssoUrlThe IdP's SSO endpoint URL where Lifestream Vault sends the authentication request (HTTP-Redirect binding).
certificateThe IdP's X.509 public certificate in PEM format (without the -----BEGIN CERTIFICATE----- headers, or with — both are accepted). Used to verify assertion signatures.

Obtain entityId, ssoUrl, and certificate from your IdP's SAML metadata XML or its admin console.

typescript
import { LifestreamVaultClient } from '@lifestreamdynamics/vault-sdk';
import { readFileSync } from 'fs';

// Admin credentials required
const { client } = await LifestreamVaultClient.login(
  'https://vault.lifestreamdynamics.com',
  'admin@acme.com',
  'admin-password',
);

// Read the IdP certificate from a local PEM file
const certificate = readFileSync('./idp-certificate.pem', 'utf8');

const ssoConfig = await client.saml.createConfig({
  domain: 'acme.com',
  slug: 'acme',
  entityId: 'https://idp.acme.com/saml2/entity',
  ssoUrl: 'https://idp.acme.com/saml2/sso',
  certificate,
});

console.log('SSO config created:', ssoConfig.id);
console.log('ACS URL (give this to your IdP):',
  `https://vault.lifestreamdynamics.com/api/v1/auth/saml/${ssoConfig.slug}/callback`);
console.log('SP Entity ID:',
  `https://vault.lifestreamdynamics.com/api/v1/auth/saml/${ssoConfig.slug}/metadata`);

In your IdP, you will need to set:

  • ACS URL (Assertion Consumer Service): https://vault.lifestreamdynamics.com/api/v1/auth/saml/{slug}/callback
  • SP Entity ID (Audience): https://vault.lifestreamdynamics.com/api/v1/auth/saml/{slug}/metadata
  • NameID format: urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
  • Attribute mapping: send the user's email as email and display name as displayName

Replace {slug} with the slug you chose (e.g. acme).

Download SP Metadata

Most identity providers allow you to configure a SAML integration by uploading Service Provider (SP) metadata XML rather than entering each field manually. Lifestream Vault generates this metadata automatically for each SSO configuration.

The SP metadata is available at a public URL — no authentication required, because it only contains the SP's public certificate and endpoints, not any secrets.

Metadata URL format:

https://vault.lifestreamdynamics.com/api/v1/auth/saml/{slug}/metadata

For example, if your slug is acme:

https://vault.lifestreamdynamics.com/api/v1/auth/saml/acme/metadata

In your IdP admin console:

  • Okta: in your custom SAML app → Sign On tab → paste the metadata URL into the "Import metadata" field
  • Azure AD: in your Enterprise App → Single sign-onUpload metadata file
  • OneLogin: in your custom SAML connector → Configuration → paste the metadata URL
typescript
// Download the SP metadata XML as a string
const metadata = await client.saml.getMetadata('acme');

// Write it to a file to upload to your IdP
import { writeFileSync } from 'fs';
writeFileSync('./sp-metadata.xml', metadata);
console.log('Metadata saved to sp-metadata.xml');

// Or just log the metadata URL for your IdP to fetch directly
console.log('Metadata URL:',
  'https://vault.lifestreamdynamics.com/api/v1/auth/saml/acme/metadata');

The SP metadata XML is generated dynamically from the current SSO configuration. If you update the SSO config (e.g. rotate the SP certificate), re-download the metadata and re-upload it to your IdP. Some IdPs support a metadata URL that they poll periodically — using the URL instead of a static file means your IdP stays in sync automatically.

Test SSO Login

Before rolling SSO out to your whole organisation, test it with a single pilot user.

The SSO login flow works as follows:

1. User visits: https://vault.lifestreamdynamics.com/login
2. User enters their email (e.g. alice@acme.com)
   → Domain matches the "acme.com" SSO config
   → Lifestream Vault generates a SAML AuthnRequest
3. Browser is redirected to the IdP SSO URL (HTTP-Redirect binding)
4. IdP authenticates the user (password, MFA, etc. — IdP-managed)
5. IdP POSTs a signed SAML Assertion to the ACS URL:
   https://vault.lifestreamdynamics.com/api/v1/auth/saml/acme/callback
6. Lifestream Vault validates the assertion:
   - Checks signature against the stored certificate
   - Verifies audience (SP Entity ID) and destination (ACS URL)
   - Checks NotBefore / NotOnOrAfter window (±5 min clock skew)
   - Verifies assertion ID has not been replayed (Redis-backed, 5-min TTL)
7. If valid, the user is signed in and receives an access token
   → New accounts are created automatically on first login
   → Existing accounts are matched by email

To test, open an incognito window (to avoid session interference) and go to the login page. Enter a pilot user's email address — the domain check should trigger the SSO redirect.

typescript
import { LifestreamVaultClient } from '@lifestreamdynamics/vault-sdk';

const { client } = await LifestreamVaultClient.login(
  'https://vault.lifestreamdynamics.com',
  'admin@acme.com',
  'admin-password',
);

// Retrieve the SSO config to confirm it was saved correctly
const configs = await client.saml.listConfigs();
const acmeConfig = configs.find((c) => c.slug === 'acme');

if (acmeConfig) {
  console.log('Domain:', acmeConfig.domain);     // acme.com
  console.log('SSO URL:', acmeConfig.ssoUrl);    // https://idp.acme.com/saml2/sso
  console.log('Entity ID:', acmeConfig.entityId); // https://idp.acme.com/saml2/entity
  console.log('Has certificate:', acmeConfig.certificate.length > 0);
} else {
  console.error('SSO config not found!');
}

Assertion replay prevention: Lifestream Vault stores every processed SAML assertion ID in Redis for 5 minutes (matching the NotOnOrAfter window). If the same assertion ID arrives twice, the second request is rejected with a 401. This prevents replay attacks where a captured assertion is reused to impersonate a user.

Set Up SCIM Provisioning

SCIM 2.0 (System for Cross-domain Identity Management) automates user provisioning. When you onboard a new employee in your IdP, SCIM creates their Lifestream Vault account automatically. When someone leaves and is deactivated in the IdP, SCIM suspends their access immediately — no manual cleanup required.

SCIM base URL:

https://vault.lifestreamdynamics.com/api/v1/scim/v2

Authentication: all SCIM requests use a Bearer token that you generate in Lifestream Vault and paste into your IdP's provisioning configuration. The token has no expiry by default but can be rotated at any time.

Step 1 — generate a SCIM token:

bash
# The SCIM Bearer token is a static server-side environment variable.
# Set it in your API environment before starting the server:

# Generate a cryptographically secure random token (64+ characters recommended)
openssl rand -hex 64

# Add it to your .env or process manager config:
# SCIM_TOKEN=<the value from the command above>

# Then restart the API server
pm2 restart lsvault-api

# Your IdP should now send requests with:
# Authorization: Bearer <SCIM_TOKEN value>

Step 2 — configure SCIM in your IdP:

IdPWhere to configure
OktaApp → ProvisioningIntegration → paste SCIM URL + token
Azure ADEnterprise App → Provisioning → Tenant URL + Secret Token
OneLoginApp → Provisioning → enable, paste token

Use these settings in your IdP:

  • SCIM base URL: https://vault.lifestreamdynamics.com/api/v1/scim/v2
  • Authentication type: Bearer token
  • Token: the value you generated above
  • Supported features: Push Users, Push Groups (if your IdP offers a test connection, run it now)

Rotate the SCIM token immediately if it is ever exposed. Generating a new token invalidates the previous one — update your IdP's provisioning configuration with the new token within a few minutes to avoid a provisioning outage.

SCIM Endpoints Reference

Lifestream Vault implements the SCIM 2.0 core schema for Users and Groups. All endpoints are under the base URL /api/v1/scim/v2 and require a Bearer <scimToken> header.

User endpoints:

MethodPathDescription
GET/UsersList users (supports filter, startIndex, count)
GET/Users/:idGet a single user by SCIM ID
POST/UsersCreate a new user
PUT/Users/:idReplace user attributes (full update)
PATCH/Users/:idPartial update (e.g. deactivate: active: false)
DELETE/Users/:idHard-delete a user (irreversible)

Group endpoints:

MethodPathDescription
GET/GroupsList groups
GET/Groups/:idGet a single group
POST/GroupsCreate a group
PUT/Groups/:idReplace group
PATCH/Groups/:idUpdate group membership
DELETE/Groups/:idDelete a group

Discovery endpoints (no auth required):

MethodPathDescription
GET/ServiceProviderConfigAdvertises supported SCIM features
GET/SchemasLists all supported SCIM schemas
GET/ResourceTypesLists User and Group resource types

Filter syntax example:

GET /api/v1/scim/v2/Users?filter=userName eq "alice@acme.com"
GET /api/v1/scim/v2/Users?filter=active eq false
GET /api/v1/scim/v2/Users?startIndex=1&count=25

SCIM user attributes map to Lifestream Vault fields as follows:

SCIM attributeVault field
userNameemail
name.formatteddisplayName
activeaccount active/suspended state
emails[primary]email
bash
curl -X POST https://vault.lifestreamdynamics.com/api/v1/scim/v2/Users \
  -H "Authorization: Bearer $SCIM_TOKEN" \
  -H "Content-Type: application/scim+json" \
  -d '{
    "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
    "userName": "alice@acme.com",
    "name": {
      "formatted": "Alice Nguyen"
    },
    "emails": [
      { "value": "alice@acme.com", "primary": true }
    ],
    "active": true
  }'

User Lifecycle Management

Understanding how SCIM events translate to Vault account state is important for compliance and for avoiding accidental data loss.

Lifecycle event mapping:

SCIM eventIdP actionVault result
POST /Users (active: true)New hire added to appAccount created; user can log in via SSO
PATCH /Users/:id (active: false)Employee deactivated / offboardedAccount suspended; all sessions invalidated; login blocked
PUT /Users/:idProfile update (name, email)displayName and email updated on existing account
DELETE /Users/:idUser hard-deleted from IdPAccount permanently deleted including all vaults and documents

Important distinction — deactivate vs. delete:

  • Deactivate (PATCH active: false) suspends access but preserves all data. This is the safe offboarding path and what most IdPs use by default.
  • Delete (DELETE /Users/:id) permanently removes the account, all vaults, and all documents. This is irreversible. Lifestream Vault queues an async deletion job via BullMQ that processes the full account removal including cloud storage cleanup.

Recommendation: configure your IdP to send PATCH deactivations rather than DELETEs. Run a manual cleanup pass after a retention period if permanent deletion is required.

typescript
import { LifestreamVaultClient } from '@lifestreamdynamics/vault-sdk';

// Create a client with scimToken to enable the SCIM resource
const client = new LifestreamVaultClient({
  baseUrl: 'https://vault.lifestreamdynamics.com',
  apiKey: process.env.VAULT_API_KEY!,
  scimToken: process.env.SCIM_TOKEN!,
});

// Find a user by email via SCIM filter
const result = await client.scim?.listUsers({
  filter: 'userName eq "bob@acme.com"',
});

if (!result || result.totalResults === 0) {
  console.log('User not found');
  process.exit(1);
}

const scimUser = result.Resources[0];
console.log('Found user:', scimUser.id, '— active:', scimUser.active);

// Suspend the user (soft deactivation — data preserved)
// updateUser performs a full PUT replacement; set active: false to suspend
await client.scim?.updateUser(scimUser.id, {
  ...scimUser,
  active: false,
});
console.log('User suspended. All active sessions have been invalidated.');

// If needed, reactivate later
await client.scim?.updateUser(scimUser.id, {
  ...scimUser,
  active: true,
});
console.log('User reactivated.');

Provisioning on first SSO login: if a user logs in via SSO but was not pre-provisioned by SCIM (e.g. SCIM is not configured yet), Lifestream Vault automatically creates their account using the email and display name from the SAML assertion. Once SCIM is active, those auto-created accounts are matched and managed by SCIM going forward — you do not need to manually link them.

Troubleshooting

Most SSO problems fall into a small number of categories. Use this section to diagnose issues before contacting support.

Assertion validation errors:

ErrorLikely causeFix
Audience mismatchIdP Entity ID doesn't match the stored entityIdUpdate the SSO config with the correct entityId from your IdP
Destination mismatchIdP is posting to the wrong ACS URLSet ACS URL in your IdP to exactly: /api/v1/auth/saml/{slug}/callback
Signature verification failedStored certificate doesn't match the signing keyRe-download the IdP certificate and update the SSO config
NotOnOrAfter expiredAssertion is older than 5 minutesCheck IdP server clock — enable NTP sync
NotBefore in the futureLifestream Vault's clock is behind the IdPCheck Vault server clock — usually a container time drift issue
Assertion ID replayedSame assertion received twice within 5 minutesPossible network retry loop — check IdP retry settings
NameID not foundAssertion is missing the email attributeEnsure IdP sends NameID in emailAddress format

SCIM provisioning errors:

ErrorLikely causeFix
401 UnauthorizedWrong or expired SCIM tokenGenerate a new token; update IdP provisioning config
409 Conflict on user createUser already exists with that emailExisting accounts are matched by email — this is expected on first sync
400 on PATCHInvalid PatchOp schemaEnsure schemas field includes the PatchOp schema URN
Provisioning stops silentlyNetwork timeout or IdP retry exhaustedCheck IdP provisioning logs; ensure SCIM URL is reachable

Debugging tools:

# Retrieve all SSO configs to verify stored values (and find a config ID)
lsvault saml list-configs

# Update a specific field without recreating the whole config
# (update-config takes the config ID as a positional argument)
lsvault saml update-config <CONFIG_ID> --certificate ./new-cert.pem

# Verify the SCIM endpoint is reachable (SCIM is enabled via the SCIM_TOKEN env var)
lsvault scim service-config

IdP certificate expiry: most IdP certificates are valid for 3–10 years, but some organisations rotate them annually or on a schedule. Set a calendar reminder to check your IdP's certificate expiry date. When the certificate rotates, you must update the Lifestream Vault SSO config with the new certificate before the old one expires, or all SSO logins will fail with a signature verification error. Update the certificate with lsvault saml update-config <CONFIG_ID> --certificate ./new-cert.pem or via the admin UI at Settings → SSO → Edit Config.

What's Next

Your organisation now has a fully automated identity pipeline — users are created, updated, and deactivated in Lifestream Vault automatically as their IdP state changes, and they sign in with a single click through SSO. Here are some natural next steps:

  • Secure Your Account with MFA — combine SSO with TOTP or passkeys for users who need an extra layer of assurance
  • Set Up a Team Knowledge Base — set up shared vaults that your SCIM-provisioned users can collaborate on immediately after their accounts are created
  • SSO and SCIM provisioning are managed in Settings → SSO and Settings → SCIM.
  • SCIM 2.0 RFC 7644 — the full SCIM protocol specification if you need to build a custom provisioning integration