Manage users with SCIM

System for Cross-domain Identity Management (SCIM) allows automating user lifecycle management across cloud apps: creating and activating a new user, updating their user attributes (e.g., email and password), and deactivating the user. For example, it allows the app’s IT system or identity provider to provision and manage users in Mosaic. Or allows integrating the app's services with Mosaic as the IDP to check if the user has been updated or deactivated.

Note

Both Users-SCIM APIs and Users APIs are operating on the same user entities in the Mosaic identity store.

Depending on your use case, you can implement various user lifecycle management chores with the Users-SCIM APIs, e.g.:

How it works

As a REST-based protocol, SCIM describes resources as JSON objects with a standardized schema which makes it easy to provision and manage resources across multiple cloud apps. In other words, with the same object you can run queries against various independent platforms.

Mosaic exposes APIs that allow performing CRUD operations with SCIM-compatible user objects. Any user in the Mosaic identity store can be presented using the SCIM-compatible schema. Mosaic uses abbreviated SCIM User Core schema (urn:ietf:params:scim:schemas:core:2.0:User) and allows extending it with custom data using urn:ietf:params:scim:schemas:extension:CustomAppData:2.0:User schema.

The Users-SCIM calls are authenticated using the Bearer token generated based on the client credentials (see Get client access tokens). You can only manage the users for the application used to create an access token. If an admin access token is used, you can retrieve all users in your tenant.

User identifiers

Depending on your integration use case, you can leverage the following attributes as user identifiers when managing and authenticating users with Mosaic. The table below provides a mapping between SCIM attributes and Mosaic user identifiers.

SCIM field Mosaic user identifiers Description
userName username Required attribute since Mosaic enables password authentication for the users created via SCIM APIs by default.
emails.value
emails.primary: true
email The primary email address of the user that can be used instead of their username for authentication.
phoneNumbers.value
phoneNumbers.primary: true
phone_number The primary phone number of the user that can be used for authentication.
externalId external_user_id The attribute set outside Mosaic, it corresponds to ID in your system and allows syncing data. In WebAuthn flows can be used to register new credentials when the user is logged out.
id user_id The ID is generated by Mosaic and returned upon user creation.

Create a user

Provision new users by sending a POST request to /scim/users endpoint with user details. For the object structure, refer to the API reference. The minimum set of attributes to create a new user includes schemas, userName, and password. Setting a password and a username makes a user eligible for password authentication.

Note

The user you create will be available in the Admin Portal and through Mosaic Users APIs too.

RequestResponse
Copy
Copied
import fetch from 'node-fetch';

async function run() {
  const resp = await fetch(
    `https://api.transmitsecurity.io/cis/scim/Users`,
    {
      method: 'POST',
      headers: {
        Accept: 'application/scim+json',
        'Content-Type': 'application/scim+json',
        Authorization: 'Bearer [CLIENT_ACCESS_TOKEN]'
      },
      body: JSON.stringify({
        name: { // User's name
          givenName: 'John',
          familyName: 'Smith',
        },
        emails: [
          { // Unique. User's primary email address. Can be used for auth
            value: 'john.smith@example.com',
            primary: true
          }
        ],
        phoneNumbers: [
          { // Unique. User's primary phone number. Can be used for auth
            value: '+12125555555',
            primary: true
          }
        ],
        schemas: ['urn:ietf:params:scim:schemas:core:2.0:User'], // Required
        externalId: '1234567ABCDE', // Unique ID, corresponds to external system. Can be used to register new credentials in WebAuthn if the user is logged out
        userName: 'user.johnsmith', // Unique username. Required for password auth that enabled by default when creating a user with SCIM
        password: '123456' // Required for password auth that enabled by default when creating a user with SCIM
      })
    }
  );

  const data = await resp.json();
  console.log(data);
}

run();
Copy
Copied
{
    "schemas": "urn:ietf:params:scim:schemas:core:2.0:User",
    "id": "1234abcd5678efgh9000a", // Unique ID generated by Transmit
    "externalId": "1234567",
    "userName": "JohnSmith",
    "name":
    {
        "givenName": "John",
        "familyName": "Smith"
    },
    "displayName": "John Smith",
    "active": true,
    "phoneNumbers": [
        {
            "value": '+12125555555',
            "primary": true
        }
    ],
    "emails": [
        {
            "value": 'john.smith@example.com',
            "primary": true
        }
    ],
    "meta":
    {
        "created": "2023-11-06T14:49:44.054Z",
        "lastModified": "2023-11-06T14:49:44.054Z",
        "location": "https://api.transmitsecurity.io/scim/Users/1234abcd5678efgh9000a",
        "resourceType": "User"
    },
    "urn:ietf:params:scim:schemas:extension:UserExtended:2.0:User":
    {
        "status": "Active",
        "lastAuth": "2024-11-06T14:49:44.054Z",
        "passwordInformation":
            {
                "expired": false,
                "temporary": false,
                "updated_at": "2023-11-06T14:49:44.054Z"
            }
    }
}

Enrich user data

Mosaic allows extending the user object with custom data. For example, add app-related user information, such as a user's department or nearest branch location.

To extend the schema with custom data, include urn:ietf:params:scim:schemas:extension:CustomAppData:2.0:User entry in the schemas array in the POST or PUT call, and then specify custom attributes within the urn:ietf:params:scim:schemas:extension:CustomAppData:2.0:User object itself. The object is untyped, you can provide as many custom attributes as needed; nesting is supported.

Note

Correponds to user's custom_app_data managed through Users APIs.

Copy
Copied
import fetch from 'node-fetch';

async function run() {
  const resp = await fetch(
    `https://api.transmitsecurity.io/cis/scim/Users`,
    {
      method: 'POST',
      headers: {
        Accept: 'application/scim+json',
        'Content-Type': 'application/scim+json',
        Authorization: 'Bearer [CLIENT_ACCESS_TOKEN]'
      },
      body: JSON.stringify({
        schemas: // Required
          [
            'urn:ietf:params:scim:schemas:core:2.0:User',
            'urn:ietf:params:scim:schemas:extension:CustomAppData:2.0:User'
          ],
        'urn:ietf:params:scim:schemas:extension:CustomAppData:2.0:User': { // Extends user data
            'nearestBranch': 'New York, Manhattan' // Custom data
        },
        userName: 'AnnaGreen', // Required
        password: '123456' // Required
      })
    }
  );

  const data = await resp.json();
  console.log(data);
}

run();

List all users

Retrieve all users by running a GET request to /scim/users endpoint or POST to /scim/users/.search. The endpoints support paging, sorting, and filtering, as well as enable you to specify which attributes to return. The examples below return 100 records per page, starting with the first record and each record includes only four attributes. The response will include a list of users with requested attributes, and metadata such as the number of records returned and SCIM schema.

Note

The call retrieves users associated with the application used to create an access token. To retrieve all users in your tenant, use the admin access token generated using management app credentials.

GETPOST
Copy
Copied
import fetch from 'node-fetch';

async function run() {
  const query = new URLSearchParams({
    startIndex: '1', // 1-based index. Controls paging. The serial number of the element to start fetching
    count: '100', // Controls paging. The number of records to return per page. If set to 0 or undefined, no records will be returned
    attributes: ['userName', 'id', 'name.familyName', 'emails'], // The attributes to return
  }).toString();

  const resp = await fetch(
    `https://api.transmitsecurity.io/cis/scim/Users?${query}`,
    {
      method: 'GET',
      headers: {
        Accept: 'application/scim+json',
        Authorization: 'Bearer [CLIENT_ACCESS_TOKEN]'
      }
    }
  );

  const data = await resp.text();
  console.log(data);
}

run();
Copy
Copied
import fetch from 'node-fetch';

async function run() {
  const resp = await fetch(
    `https://api.transmitsecurity.io/cis/scim/Users/.search`,
    {
      method: 'POST',
      headers: {
        Accept: 'application/scim+json',
        'Content-Type': 'application/scim+json',
        Authorization: 'Bearer [CLIENT_ACCESS_TOKEN]'
      },
      body: JSON.stringify({
        startIndex: 1, // 1-based index. Controls paging. The serial number of the element to start fetching
        count: '100', // Controls paging. The number of records to return per page. If set to 0 or undefined, no records will be returned
        attributes: ['userName', 'id', 'name.givenName', 'emails'], // The attributes to return
        schemas: [
            "urn:ietf:params:scim:api:messages:2.0:SearchRequest"
        ]
      })
    }
  );

  const data = await resp.json();
  console.log(data);
}

run();

Search for users

Filters allow searching for users matching your search criteria as well as locating a single user record based on the unique identifier. Below are basic recipes demonstrating how to find users. For details on how filtering works, see Search query syntax.

Find a user

Find a user based on one of the unique user identifiers such as username, ID, email, phone number, or external ID.

Find user by username

Locate a user by their unique userName. To do it, send a GET request to /scim/users endpoint or POST to /scim/users/.search.

Note

The userName might be missing for users created with Users APIs.

GETPOST
Copy
Copied
import fetch from 'node-fetch';

async function run() {
  const query = new URLSearchParams({
    startIndex: '1', // 1-based index
    count: '100', // If set to 0 or undefined, no records will be returned
    filter: 'userName eq \"JohnSmith\"' // Filter expression allows locating a user with a specific username
  }).toString();

  const resp = await fetch(
    `https://api.transmitsecurity.io/cis/scim/Users?${query}`,
    {
      method: 'GET',
      headers: {
        Accept: 'application/scim+json',
        Authorization: 'Bearer [CLIENT_ACCESS_TOKEN]'
      }
    }
  );

  const data = await resp.text();
  console.log(data);
}

run();
Copy
Copied
import fetch from 'node-fetch';

async function run() {
  const resp = await fetch(
    `https://api.transmitsecurity.io/cis/scim/Users/.search`,
    {
      method: 'POST',
      headers: {
        Accept: 'application/scim+json',
        'Content-Type': 'application/scim+json',
        Authorization: 'Bearer [CLIENT_ACCESS_TOKEN]'
      },
      body: JSON.stringify({
        startIndex: 1, // 1-based index
        count: '100', // If set to 0 or undefined, no records will be returned
        filter: 'userName eq \"JohnSmith\"' // Filter expression allows locating a user with a specific username
        schemas: [
            "urn:ietf:params:scim:api:messages:2.0:SearchRequest"
        ]
      })
    }
  );

  const data = await resp.json();
  console.log(data);
}

run();
Find user by ID

Locate a user by their unique ID. To do this, send a GET request to /scim/users/{user_id} endpoint with the ID of the user. Alternatively, send a POST to /scim/users/.search with a user's ID.

GETPOST
Copy
Copied
import fetch from 'node-fetch';

async function run() {
  const userId = '[ID]'; // Unique ID of the user
  const resp = await fetch(
    `https://api.transmitsecurity.io/cis/scim/Users/${userId}`,
    {
      method: 'GET',
      headers: {
        Accept: 'application/scim+json',
        Authorization: 'Bearer [CLIENT_ACCESS_TOKEN]'
      }
    }
  );

  const data = await resp.text();
  console.log(data);
}

run();
Copy
Copied
import fetch from 'node-fetch';

async function run() {
  const resp = await fetch(
    `https://api.transmitsecurity.io/cis/scim/Users/.search`,
    {
      method: 'POST',
      headers: {
        Accept: 'application/scim+json',
        'Content-Type': 'application/scim+json',
        Authorization: 'Bearer [CLIENT_ACCESS_TOKEN]'
      },
      body: JSON.stringify({
        startIndex: 1, // 1-based index
        count: '100', // If set to 0 or undefined, no records will be returned
        filter: 'id eq \"[ID]\"' // Filter expression allows locating a user by ID
        schemas: [
            "urn:ietf:params:scim:api:messages:2.0:SearchRequest"
        ]
      })
    }
  );

  const data = await resp.json();
  console.log(data);
}

run();
Find user by email

Locate a user by their email by running a GET request to /scim/users endpoint or POST to /scim/users/.search.

GETPOST
Copy
Copied
import fetch from 'node-fetch';

async function run() {
  const query = new URLSearchParams({
    startIndex: '1', // 1-based index
    count: '100', // If set to 0 or undefined, no records will be returned
    filter: 'emails eq \"john.smith@example.com\"' // Filter expression allows locating a user with a specific email
  }).toString();

  const resp = await fetch(
    `https://api.transmitsecurity.io/cis/scim/Users?${query}`,
    {
      method: 'GET',
      headers: {
        Accept: 'application/scim+json',
        Authorization: 'Bearer [CLIENT_ACCESS_TOKEN]'
      }
    }
  );

  const data = await resp.text();
  console.log(data);
}

run();
Copy
Copied
import fetch from 'node-fetch';

async function run() {
  const resp = await fetch(
    `https://api.transmitsecurity.io/cis/scim/Users/.search`,
    {
      method: 'POST',
      headers: {
        Accept: 'application/scim+json',
        'Content-Type': 'application/scim+json',
        Authorization: 'Bearer [CLIENT_ACCESS_TOKEN]'
      },
      body: JSON.stringify({
        startIndex: 1, // 1-based index
        count: '100', // If set to 0 or undefined, no records will be returned
        filter: 'emails eq \"john.smith@example.com\"' // Filter expression allows locating a user with a specific email
        schemas: [
            "urn:ietf:params:scim:api:messages:2.0:SearchRequest"
        ]
      })
    }
  );

  const data = await resp.json();
  console.log(data);
}

run();
Find user by phone number

Locate a user by their email by running a GET request to /scim/users endpoint or POST to /scim/users/.search.

GETPOST
Copy
Copied
import fetch from 'node-fetch';

async function run() {
  const query = new URLSearchParams({
    startIndex: '1', // 1-based index
    count: '100', // If set to 0 or undefined, no records will be returned
    filter: 'phoneNumbers eq \"+12125555555\"' // Filter expression allows locating a user with a specific phone number
  }).toString();

  const resp = await fetch(
    `https://api.transmitsecurity.io/cis/scim/Users?${query}`,
    {
      method: 'GET',
      headers: {
        Accept: 'application/scim+json',
        Authorization: 'Bearer [CLIENT_ACCESS_TOKEN]'
      }
    }
  );

  const data = await resp.text();
  console.log(data);
}

run();
Copy
Copied
import fetch from 'node-fetch';

async function run() {
  const resp = await fetch(
    `https://api.transmitsecurity.io/cis/scim/Users/.search`,
    {
      method: 'POST',
      headers: {
        Accept: 'application/scim+json',
        'Content-Type': 'application/scim+json',
        Authorization: 'Bearer [CLIENT_ACCESS_TOKEN]'
      },
      body: JSON.stringify({
        startIndex: 1, // 1-based index
        count: '100', // If set to 0 or undefined, no records will be returned
        filter: 'phoneNumbers eq \"+12125555555\"' // Filter expression allows locating a user with a specific phone number
        schemas: [
            "urn:ietf:params:scim:api:messages:2.0:SearchRequest"
        ]
      })
    }
  );

  const data = await resp.json();
  console.log(data);
}

run();
Find user by external ID

Locate a user by their ID set in your system (external id for Transmit) by running a GET request to /scim/users endpoint or POST to /scim/users/.search.

GETPOST
Copy
Copied
import fetch from 'node-fetch';

async function run() {
  const query = new URLSearchParams({
    startIndex: '1', // 1-based index
    count: '100', // If set to 0 or undefined, no records will be returned
    filter: 'externalID eq \"AABBCCDD\"' // Filter expression allows locating a user with a specific external ID
  }).toString();

  const resp = await fetch(
    `https://api.transmitsecurity.io/cis/scim/Users?${query}`,
    {
      method: 'GET',
      headers: {
        Accept: 'application/scim+json',
        Authorization: 'Bearer [CLIENT_ACCESS_TOKEN]'
      }
    }
  );

  const data = await resp.text();
  console.log(data);
}

run();
Copy
Copied
import fetch from 'node-fetch';

async function run() {
  const resp = await fetch(
    `https://api.transmitsecurity.io/cis/scim/Users/.search`,
    {
      method: 'POST',
      headers: {
        Accept: 'application/scim+json',
        'Content-Type': 'application/scim+json',
        Authorization: 'Bearer [CLIENT_ACCESS_TOKEN]'
      },
      body: JSON.stringify({
        startIndex: 1, // 1-based index
        count: '100', // If set to 0 or undefined, no records will be returned
        filter: 'externalId eq \"AABBCCDD\"' // Filter expression allows locating a user with a specific external ID
        schemas: [
            "urn:ietf:params:scim:api:messages:2.0:SearchRequest"
        ]
      })
    }
  );

  const data = await resp.json();
  console.log(data);
}

run();

Search for users in your region

Let's assume your business is located in the United States and you're interested in finding users either located in the United States as well or speaking English. Send a GET request to /scim/users endpoint or POST to /scim/users/.search with a complex filter expression. For details on how filtering works and available operators, see Search query syntax.

GETPOST
Copy
Copied
import fetch from 'node-fetch';

async function run() {
  const query = new URLSearchParams({
    startIndex: '1', // 1-based index
    count: '100', // If set to 0 or undefined, no records will be returned
    filter: '(preferredLanguage eq \"en\")or(addresses.country eq \"USA\")' // Filter expression allows fetching users who either speak English or located in the USA
  }).toString();

  const resp = await fetch(
    `https://api.transmitsecurity.io/cis/scim/Users?${query}`,
    {
      method: 'GET',
      headers: {
        Accept: 'application/scim+json',
        Authorization: 'Bearer [CLIENT_ACCESS_TOKEN]'
      }
    }
  );

  const data = await resp.text();
  console.log(data);
}

run();
Copy
Copied
import fetch from 'node-fetch';

async function run() {
  const resp = await fetch(
    `https://api.transmitsecurity.io/cis/scim/Users/.search`,
    {
      method: 'POST',
      headers: {
        Accept: 'application/scim+json',
        'Content-Type': 'application/scim+json',
        Authorization: 'Bearer [CLIENT_ACCESS_TOKEN]'
      },
      body: JSON.stringify({
        startIndex: 1, // 1-based index
        count: '100', // If set to 0 or undefined, no records will be returned
        filter: '(preferredLanguage eq \"en\")or(addresses.country eq \"USA\")"' // Filter expression allows fetching users who either speak English or located in the USA
        schemas: [
            "urn:ietf:params:scim:api:messages:2.0:SearchRequest"
        ]
      })
    }
  );

  const data = await resp.json();
  console.log(data);
}

run();

Fetch user updates

For ongoing exchange of identity information, consider synchronizing data between Mosaic and other services, for example, every 24 hours. With the query below you can retrieve all user records that have been updated within the last 24 hours. Send a GET request to /scim/users endpoint or POST to /scim/users/.search with a filter expression.

GETPOST
Copy
Copied
import fetch from 'node-fetch';

async function run() {
  const query = new URLSearchParams({
    startIndex: '1', // 1-based index
    count: '100', // If set to 0 or undefined, no records will be returned
    filter: 'meta.lastModified ge \"2023-11-20T00:00:00.000Z\"' // Filter expression allows fetching user updates that occurred after November 20, 2023.
  }).toString();

  const resp = await fetch(
    `https://api.transmitsecurity.io/cis/scim/Users?${query}`,
    {
      method: 'GET',
      headers: {
        Accept: 'application/scim+json',
        Authorization: 'Bearer [CLIENT_ACCESS_TOKEN]'
      }
    }
  );

  const data = await resp.text();
  console.log(data);
}

run();
Copy
Copied
import fetch from 'node-fetch';

async function run() {
  const resp = await fetch(
    `https://api.transmitsecurity.io/cis/scim/Users/.search`,
    {
      method: 'POST',
      headers: {
        Accept: 'application/scim+json',
        'Content-Type': 'application/scim+json',
        Authorization: 'Bearer [CLIENT_ACCESS_TOKEN]'
      },
      body: JSON.stringify({
        startIndex: 1, // 1-based index
        count: '100', // If set to 0 or undefined, no records will be returned
        filter: 'meta.lastModified ge \"2023-11-20T00:00:00.000Z\"' // Filter expression allows fetching user updates that occurred after November 20, 2023.
        schemas: [
            "urn:ietf:params:scim:api:messages:2.0:SearchRequest"
        ]
      })
    }
  );

  const data = await resp.json();
  console.log(data);
}

run();

Update user's profile

Modify user details such as their phone number or email address by sending a PUT request to /scim/users endpoint. Changes will be merged into the user's profile, so you can specify only the fields you want to add or update. Note that:

  • Since Mosaic performs a shallow merge, object- and array-based attributes (like name and addresses ) cannot be partially updated, as the new value of an object or array will just replace the current one. For example, updating a familyName for a user who has both a familyName and a givenName defined will override an entire name object and an updated user profile will only include a new family name.
  • Custom data specified in urn:ietf:params:scim:schemas:extension:CustomAppData:2.0:User is also updated as a shallow merge.
Copy
Copied
import fetch from 'node-fetch';

async function run() {
  const userId = '[USER_ID]'; // User to update
  const resp = await fetch(
    `https://api.transmitsecurity.io/cis/scim/Users/${userId}`,
    {
      method: 'PUT',
      headers: {
        Accept: 'string',
        'Content-Type': 'application/json',
        Authorization: 'Bearer [CLIENT_ACCESS_TOKEN]'
      },
      body: JSON.stringify({
        name: { // Overrides user's name. Other attributes inside this object will be overridden
          familyName: '[USER_FAMILY_NAME]'
        },
        preferredLanguage: 'LANGUAGE', // Adds preferred language to user profile
        emails: [
          {
            'value': '[NEW_USER_EMAIL]', // Overrides primary user email. Non-primary emails remain as is
            'primary': true
          }
        ],
        schemas: [
          'urn:ietf:params:scim:schemas:core:2.0:User'
        ],
        userName: '[USERNAME]'// Unique user identifier
      })
    }
  );

  const data = await resp.json();
  console.log(data);
}

run();

Delete a user

As a final step in user lifecycle management, remove inactive users by sending a DELETE request to scim/users endpoint and providing the user's ID.

Copy
Copied
import fetch from 'node-fetch';

async function run() {
  const userId = '[USERID]'; // The ID of the user to delete
  const resp = await fetch(
    `https://api.transmitsecurity.io/cis/scim/Users/${userId}`,
    {
      method: 'DELETE',
      headers: {
        Accept: 'string',
        Authorization: 'Bearer [CLIENT_ACCESS_TOKEN]'
      }
    }
  );

  if (resp.status === 204) {
    console.log('success');
  } else {
    const data = await resp.text();
    console.log(data);
  }
}

run();