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.:
- Create a user
- Enrich user data
- List all users
- Search for users
- Fetch user updates
- Update user's profile
- Delete a user
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.
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();
{
"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.
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.
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();
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.
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();
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.
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();
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.
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();
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.
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();
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.
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();
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.
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();
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.
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();
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
andaddresses
) cannot be partially updated, as the new value of an object or array will just replace the current one. For example, updating afamilyName
for a user who has both afamilyName
and agivenName
defined will override an entirename
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.
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.
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();