API Reference
The following guides provide information about the APIs and SDKs that are available for the Directory Sync service.
Directory
Below are the properties of a directory connection.
Properties
name
: The name of the directorytenant
: The tenant ID of the tenant you want to create the directory for.product
: The product ID of the product you want to create the directory for.type
: The directory provider type. See the Directory Providers for more information.webhook.endpoint
: The webhook URL to which the directory connection will POST the events.webhook.secret
: The webhook secret used to sign the webhook payload.log_webhook_events
: Indicate if the webhook events should be logged.deactivated
: Indicate if the directory connection is deactivated.
The tenant
and product
should not contain the :
character since we use it as a delimiter internally.
Initialize Directory Sync
const opts = {
externalUrl: 'https://my-cool-app.com',
db: {
engine: 'mongo',
url: 'mongodb://localhost:27017/my-cool-app',
},
};
let directorySyncController;
// Please note that the initialization of @boxyhq/saml-jackson is async, you cannot run it at the top level.
async function init() {
const jackson = await require('@boxyhq/saml-jackson').controllers(opts);
directorySyncController = jackson.directorySyncController;
}
Create a new directory
Create a new directory connection.
Request
- Node.js
- Shell
const { data, error } = await directorySyncController.directories.create({
name: 'App',
tenant: 'boxyhq',
product: 'jackson',
type: 'onelogin-scim-v2',
webhook_url: 'https://my-cool-app.com/webhook',
webhook_secret: 'my-secret',
});
curl --request POST \
--url http://localhost:5225/api/v1/directory-sync \
--header 'Authorization: Api-Key secret' \
--header 'Content-Type: application/json' \
--data '{
"name": "App",
"tenant": "boxyhq",
"product": "jackson",
"type": "onelogin-scim-v2",
"webhook_url": "https://my-cool-app.com/webhook",
"webhook_secret": "my-secret"
}'
Response
{
"data": {
"id": "58b5cd9dfaa39d47eb8f5f88631f9a629a232016",
"name": "App",
"tenant": "boxyhq",
"product": "jackson",
"type": "onelogin-scim-v2",
"log_webhook_events": false,
"scim": {
"path": "/api/scim/v2.0/58b5cd9dfaa39d47eb8f5f88631f9a629a232016",
"secret": "IJzAoevjD_liiiy-VkDtXg",
"endpoint": "http://localhost:5225/api/scim/v2.0/58b5cd9dfaa39d47eb8f5f88631f9a629a232016"
},
"webhook": {
"endpoint": "https://my-cool-app.com/webhook",
"secret": "my-secret"
}
},
"error": null
}
Get directories
Get the list of directories for the given tenant and product. A tenant can have multiple directories for same or different products.
Request
- Node.js
- Shell
const tenant = 'boxyhq';
const product = 'jackson';
const { data, error } =
await directorySyncController.directories.getByTenantAndProduct(
tenant,
product
);
curl --request GET \
--url 'http://localhost:5225/api/v1/directory-sync?tenant=boxyhq&product=jackson' \
--header 'Authorization: Api-Key secret' \
--header 'Content-Type: application/json'
Response
{
"data": [
{
"id": "58b5cd9dfaa39d47eb8f5f88631f9a629a232016",
"name": "App",
"tenant": "boxyhq",
"product": "jackson",
"type": "onelogin-scim-v2",
"log_webhook_events": false,
"scim": {
"path": "/api/scim/v2.0/58b5cd9dfaa39d47eb8f5f88631f9a629a232016",
"secret": "IJzAoevjD_liiiy-VkDtXg",
"endpoint": "http://localhost:5225/api/scim/v2.0/58b5cd9dfaa39d47eb8f5f88631f9a629a232016"
},
"webhook": {
"endpoint": "https://my-cool-app.com/webhook",
"secret": "my-secret"
}
}
],
"error": null
}
Get a directory
Get the details of a directory by its unique id.
Request
- Node.js
- Shell
const directoryId = '58b5cd9dfaa39d47eb8f5f88631f9a629a232016';
const { data, error } = await directorySyncController.directories.get(
directoryId
);
curl --request GET \
--url 'http://localhost:5225/api/v1/directory-sync/58b5cd9dfaa39d47eb8f5f88631f9a629a232016' \
--header 'Authorization: Api-Key secret' \
--header 'Content-Type: application/json'
Response
{
"data": {
"id": "58b5cd9dfaa39d47eb8f5f88631f9a629a232016",
"name": "App",
"tenant": "boxyhq",
"product": "jackson",
"type": "onelogin-scim-v2",
"log_webhook_events": false,
"scim": {
"path": "/api/scim/v2.0/58b5cd9dfaa39d47eb8f5f88631f9a629a232016",
"secret": "IJzAoevjD_liiiy-VkDtXg",
"endpoint": "http://localhost:5225/api/scim/v2.0/58b5cd9dfaa39d47eb8f5f88631f9a629a232016"
},
"webhook": {
"endpoint": "https://my-cool-app.com/webhook",
"secret": "my-secret"
}
},
"error": null
}
List directory users
List all the users in a directory.
Request
- Node.js
- Shell
const tenant = 'boxyhq';
const product = 'jackson';
const { data, error } = await directorySyncController.users
.setTenantAndProduct(tenant, product)
.getAll();
curl --request GET \
--url 'http://localhost:5225/api/v1/directory-sync/users?tenant=boxyhq&product=jackson' \
--header 'Authorization: Api-Key secret' \
--header 'Content-Type: application/json'
Response
{
"data": [
{
"id": "6296f71e-15fd-4af4-86ee-d6623b3ef1a4",
"first_name": "Aswin",
"last_name": "Venugopal",
"email": "[email protected]",
"active": true,
"raw": {
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName": "[email protected]",
"name": {
"givenName": "Aswin",
"familyName": "Venugopal",
"honorificPrefix": "Sir"
},
"emails": [
{
"primary": true,
"value": "[email protected]",
"type": "work"
}
],
"displayName": "Aswin Venugopal",
"addresses": [
{
"primary": true,
"postalCode": "123"
}
],
"locale": "en-US",
"externalId": "00u34iw1hm16RmjS95d7",
"groups": [],
"active": true,
"id": "6296f71e-15fd-4af4-86ee-d6623b3ef1a4"
}
},
{
"id": "ebc31d6e-7d62-4f81-b9e5-eb5f1a04ee92",
"first_name": "Kiran",
"last_name": "Krishnan",
"email": "[email protected]",
"active": true,
"raw": {
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName": "[email protected]",
"name": {
"givenName": "Kiran",
"familyName": "Krishnan"
},
"emails": [
{
"primary": true,
"value": "[email protected]",
"type": "work"
}
],
"displayName": "Kiran Krishnan",
"addresses": [
{
"primary": true,
"region": "Kerala"
}
],
"locale": "en-US",
"externalId": "00u3e3cmpdDydXdzV5d7",
"groups": [],
"active": true,
"id": "ebc31d6e-7d62-4f81-b9e5-eb5f1a04ee92"
}
}
],
"error": null
}
Get a directory user
Get the details of a directory user.
Request
- Node.js
- Shell
const tenant = 'boxyhq';
const product = 'flex';
const userId = 'ebc31d6e-7d62-4f81-b9e5-eb5f1a04ee92';
const users = await directorySyncController.users
.setTenantAndProduct(tenant, product)
.get(userId);
curl --request GET \
--url 'http://localhost:5225/api/v1/directory-sync/users/ebc31d6e-7d62-4f81-b9e5-eb5f1a04ee92?tenant=boxyhq&product=jackson' \
--header 'Authorization: Api-Key secret' \
--header 'Content-Type: application/json'
Response
{
"data": {
"id": "ebc31d6e-7d62-4f81-b9e5-eb5f1a04ee92",
"first_name": "Kiran",
"last_name": "Krishnan",
"email": "[email protected]",
"active": true,
"raw": {
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName": "[email protected]",
"name": {
"givenName": "Kiran",
"familyName": "Krishnan"
},
"emails": [
{
"primary": true,
"value": "[email protected]",
"type": "work"
}
],
"displayName": "Kiran Krishnan",
"addresses": [
{
"primary": true,
"region": "Kerala"
}
],
"locale": "en-US",
"externalId": "00u3e3cmpdDydXdzV5d7",
"groups": [],
"active": true,
"id": "ebc31d6e-7d62-4f81-b9e5-eb5f1a04ee92"
}
},
"error": null
}
List directory groups
List all the groups in a directory.
Request
- Node.js
- Shell
const tenant = 'boxyhq';
const product = 'jackson';
const users = await directorySyncController.groups
.setTenantAndProduct(tenant, product)
.getAll();
curl --request GET \
--url 'http://localhost:5225/api/v1/directory-sync/groups?tenant=boxyhq&product=jackson' \
--header 'Authorization: Api-Key secret' \
--header 'Content-Type: application/json'
Response
{
"data": [
{
"id": "44d08c0e-d185-4a5e-80a6-b47a717ffaa5",
"name": "Developers",
"raw": {
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"displayName": "Developers",
"members": [
{
"value": "6296f71e-15fd-4af4-86ee-d6623b3ef1a4",
"display": "[email protected]"
},
{
"value": "ebc31d6e-7d62-4f81-b9e5-eb5f1a04ee92",
"display": "[email protected]"
}
],
"id": "44d08c0e-d185-4a5e-80a6-b47a717ffaa5"
}
}
],
"error": null
}
Get a directory group
Get the details of a directory group.
Request
- Node.js
- Shell
const tenant = 'boxyhq';
const product = 'jackson';
const groupId = '44d08c0e-d185-4a5e-80a6-b47a717ffaa5';
const users = await directorySyncController.groups
.setTenantAndProduct(tenant, product)
.get(groupId);
curl --request GET \
--url 'http://localhost:5225/api/v1/directory-sync/groups/44d08c0e-d185-4a5e-80a6-b47a717ffaa5?tenant=boxyhq&product=jackson' \
--header 'Authorization: Api-Key secret' \
--header 'Content-Type: application/json'
Response
{
"data": {
"id": "44d08c0e-d185-4a5e-80a6-b47a717ffaa5",
"name": "Developers",
"raw": {
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"displayName": "Developers",
"members": [
{
"value": "6296f71e-15fd-4af4-86ee-d6623b3ef1a4",
"display": "[email protected]"
},
{
"value": "ebc31d6e-7d62-4f81-b9e5-eb5f1a04ee92",
"display": "[email protected]"
}
],
"id": "44d08c0e-d185-4a5e-80a6-b47a717ffaa5"
},
"members": [
{
"group_id": "44d08c0e-d185-4a5e-80a6-b47a717ffaa5",
"user_id": "6296f71e-15fd-4af4-86ee-d6623b3ef1a4"
},
{
"group_id": "44d08c0e-d185-4a5e-80a6-b47a717ffaa5",
"user_id": "ebc31d6e-7d62-4f81-b9e5-eb5f1a04ee92"
}
]
},
"error": null
}
List group members
Get the members of a directory group. Only user IDs are returned.
(Introduced in v1.20.6)
Request
- Node.js
- Shell
await directorySyncController.groups
.setTenantAndProduct('boxyhq', 'jackson')
.getGroupMembers({
groupId: '44d08c0e-d185-4a5e-80a6-b47a717ffaa5',
pageOffset: 0,
pageLimit: 10,
});
curl --request GET \
--url 'http://localhost:5225/api/v1/directory-sync/groups/44d08c0e-d185-4a5e-80a6-b47a717ffaa5/members?tenant=boxyhq&product=jackson&pageOffset=0&pageLimit=10' \
--header 'Authorization: Api-Key secret' \
--header 'Content-Type: application/json'
Response
{
"data": [
{
"user_id": "107130779649255553459"
},
{
"user_id": "107471187046551199726"
}
]
}
Google Directory Sync
Google Directory Sync specific properties:
google_domain
: The Google Workspace domain name.google_access_token
: The Google Admin access token.google_refresh_token
: The Google Admin refresh token.
Jackson requires following API scopes:
https://www.googleapis.com/auth/admin.directory.group.member.readonly
https://www.googleapis.com/auth/admin.directory.group.readonly
1. Get Authentication URL
Get the URL to authenticate with admin credentials. Google will prompt the user to authenticate with the admin credentials and authorize the application to access the Google Workspace directories.
Request
- Node.js
await directorySyncController.google.generateAuthorizationUrl({
directoryId: '58b5cd9dfaa39d47eb8f5f88631f9a629a232016',
});
Response
{
"data": {
"authorizationUrl": "https://accounts.google.com/..."
},
"error": null
}
2. Get Access Token
Get the access token from the authorization code.
Request
- Node.js
await directorySyncController.google.getAccessToken({
directoryId: '58b5cd9dfaa39d47eb8f5f88631f9a629a232016',
code: 'authorization-code',
});
Response
{
"data": {
"access_token": "ya29.a0AWY7CknlXtELCHzdKuS...",
"refresh_token": "1//03MJpa-zseicbCgYIARAAGAMSNwF-...",
"scope": "https://www.googleapis.com/auth/admin.directory.group.member.readonly https://www.googleapis.com/auth/admin.directory.group.readonly",
"token_type": "Bearer",
"expiry_date": 1684343934305
},
"error": null
}
3. Set Access Token to Directory
Set the access token and refresh token to the directory. We'll use the access token to make requests to the Google Directory API.
Request
- Node.js
await directorySyncController.google.setToken({
directoryId: '58b5cd9dfaa39d47eb8f5f88631f9a629a232016',
accessToken: 'ya29.a0AWY7CknlXtELCHzdKuS...',
refreshToken: '1//03MJpa-zseicbCgYIARAAGAMSNwF-...',
});
Response
{
"data": {
"id": "58b5cd9dfaa39d47eb8f5f88631f9a629a232016",
"name": "App",
"tenant": "boxyhq",
"product": "jackson",
"type": "google",
"google_domain": "boxyhq.com",
"google_access_token": "ya29.a0AWY7CknlXtELCHzdKuS...",
"google_refresh_token": "1//03MJpa-zseicbCgYIARAAGAMSNwF-..."
},
"error": null
}
4. Sync Directory
Jackson can be configured to sync your Google Workspace directory.
- Node.js
- Shell
const callback = (event: DirectorySyncEvent) => {
console.log(event);
};
await directorySyncController.sync(callback);
curl -X POST \
-H "Authorization: Api-Key YOUR_API_KEY" \
http://localhost:5225/api/v1/dsync/cron/sync-google
You'll ideally want to run the sync on a schedule (e.g. every 2 hours). You can use a cron job to invoke this URL on a schedule.
See more information about the Directory Sync Event below.
Handle the Requests from Identity Providers
Make sure your application can handle the requests from Identity Providers.
Routes
Typically, you will want to add the following routes to your application to handle the requests. However, the Methods
can vary for some Identity Providers.
Route | Methods | Event |
---|---|---|
/Users | POST | A new user has been assigned to a SCIM app |
/Users/:id | PUT, PATCH | A user information has been updated |
/Users/:id | DELETE | A user has been removed from a SCIM app |
/Groups | POST | A new group has been pushed |
/Groups/:id | PUT | Group name has been changed |
/Groups/:id | PATCH | User has been added to or removed from a group |
/Groups/:id | DELETE | Group has been removed |
User Requests
const { data, status } = await directorySyncController.requests.handle(request);
The shape of the request
should be as follows:
{
method: HTTPMethod;
body?: any;
directoryId: string;
resourceId: string;
resourceType: "users",
apiSecret: string;
query: {
count?: number;
startIndex?: number;
filter?: string;
};
};
Group Requests
Handling the group requests is similar to handling the user requests.
const { data, status } = await directorySyncController.requests.handle(request);
The shape of the request
should be as follows:
{
method: HTTPMethod;
body?: any;
directoryId: string;
resourceId: string;
resourceType: "groups",
apiSecret: string;
query: {
count?: number;
startIndex?: number;
filter?: string;
};
};
Callback Function
You can optionally pass a callback function as a second parameter to the handle method requests.handle
. Jackson will invoke the callback with the event object as the first argument after handling the request. You can use the event object to determine the action to take.
const callback = (event: DirectorySyncEvent) => {
console.log(event);
};
const { data, status } = await directorySyncController.requests.handle(
request,
callback
);
Batch processing events
You can enable batch processing of directory sync events instead of receiving events in real-time on your webhook endpoint. Once enabled, Jackson will queue the events and process them in batches. The batch size can be configured using the DSYNC_WEBHOOK_BATCH_SIZE environment variable.
You'll ideally want to run the events processing endpoint on a schedule. You can use a cron job to do this.
- Shell
curl -X POST \
-H "Authorization: Api-Key YOUR_API_KEY" \
http://localhost:5225/api/v1/dsync/cron/process-events
Alternatively you can set the DSYNC_WEBHOOK_BATCH_CRON_INTERVAL
env var.