Skip to main content

Single Sign-On (SSO) & SCIM Provisioning

Contract Lucidity supports enterprise Single Sign-On via OpenID Connect (OIDC) and automated user/group provisioning via SCIM 2.0. This lets your users authenticate through your organization's identity provider (Azure AD / Entra ID, Google Workspace, Okta, etc.) and automatically keeps user accounts and group memberships in sync.

How SSO Works

What the Identity Provider Handles

Contract Lucidity delegates authentication to your IdP. This means:

  • Multi-Factor Authentication (MFA) -- configured in your IdP, not in CL
  • Conditional access policies -- device compliance, location restrictions, etc.
  • Password policies -- complexity, rotation, lockout
  • Session management -- the IdP controls the authentication experience

CL only receives the claims from the ID token (email, name, subject ID) after successful authentication.

Prerequisites

Before configuring SSO, ensure:

  1. You have admin access to both Contract Lucidity and your identity provider
  2. Your CL instance is accessible via HTTPS (required for secure OIDC redirects)
  3. You know your CL deployment's public URL (e.g., https://contracts.yourcompany.com)
  4. FRONTEND_URL is set correctly on the backend service (see below)
Critical: FRONTEND_URL must match your public domain

The backend uses the FRONTEND_URL environment variable to construct SSO redirect URIs and post-login callbacks. If this is set incorrectly (e.g., http://localhost:3000 instead of your actual domain), SSO will fail with redirect URI mismatch errors from your identity provider.

Set this on the cl-backend service environment:

FRONTEND_URL=https://contracts.yourcompany.com

This must exactly match the domain users access in their browser, including the scheme (https://). Do not include a trailing slash.

SSO Configuration in Contract Lucidity

Navigate to Settings > Security > SSO in the admin panel. The configuration fields are:

FieldDescriptionExample
Provider NameDisplay name shown on the login buttonAzure AD, Okta, Google
Issuer URLOIDC issuer URL (must serve /.well-known/openid-configuration)See provider sections below
Client IDApplication/client ID from your IdPa1b2c3d4-...
Client SecretClient secret from your IdP (stored encrypted)secret~...
ScopesOIDC scopes to requestopenid email profile (default)
Auto-create usersCreate CL accounts for new SSO users on first logintrue (default)
Default roleRole assigned to auto-created usersuser (default)
Default groupGroup auto-created users are added to (optional)Select from existing groups
EnabledToggle SSO on/offfalse (default)
info

The Client Secret is encrypted at rest using AES (Fernet) derived from your JWT_SECRET_KEY. It is never returned in API responses -- only a redacted version (last 4 characters) is displayed.

Redirect URI

The callback URI is dynamically generated based on your deployment's domain:

https://your-domain.com/api/auth/sso/callback

You must register this exact URI in your identity provider's application configuration. CL uses the request's origin to build the redirect URI, so it works correctly regardless of the domain name.


SCIM 2.0 Provisioning

SCIM (System for Cross-domain Identity Management) allows your identity provider to automatically push user and group changes to Contract Lucidity. Instead of manually creating users or relying solely on just-in-time provisioning at login, SCIM keeps your CL user directory in continuous sync with your IdP.

What SCIM Does

Action in IdPResult in Contract Lucidity
Assign a user to the CL appUser account is created (or linked if email matches an existing account)
Update a user's name or emailUser profile is updated in CL
Unassign / deactivate a userUser is deactivated in CL (soft-delete; data is preserved)
Create / push a groupGroup is created in CL with a description noting it was provisioned via SCIM
Add/remove group membersGroup membership is synced in CL
Delete a groupGroup is removed from CL

How SCIM Authentication Works

SCIM uses a long-lived bearer token for authentication. The token is generated in CL, shown once to the admin, and stored as a bcrypt hash. Each SCIM request from the IdP includes this token in the Authorization: Bearer <token> header.

Setting Up SCIM Provisioning

Step 1: Configure SSO First

SCIM provisioning requires an active SSO configuration. Complete the SSO setup (described above) before enabling SCIM.

Step 2: Generate a SCIM Token

  1. Navigate to Settings > Security > SSO
  2. Scroll to the SCIM Provisioning section
  3. Click Generate SCIM Token
  4. Copy the token immediately -- it is displayed only once and cannot be retrieved later
  5. Store it securely (you will paste it into your IdP's SCIM configuration)
warning

The SCIM token is shown only once. If you lose it, you must revoke the current token and generate a new one, then update the token in your identity provider's configuration.

Step 3: Note the SCIM Endpoint URL

The SCIM base URL for your deployment is:

https://your-backend-domain.com/scim/v2
Important: SCIM Endpoint Routing

The SCIM endpoint is served at /scim/v2 on the backend service, not under the /api/ prefix. The default frontend proxy in Contract Lucidity only forwards /api/* requests to the backend.

This means your identity provider must be able to reach the backend's SCIM endpoint directly. You have two options:

  1. Add a frontend rewrite rule for /scim/* in your next.config.ts (recommended for simplicity -- keeps a single public URL)
  2. Expose the backend directly on a separate URL or port for SCIM traffic only

See the Configuration Reference for details on setting up the SCIM route.

Step 4: Configure Your Identity Provider

Follow the provider-specific instructions below to point your IdP at the CL SCIM endpoint.

Step 5: Test the Integration

After configuring SCIM in your IdP:

  1. Assign a test user to the CL application in your IdP
  2. Verify the user appears in Settings > Users in CL
  3. Try updating the user's name in the IdP and confirm it syncs
  4. Try assigning a group and verify it appears in Settings > Groups

Revoking a SCIM Token

To revoke the current SCIM token:

  1. Navigate to Settings > Security > SSO
  2. In the SCIM Provisioning section, click Revoke Token
  3. The IdP will receive 401 Unauthorized errors on subsequent SCIM requests until a new token is generated and configured

SCIM Capabilities

Contract Lucidity's SCIM implementation supports:

  • Users: Create, read, update (PUT and PATCH), deactivate (soft-delete), list with filtering
  • Groups: Create, read, update (PUT and PATCH including member add/remove), delete, list with filtering
  • Filtering: userName eq "value", externalId eq "value", displayName eq "value"
  • Pagination: Standard SCIM 1-based pagination with startIndex and count parameters (max 200 per page)
  • Discovery: /ServiceProviderConfig, /Schemas, /ResourceTypes endpoints

Not supported: bulk operations, password changes, sorting, ETags.


Provider Setup: Azure AD / Entra ID

note

Cloud providers frequently update their admin interfaces and features. The steps below were verified against Microsoft's documentation as of early 2026. If the steps do not match what you see, consult the official Microsoft documentation linked below.

Official docs: Develop a SCIM endpoint for user provisioning from Microsoft Entra ID | What is automated app user provisioning

SSO Setup (OIDC)

1. Register an Application

  1. Go to the Azure Portal > Microsoft Entra ID > App registrations
  2. Click New registration
  3. Set:
    • Name: Contract Lucidity
    • Supported account types: Choose based on your needs (typically "Accounts in this organizational directory only")
    • Redirect URI: Select Web and enter https://your-domain.com/api/auth/sso/callback
  4. Click Register

2. Get Credentials

  1. From the app registration overview, copy the Application (client) ID
  2. Go to Certificates & secrets > New client secret
  3. Copy the secret Value (not the Secret ID)

3. Find the Issuer URL

The issuer URL for Azure AD is:

https://login.microsoftonline.com/{tenant-id}/v2.0

Find your tenant ID on the Overview page of the app registration (Directory (tenant) ID).

4. Configure in CL

FieldValue
Provider NameAzure AD
Issuer URLhttps://login.microsoftonline.com/{tenant-id}/v2.0
Client IDApplication (client) ID from step 2
Client SecretClient secret value from step 2
Scopesopenid email profile
Azure-specific note: Required optional claims

Azure AD does not include name or email claims in the ID token by default. You must add them manually:

  1. Go to Token configuration > Add optional claim > select ID token type
  2. Add email, given_name, and family_name
  3. If prompted to enable Microsoft Graph permissions, accept

Without email, CL cannot identify the user. Without given_name and family_name, user accounts will be created with a blank name (the email prefix is used as a fallback for first name).

SCIM Provisioning Setup (Azure AD / Entra ID)

Azure Entra ID has built-in SCIM provisioning support for both gallery and non-gallery (custom) applications.

1. Create an Enterprise Application for Provisioning

Since Contract Lucidity is not in the Microsoft app gallery, you create a non-gallery application:

  1. In the Azure Portal, go to Microsoft Entra ID > Enterprise applications
  2. Click New application > Create your own application
  3. Enter a name (e.g., Contract Lucidity - Provisioning) and select Integrate any other application you don't find in the gallery (Non-gallery)
  4. Click Create
info

You will have two separate app registrations in Entra ID: one for SSO (OIDC) and one for provisioning (SCIM). This is normal for non-gallery applications.

2. Configure Provisioning

  1. Open the enterprise application you just created
  2. Go to the Provisioning blade in the left menu
  3. Click Get started, then set Provisioning Mode to Automatic
  4. Under Admin Credentials:
    • Tenant URL: Enter your SCIM endpoint URL: https://your-backend-domain.com/scim/v2
    • Secret Token: Paste the SCIM bearer token you generated in CL
  5. Click Test Connection to verify Entra ID can reach the SCIM endpoint
  6. Click Save

3. Configure Attribute Mappings

After saving the admin credentials:

  1. Expand the Mappings section
  2. Click Provision Microsoft Entra ID Users
  3. Review the default attribute mappings. The key mappings for CL are:
Azure AD AttributeSCIM AttributeNotes
userPrincipalNameuserNameCL uses this as the email address
givenNamename.givenNameFirst name
surnamename.familyNameLast name
mailemails[type eq "work"].valueAlternative email mapping
Switch([IsSoftDeleted]...)activeControls user active status
objectIdexternalIdLinks the Entra ID user to the CL user
  1. Click Provision Microsoft Entra ID Groups to configure group provisioning
  2. Ensure displayName maps to displayName and members maps to members
tip

If your users sign in with their email address (not UPN), change the userName mapping source from userPrincipalName to mail. CL uses userName as the email address for user lookup.

4. Assign Users and Groups

  1. Go to the Users and groups blade of the enterprise application
  2. Click Add user/group
  3. Select the users or groups you want to provision to CL
  4. Click Assign

5. Start Provisioning

  1. Return to the Provisioning blade
  2. Click Start provisioning
  3. Entra ID will perform an initial sync cycle (which may take several minutes to hours depending on the number of users)
  4. Subsequent incremental syncs run approximately every 40 minutes

Provider Setup: Google Workspace

note

Cloud providers frequently update their admin interfaces and features. The steps below were verified against Google's documentation as of early 2026. If the steps do not match what you see, consult the official Google documentation linked below.

Official docs: About automated user provisioning | Set up your own custom SAML app

SSO Setup (OIDC)

1. Create OAuth Credentials

  1. Go to the Google Cloud Console > APIs & Services > Credentials
  2. Click Create Credentials > OAuth client ID
  3. Set:
    • Application type: Web application
    • Name: Contract Lucidity
    • Authorized redirect URIs: https://your-domain.com/api/auth/sso/callback
  4. Click Create

2. Get Credentials

Copy the Client ID and Client secret from the confirmation dialog.

3. Configure in CL

FieldValue
Provider NameGoogle
Issuer URLhttps://accounts.google.com
Client IDClient ID from step 2
Client SecretClient secret from step 2
Scopesopenid email profile
tip

To restrict login to your Google Workspace domain, configure the allowed domains in Google Admin Console under Security > API Controls. CL does not filter by domain -- the IdP must enforce this.

SCIM Provisioning (Google Workspace)

Google Workspace limitation

Google Workspace's automated provisioning only supports applications in its pre-integrated app catalog. It does not support pushing users via SCIM to arbitrary custom applications like Contract Lucidity.

Workarounds:

  • Use SSO with auto-create: Enable "Auto-create users" in CL's SSO settings. Users will be created in CL on their first SSO login (just-in-time provisioning).
  • Use a third-party identity governance tool: Tools like Okta, Microsoft Entra ID, or dedicated SCIM bridges can sit between Google Workspace and CL to provide SCIM provisioning.
  • Manual provisioning: Create user accounts manually in CL or use the CL admin API.

If your organization uses Google Workspace as its primary IdP, just-in-time provisioning via SSO (with auto-create enabled) is the recommended approach. Users are created automatically on first login, and deactivation can be managed through CL's admin UI.


Provider Setup: Okta

note

Cloud providers frequently update their admin interfaces and features. The steps below were verified against Okta's documentation as of early 2026. If the steps do not match what you see, consult the official Okta documentation linked below.

Official docs: Add a private SCIM integration | Add SCIM provisioning to app integrations | Understanding SCIM

SSO Setup (OIDC)

1. Create an Application

  1. Go to your Okta admin console > Applications > Create App Integration
  2. Select OIDC - OpenID Connect and Web Application
  3. Set:
    • App integration name: Contract Lucidity
    • Sign-in redirect URIs: https://your-domain.com/api/auth/sso/callback
    • Sign-out redirect URIs: (optional, leave blank)
    • Assignments: Choose who can access the app
  4. Click Save

2. Get Credentials

From the application's General tab, copy the Client ID and Client secret.

3. Find the Issuer URL

The issuer URL for Okta is:

https://{your-okta-domain}/oauth2/default

Or if you use a custom authorization server:

https://{your-okta-domain}/oauth2/{authorization-server-id}

4. Configure in CL

FieldValue
Provider NameOkta
Issuer URLhttps://{your-okta-domain}/oauth2/default
Client IDClient ID from step 2
Client SecretClient secret from step 2
Scopesopenid email profile

SCIM Provisioning Setup (Okta)

Okta supports SCIM provisioning for custom applications through its App Integration Wizard.

1. Enable SCIM on the Application

You can either add SCIM to your existing OIDC app or create a separate SCIM app:

Option A: Use the App Catalog SCIM template (recommended)

  1. Go to Applications > Browse App Catalog
  2. Search for SCIM 2.0 Test App (Header Auth) and click Add Integration
  3. Give it a name (e.g., Contract Lucidity - Provisioning) and click Done

Option B: Add SCIM to your existing OIDC app

  1. Open your Contract Lucidity OIDC application
  2. Go to the General tab and edit the Provisioning section
  3. Select SCIM as the provisioning type

2. Configure API Integration

  1. Click the Provisioning tab
  2. Click Configure API Integration
  3. Check Enable API Integration
  4. Set the fields:
    • SCIM 2.0 Base URL: https://your-backend-domain.com/scim/v2
    • Unique identifier field for users: userName
    • Supported provisioning actions: Check all that apply:
      • Push New Users
      • Push Profile Updates
      • Push Groups
    • Authentication Mode: Select HTTP Header
    • Authorization / Bearer Token: Paste the SCIM token generated in CL
  5. Click Test API Credentials to verify connectivity
  6. Click Save

3. Configure Provisioning to App

  1. On the Provisioning tab, click To App in the left panel
  2. Click Edit and enable:
    • Create Users: Checked
    • Update User Attributes: Checked
    • Deactivate Users: Checked
  3. Click Save

4. Configure Attribute Mappings

  1. Still on the Provisioning tab, scroll to the attribute mappings
  2. Verify that these mappings are configured:
Okta AttributeSCIM Attribute
userNameuserName
givenNamename.givenName
familyNamename.familyName
emailemails[type eq "work"].value
externalIdexternalId
  1. CL requires userName to be the user's email address. If your Okta userName is not an email, map email to userName instead.

5. Assign Users and Groups

  1. Go to the Assignments tab
  2. Assign individual users or groups to the application
  3. If pushing groups, go to the Push Groups tab and configure which Okta groups should be pushed to CL

Auto-Create Users

When Auto-create users is enabled (the default), Contract Lucidity will automatically create a new user account the first time someone logs in via SSO. The new account is configured with:

  • Email: From the email claim in the ID token
  • Name: From the given_name and family_name claims
  • Role: The configured Default role (typically user)
  • Group: Added to the configured Default group (if set)
  • Active: true
  • must_change_password: false (SSO users do not have passwords)

If auto-create is disabled, only users with pre-existing CL accounts (matching by email or SSO subject ID) can log in via SSO.

Pre-provisioning users

You can pre-create user accounts in CL with matching email addresses before enabling SSO. When those users log in via SSO for the first time, their existing account is linked to the SSO identity rather than creating a duplicate. The same linking behavior applies to SCIM-provisioned users -- if a SCIM push creates a user with an email that already exists in CL, the existing account is linked to the SSO identity.

SSO and Local Auth Coexistence

SSO and local password authentication run side-by-side:

  • The login page shows a "Sign in with SSO" button when SSO is enabled
  • Users can still log in with email and password if they have a local account
  • An admin can disable SSO at any time without affecting local accounts
  • SSO users who have been linked from a pre-existing local account retain their password and can use either method
info

CL supports one SSO provider at a time. The sso_config table stores a single configuration record.

Troubleshooting

Redirect URI Mismatch

Symptom: Identity provider returns an error like "redirect_uri does not match", "invalid redirect_uri", or Azure error AADSTS50011.

Cause: The redirect URI sent by CL does not match what is registered in your IdP. CL constructs the redirect URI from the FRONTEND_URL environment variable on the backend.

Fix:

  1. Check FRONTEND_URL on the cl-backend service. This is the most common cause. It must be set to your public domain (e.g., https://contracts.yourcompany.com), not http://localhost:3000 or an internal hostname
  2. Register the exact URI in your IdP: https://your-domain.com/api/auth/sso/callback (where your-domain.com matches FRONTEND_URL)
  3. Ensure there is no trailing slash mismatch
  4. After changing FRONTEND_URL, you must recreate the backend container (not just restart it) for the new env var to take effect: docker compose up -d cl-backend

Token Validation Failed

Symptom: Login redirects back with "ID token validation failed" error.

Possible causes:

  • Clock skew: The server's clock is out of sync. Ensure NTP is configured.
  • Issuer mismatch: The issuer_url in CL settings does not match the iss claim in the token. Check for trailing slashes.
  • Audience mismatch: The client_id in CL settings does not match the aud claim. Verify the client ID is correct.

User Not Found (Auto-Create Disabled)

Symptom: "User not found and automatic account creation is disabled" error.

Fix: Either enable auto-create in SSO settings, or pre-create the user account in CL with a matching email address.

Missing Email Claim

Symptom: "ID token does not contain an email claim" error.

Fix: Configure your IdP to include the email claim in the ID token:

  • Azure AD: Add the email optional claim in Token configuration
  • Google: The email scope includes this by default
  • Okta: Ensure the email scope is granted and the user has an email set

SSO State Expired

Symptom: "Invalid or expired SSO state parameter" error.

Cause: The OIDC state parameter (stored in Redis with a 5-minute TTL) has expired. This happens if the user takes too long to complete authentication at the IdP.

Fix: Have the user try again. If the issue persists, verify Redis is running and accessible.

SCIM Provisioning Not Working

Symptom: Users or groups are not being synced from the IdP to CL.

Possible causes and fixes:

  • 401 Unauthorized: The SCIM token is invalid or has been revoked. Generate a new token in CL and update it in your IdP.
  • Connection timeout: The IdP cannot reach the SCIM endpoint. Verify the SCIM URL is correct and that the backend is accessible from the internet (see the routing note in the SCIM setup section above).
  • SCIM not configured: Ensure SSO is configured first, then generate a SCIM token. The SCIM endpoints return 401 if no SSO config with a SCIM token hash exists.
  • User already exists (409): For groups, a 409 means a group with the same name already exists. Rename the group in CL or in the IdP to resolve the conflict.
  • Attribute mapping issues: Verify that userName in the SCIM payload is the user's email address. CL uses userName as the email field.

SCIM Deactivation vs Deletion

Symptom: Deleting a user in the IdP does not remove them from CL.

Expected behavior: SCIM DELETE /Users performs a soft-delete (deactivates the user) rather than permanently removing the account. This preserves audit trails and document associations. The user can be reactivated later if needed.

API Reference

SSO Admin Endpoints (require admin auth)

MethodEndpointDescription
GET/api/auth/sso/configGet current SSO configuration (secret redacted)
POST/api/auth/sso/configCreate or update SSO configuration
DELETE/api/auth/sso/configDelete SSO configuration
POST/api/auth/sso/scim-tokenGenerate a SCIM bearer token (shown once)
DELETE/api/auth/sso/scim-tokenRevoke the current SCIM token

SSO Public Endpoints (no auth required)

MethodEndpointDescription
GET/api/auth/sso/statusCheck if SSO is configured and enabled
GET/api/auth/sso/loginInitiate SSO login (redirects to IdP)
GET/api/auth/sso/callbackHandle IdP callback (not called directly)

SCIM Endpoints (require SCIM bearer token)

MethodEndpointDescription
GET/scim/v2/ServiceProviderConfigSCIM service provider capabilities
GET/scim/v2/SchemasSupported SCIM schemas
GET/scim/v2/ResourceTypesSupported resource types
GET/scim/v2/UsersList users (supports filtering and pagination)
POST/scim/v2/UsersCreate or link a user
GET/scim/v2/Users/:idGet a specific user
PUT/scim/v2/Users/:idReplace a user
PATCH/scim/v2/Users/:idPartially update a user
DELETE/scim/v2/Users/:idDeactivate a user (soft-delete)
GET/scim/v2/GroupsList groups (supports filtering and pagination)
POST/scim/v2/GroupsCreate a group
GET/scim/v2/Groups/:idGet a specific group
PUT/scim/v2/Groups/:idReplace a group (full member replacement)
PATCH/scim/v2/Groups/:idAdd/remove members or update group name
DELETE/scim/v2/Groups/:idDelete a group