A comprehensive TypeScript library for interacting with SCIM 2.0 (System for Cross-domain Identity Management) servers. This library provides full support for user and group management, advanced filtering, pagination, and bulk operations according to RFC 7643 and RFC 7644 specifications.
- ✅ Full SCIM 2.0 Compliance - Implements RFC 7643 (Core Schema) and RFC 7644 (Protocol)
- 🔐 Multiple Authentication Methods - Bearer token, API key, Basic auth
- 👥 User Management - Create, read, update, delete, and list users
- 👨👩👧👦 Group Management - Complete group lifecycle management
- 🔍 Advanced Filtering - Fluent filter builder with SCIM query syntax
- 📄 Pagination Support - Handle large datasets efficiently
- 🔄 Patch Operations - Partial updates using SCIM patch semantics
- 📦 Bulk Operations - Perform multiple operations in a single request
- 🛠 TypeScript First - Full type safety and IntelliSense support
- 🚀 Easy to Use - Intuitive API with sensible defaults
npm install scim-clientimport { SCIMClient, filter } from 'scim-client';
// Initialize the client
const client = new SCIMClient({
baseUrl: 'https://your-scim-server.com/scim/v2',
authToken: 'your-bearer-token'
});
// Create a user
const user = await client.createUser({
userName: 'john.doe@example.com',
name: {
givenName: 'John',
familyName: 'Doe'
},
emails: [{
value: 'john.doe@example.com',
primary: true
}],
active: true
});
// List users with filtering
const users = await client.listUsers({
filter: filter().eq('active', true).build(),
count: 50
});// Bearer Token
const client = new SCIMClient({
baseUrl: 'https://api.example.com/scim/v2',
authToken: 'your-bearer-token'
});
// API Key
const client = new SCIMClient({
baseUrl: 'https://api.example.com/scim/v2',
apiKey: 'your-api-key'
});
// Basic Authentication
const client = new SCIMClient({
baseUrl: 'https://api.example.com/scim/v2',
username: 'your-username',
password: 'your-password'
});
// Custom Headers
const client = new SCIMClient({
baseUrl: 'https://api.example.com/scim/v2',
authToken: 'token',
customHeaders: {
'X-Custom-Header': 'value'
}
});const user = await client.createUser({
userName: 'john.doe@example.com',
name: {
formatted: 'John Doe',
familyName: 'Doe',
givenName: 'John'
},
displayName: 'John Doe',
active: true,
emails: [{
value: 'john.doe@example.com',
type: 'work',
primary: true
}],
phoneNumbers: [{
value: '+1-555-0123',
type: 'work'
}]
});// Full update (PUT)
const updatedUser = await client.updateUser(userId, {
userName: 'john.smith@example.com',
displayName: 'John Smith',
active: true
});
// Partial update (PATCH)
import { createPatchRequest, replaceOperation } from 'scim-client';
const patchRequest = createPatchRequest([
replaceOperation('displayName', 'John Smith'),
replaceOperation('active', false)
]);
const patchedUser = await client.patchUser(userId, patchRequest);import { filter } from 'scim-client';
// Simple filter
const activeUsers = await client.listUsers({
filter: filter().eq('active', true).build()
});
// Complex filter
const complexFilter = filter()
.eq('active', true)
.and()
.co('emails.value', '@example.com')
.and()
.gt('meta.created', '2023-01-01T00:00:00Z')
.build();
const users = await client.listUsers({
filter: complexFilter,
sortBy: 'meta.created',
sortOrder: 'descending',
startIndex: 1,
count: 50
});const group = await client.createGroup({
displayName: 'Engineering Team',
members: [{
value: 'user-id-1',
display: 'John Doe'
}]
});import { createPatchRequest, addOperation } from 'scim-client';
const addMembersPatch = createPatchRequest([
addOperation('members', [{
value: 'user-id-2',
display: 'Jane Smith'
}])
]);
await client.patchGroup(groupId, addMembersPatch);The library provides a fluent filter builder that supports all SCIM filter operations:
import { filter } from 'scim-client';
// Comparison operators
filter().eq('userName', 'john.doe@example.com').build()
filter().ne('active', false).build()
filter().co('displayName', 'John').build() // contains
filter().sw('userName', 'admin').build() // starts with
filter().ew('emails.value', '@example.com').build() // ends with
filter().gt('meta.created', '2023-01-01').build()
filter().ge('meta.lastModified', '2023-01-01').build()
filter().lt('meta.created', '2023-12-31').build()
filter().le('meta.lastModified', '2023-12-31').build()
filter().pr('phoneNumbers').build() // present
// Logical operators
filter()
.eq('active', true)
.and()
.co('emails.value', '@example.com')
.build()
filter()
.eq('userType', 'Employee')
.or()
.eq('userType', 'Contractor')
.build()
filter()
.not()
.eq('active', false)
.build()Perform multiple operations in a single request:
import { createBulkRequest } from 'scim-client';
const bulkRequest = createBulkRequest([
{
method: 'POST',
bulkId: 'create-user-1',
path: '/Users',
data: {
userName: 'user1@example.com',
name: { givenName: 'User', familyName: 'One' }
}
},
{
method: 'POST',
bulkId: 'create-user-2',
path: '/Users',
data: {
userName: 'user2@example.com',
name: { givenName: 'User', familyName: 'Two' }
}
},
{
method: 'DELETE',
bulkId: 'delete-user',
path: '/Users/old-user-id'
}
]);
const bulkResponse = await client.bulkOperations(bulkRequest);The library provides comprehensive error handling:
import { SCIMError } from 'scim-client';
try {
const user = await client.getUser('non-existent-id');
} catch (error) {
if (error instanceof SCIMError) {
console.log('SCIM Error:', error.status, error.message, error.scimType);
} else {
console.log('Network or other error:', error);
}
}Query server capabilities and configuration:
// Test connection
const isConnected = await client.testConnection();
// Get service provider configuration
const config = await client.getServiceProviderConfig();
// Get supported resource types
const resourceTypes = await client.getResourceTypes();
// Get available schemas
const schemas = await client.getSchemas();Main client class for all SCIM operations.
| Option | Type | Required | Description |
|---|---|---|---|
baseUrl |
string | Yes | Base URL of the SCIM server |
authToken |
string | No | Bearer token for authentication |
apiKey |
string | No | API key for authentication |
username |
string | No | Username for basic auth |
password |
string | No | Password for basic auth |
timeout |
number | No | Request timeout in milliseconds (default: 30000) |
customHeaders |
object | No | Additional HTTP headers |
createUser(user)- Create a new usergetUser(id, attributes?, excludedAttributes?)- Get user by IDupdateUser(id, user)- Update user (full replace)patchUser(id, patchRequest)- Update user (partial)deleteUser(id)- Delete userlistUsers(options?)- List users with filtering/pagination
createGroup(group)- Create a new groupgetGroup(id, attributes?, excludedAttributes?)- Get group by IDupdateGroup(id, group)- Update group (full replace)patchGroup(id, patchRequest)- Update group (partial)deleteGroup(id)- Delete grouplistGroups(options?)- List groups with filtering/pagination
bulkOperations(bulkRequest)- Perform bulk operationsgetServiceProviderConfig()- Get server configurationgetResourceTypes()- Get supported resource typesgetSchemas()- Get available schemastestConnection()- Test server connectivity
# Install dependencies
npm install
# Build the project
npm run build
# Run in development mode
npm run dev
# Watch for changes
npm run watch- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
MIT
For issues and questions, please use the GitHub issue tracker.