Author: Jarle Elshaug
Validated through IdP's:
Latest news:
With SCIM Gateway, user management is facilitated through the utilization of the REST-based SCIM 1.1 or 2.0 protocol. The gateway acts as a translator for incoming SCIM requests, seamlessly enabling the exposure of CRUD functionality (create, read, update, and delete user/group) towards destinations. This is achieved through the implementation of endpoint-specific protocols, ensuring precise and efficient provisioning with diverse endpoints.
SCIM Gateway is based on popular asynchronous event driven framework Bun or Node.js using TypeScript/JavaScript. It is cloud and firewall friendly. Runs on almost all operating systems, and may load balance between hosts (horizontal) and cpu's (vertical).
Following example plugins are included:
Loki (NoSQL Document-Oriented Database)
SCIM Gateway becomes a standalone SCIM endpoint
Demonstrates user provisioning towards document-oriented database
Using LokiJS for a fast, in-memory document-oriented database (much like MongoDB/PouchDB)
Default gives two predefined test users loaded using in-memory only (no persistence)
Configuration {"persistence": true}
gives persistence file store (no test users)
Example of a fully functional SCIM Gateway plugin
MongoDB (NoSQL Document-Oriented Database)
Same as plugin "Loki", but using external MongoDB
Shows how to implement a highly configurable multi tenant or multi endpoint solution through baseEntity
in URL
SCIM (REST Webservice)
Demonstrates user provisioning towards REST-Based endpoint (type SCIM)
Using plugin Loki as SCIM endpoint
Can be used as SCIM version-gateway e.g. 1.1=>2.0 or 2.0=>1.1
Can be used to chain several gateways
Soap (SOAP Webservice)
Demonstrates user provisioning towards SOAP-Based endpoint
Example WSDLs are included
Using endpoint "Forwardinc" as an example (comes with Symantec/Broadcom/CA IM SDK - SDKWS)
Shows how to implement a highly configurable multi tenant or multi endpoint solution through baseEntity
in URL
MSSQL (MSSQL Database)
Demonstrates user provisioning towards MSSQL database
SAP HANA (SAP HANA Database)
Demonstrates SAP HANA specific user provisioning
Entra ID (REST Webservices)
Entra ID user provisioning including license management (App Service plans) e.g. Office 365
Using Microsoft Graph API
Using customized SCIM attributes according to Microsoft Graph API
Includes Symantec/Broadcom/CA ConnectorXpress metafile for creating provisioning "Azure - ScimGateway" endpoint type
LDAP (Directory)
Fully functional LDAP plugin
Pre-configured for Microsoft Active Directory
Using endpointMapper (like plugin-entra-id) for attribute mapping flexibility
API (REST Webservices)
Demonstrates API Gateway/plugin functionality using post/put/patch/get/delete
None SCIM plugin, becomes what you want it to become.
Methods included can also be used in standard SCIM plugins
Endpoint complexity could be put in this plugin, and client could instead communicate through Gateway using your own simplified REST specification.
One example of usage could be creation of tickets in ServiceDesk and also the other way, closing a ticket could automatically approve/reject corresponding workflow in IdP.
Bun is a prerequisite and must be installed on the server.
Note, Bun installs by default in the current user’s HOMEPATH\.bun
. To install it elsewhere, set BUN_INSTALL=<install-path>
as a global or system environment variable before installing. The installation will add Bun to the current user’s path, but consider adding it to the global or system path for easier access across all users.
Open a command window (run as administrator)
Create your own package directory e.g. c:\my-scimgateway and install SCIM Gateway within this package.
mkdir c:\my-scimgateway
cd c:\my-scimgateway
bun init -y
bun install scimgateway
bun pm trust scimgateway
c:\my-scimgateway will now be <package-root>
index.ts, lib and config directories containing example plugins have been copied to your package from the original scimgateway package located under node_modules. Bun requires bun pm trust scimgateway
for allowing postinstall copying these files.
If internet connection is blocked, we could install on another machine and copy the <package-root>
folder.
bun c:\my-scimgateway
If using Node.js instead of Bun, scimgateway must be downloaded from github and startup: node --experimental-strip-types c:\my-scimgateway\index.ts
Start a browser (note, Edge do not pop-up logon dialog box when using http)
http://localhost:8880/ping
=> Health check with a "hello" response
http://localhost:8880/Users
http://localhost:8880/Groups
=> Logon using gwadmin/password and two users and groups should be listed
http://localhost:8880/Users/bjensen
http://localhost:8880/Groups/Admins
or
http://localhost:8880/Users?filter=userName eq "bjensen"
http://localhost:8880/Groups?filter=displayName eq "Admins"
=> Lists all attributes for specified user/group
http://localhost:8880/Groups?filter=displayName eq "Admins"&excludedAttributes=members
http://localhost:8880/Groups?filter=members.value eq "bjensen"&attributes=id,displayName,members.value
http://localhost:8880/Users?filter=userName eq "bjensen"&attributes=userName,id,name.givenName
http://localhost:8880/Users?filter=meta.created ge "2010-01-01T00:00:00Z"&attributes=userName,name.familyName,meta.created
http://localhost:8880/Users?filter=emails.value co "@example.com"&attributes=userName,name.familyName,emails&sortBy=name.familyName&sortOrder=descending
=> Filtering and attribute examples
"Ctrl + c" to stop the SCIM Gateway
Tip, take a look at bun test scripts located in
node_modules\scimgateway\test\lib
Not needed after a fresh install
The best and easiest way to upgrade is renaming existing scimgateway package folder, create a new one and do a fresh installation. After the installation we copy index.ts, config and lib folder
(customized plugins) from previous installation to the new installation. You should also read the version history to see custom plugins needs to be updated.
Alternatives are:
Upgrade to latest minor version:
cd c:\my-scimgateway
bun install scimgateway
Note, always backup/copy c:\my-scimgateway before upgrading. Custom plugins and corresponding configuration files will not be affected.
To force a major upgrade (version x.*.* => y.*.*) that will brake compability with any existing custom plugins, we have to include the @latest
suffix in the install command: bun install scimgateway@latest
postinstall
For production we do not need example plugins to be incuded by the postinstall
job
Bun will by default exlude any postinstall
jobs unless we have trusted the scimgateway package using the bun pm trust scimgateway
that updates package.json { trustedDependencies: ["scimgateway"] }
For Node.js (and also Bun), we might set the property scimgateway_postinstall_skip = true
in .npmrc
or setting environment SCIMGATEWAY_POSTINSTALL_SKIP = true
index.ts defines one or more plugins to be started by the const plugins
setting.
// example starting all default plugins:
// const plugins = ['loki', 'scim', 'entra-id', 'ldap', 'mssql', 'api', 'mongodb', 'saphana', 'soap']
const plugins = ['ldap']
for (const plugin of plugins) {
try {
await import(`./lib/plugin-${plugin}.ts`)
} catch (err: any) {
console.error(`plugin-${plugin} startup error: ${err.message}`)
console.log()
}
}
Each endpoint plugin needs a TypeScript file (.ts) and a configuration file (.json). They both must have the same naming prefix. For SAP Hana endpoint we have:
lib\plugin-saphana.ts
config\plugin-saphana.json
Edit specific plugin configuration file according to your needs.
Below shows an example of config\plugin-saphana.json
{
"scimgateway": {
"port": 8884,
"localhostonly": false,
"payloadSize": null,
"scim": {
"version": "2.0",
"skipTypeConvert" : false,
"skipMetaLocation" false,
"groupMemberOfUser": false
"usePutSoftSync" : false
},
"log": {
"loglevel": {
"file": "debug",
"console": "error"
},
"customMasking": []
},
"auth": {
"basic": [
{
"username": "gwadmin",
"password": "password",
"readOnly": false,
"baseEntities": []
}
],
"bearerToken": [
{
"token": null,
"readOnly": false,
"baseEntities": []
}
],
"bearerJwtAzure": [
{
"tenantIdGUID": null,
"readOnly": false,
"baseEntities": []
}
],
"bearerJwt": [
{
"secret": null,
"publicKey": null,
"options": {
"issuer": null
},
"readOnly": false,
"baseEntities": []
}
],
"bearerOAuth": [
{
"client_id": null,
"client_secret": null,
"readOnly": false,
"baseEntities": []
}
],
"passThrough": {
"enabled": false,
"readOnly": false,
"baseEntities": []
}
},
"certificate": {
"key": null,
"cert": null,
"ca": null,
"pfx": {
"bundle": null,
"password": null
}
},
"ipAllowList": [],
"emailOnError": {
"smtp": {
"enabled": false,
"host": null,
"port": 587,
"proxy": null,
"authenticate": true,
"username": null,
"password": null,
"sendInterval": 15,
"to": null,
"cc": null
}
},
"stream": {
"baseUrls": [],
"certificate": {
"ca": null
},
"subscriber": {
"enabled": false,
"entity": {
"undefined": {
"nats": {
"tenant": null,
"subject": null,
"jwt": null,
"secret": null
},
"deleteUserOnLastGroupRoleRemoval": false,
"convertRolesToGroups": false,
"generateUserPassword": false,
"modifyOnly": false,
"replaceDomains": []
}
}
},
"publisher": {
"enabled": false,
"entity": {
"undefined": {
"nats": {
"tenant": null,
"subject": null,
"jwt": null,
"secret": null
}
}
}
}
}
},
"endpoint": {
"host": "hostname",
"port": 30015,
"username": "username",
"password": "password",
"saml_provider": "saml_provider_name"
}
}
Configuration file have two main JSON objects: scimgateway
and endpoint
Definitions in scimgateway
object have fixed attributes, but values can be modified. Sections not used/configured can be removed. This object is used by the core functionality of the SCIM Gateway.
Definitions in endpoint
object are customized according to our plugin code. Plugin typically need this information for communicating with endpoint
port - Gateway will listen on this port number. Clients (e.g. Provisioning Server) will be using this port number for communicating with the gateway.
localhostonly - true or false. False means gateway accepts incoming requests from all clients. True means traffic from only localhost (127.0.0.1) is accepted.
payloadSize - if not defined, default "1mb" will be used. There are cases which large groups could exceed default size and you may want to increase by setting your own size
scim.version - "1.1" or "2.0". Default is "2.0".
scim.skipTypeConvert - true or false, default false. Multivalue attributes supporting types e.g. emails, phoneNumbers, ims, photos, addresses, entitlements and x509Certificates (but not roles, groups and members) will be become "type converted objects" when sent to modifyUser and createUser. This for simplicity of checking attributes included and also for the endpointMapper method (used by plugin-ldap and plugin-entra-id), e.g.:
"emails": {
"work": {"value": "jsmith@example.com", "type": "work"},
"home": {"value": "", "type": "home", "operation": "delete"},
"undefined": {"value": "jsmith@hotmail.com"}
}
skipTypeConvert set to true gives attribute "as-is": array, allow duplicate types including blank, but values to be deleted have been marked with "operation": "delete"
"emails": [
{"value": "jsmith@example.com", "type": "work"},
{"value": "john.smith.org", "type": "home", "operation": "delete"},
{"value": "jsmith@hotmail.com"}
]
scim.skipMetaLocation - true or false, default false. If set to true, meta.location
which contains protocol and hostname from request-url, will be excluded from response e.g. "{...,meta":{"location":"https://my-company.com/<...>"}}
. If using reverse proxy and not including headers X-Forwarded-Proto
and X-Forwarded-Host
, originator will be the proxy and we might not want to expose internal protocol and hostname being used by the proxy request.
scim."groupMemberOfUser - true or false, default false. If body contains groups and groupMemberOfUser=true, groups attribute will remain at user object (groups are member of user) instead of default user member of groups that will use modifyGroup method for maintaining group members.
scim.usePutSoftSync - true or false, default false. PUT /Users/bjensen
will replace the user bjensen with body content. If set to true
, only PUT body content will be replaced. Any additional existing user attributes and groups supported by plugin will remain as-is.
log.loglevel.file - off, error, info, or debug. Output to plugin-logfile e.g. logs\plugin-saphana.log
log.loglevel.console - off, error, info, or debug. Output to stdout and errors to stderr.
log.customMasking - array of attributes to be masked e.g. "customMasking": ["SSN", "weight"]
. By default SCIM Gateway includes masking of some standard attributes like password.
auth - Contains one or more authentication/authorization methods used by clients for accessing gateway - may also include:
GET
requests for corresponding admin userbaseEntity
allowed for this user e.g. ["client-a"] - empty array allowing all.auth.basic - Array of one ore more basic authentication objects - Basic Authentication with username/password. Note, we set a clear text password that will become encrypted when gateway is started.
auth.bearerToken - Array of one or more bearer token objects - Shared token/secret (supported by Entra ID). Clear text value will become encrypted when gateway is started.
auth.bearerJwtAzure - Array of one or more JWT used by Azure SyncFabric. tenantIdGUID must be set to Entra ID Tenant ID.
auth.bearerJwt - Array of one or more standard JWT objects. Using secret or publicKey for signature verification. publicKey should be set to the filename of public key or certificate pem-file located in <package-root>\config\certs
or absolute path being used. Clear text secret will become encrypted when gateway is started. options.issuer is mandatory. Other options may also be included according to jsonwebtoken npm package definition.
auth.bearerOAuth - Array of one or more Client Credentials OAuth configuration objects. client_id
and client_secret
are mandatory. client_secret value will become encrypted when gateway is started. OAuth token request url is /oauth/token e.g. http://localhost:8880/oauth/token
auth.passThrough - Setting auth.passThrough.enabled=true will bypass SCIM Gateway authentication. Gateway will instead pass ctx containing authentication header to the plugin. Plugin could then use this information for endpoint authentication and we don't have any password/token stored at the gateway. Note, this also requires plugin binary having scimgateway.authPassThroughAllowed = true
and endpoint logic for handling/passing ctx.request.header.authorization
certificate - If not using TLS certificate, set "key", "cert" and "ca" to null. When using TLS, "key" and "cert" have to be defined with the filename corresponding to the primary-key and public-certificate. Both files must be located in the <package-root>\config\certs
directory unless absolute path being defined e.g:
"certificate": {
"key": "key.pem",
"cert": "cert.pem",
"ca": null
}
Example of how to make a self signed certificate:
openssl req -nodes -newkey rsa:2048 -x509 -sha256 -days 3650 -keyout key.pem -out cert.pem -subj "/O=Testing/OU=SCIM Gateway/CN=<FQDN>" -config "<path>\openssl.cnf"
<FQDN>
is Fully Qualified Domain Name of the host having SCIM Gateway installed
Note, when using Symantec/Broadcom/CA Provisioning, the "certificate authority - CA" also have to be imported on the Connector Server. For self-signed certificate CA and the certificate (public key) is the same.
PFX / PKCS#12 bundle can be used instead of key/cert/ca e.g:
"pfx": {
"bundle": "certbundle.pfx",
"password": "password"
}
Note, we should normally use certificate (https) for communicating with SCIM Gateway unless we install ScimGatway locally on the manager (e.g. on the CA Connector Server). When installed on the manager, we could use http://localhost:port
or http://127.0.0.1:port
which will not be passed down to the data link layer for transmission. We could then also set {"localhostonly": true}
ipAllowList - Array of one or more IPv4/IPv6 subnets (CIDR) allowed for incoming traffic. E.g. using Entra ID as IdP, we would like to restrict access to IP addresses used by Azure. Azure IP-range can be downloaded from: https://azureipranges.azurewebsites.net, enter AzureActiveDirectory in the search list and select JSON download. Copy the "addressPrefixes" array content and paste into ipAllowList array. CIDR single IP-host syntax is a.b.c.d/32. Note, front-end HTTP proxy or a load balancer must include client IP-address in the X-Forwarded-For header. Configuration example:
"ipAllowList": [
"13.64.151.161/32",
"13.66.141.64/27",
...
"2603:1056:2000::/48",
"2603:1057:2::/48"
]
emailOnError - Contains configuration for sending error notifications by email. Note, only the first error will be sent until sendInterval have passed
emailOnError.smtp.enabled - true or false, value set to true will enable email notifications
emailOnError.smtp.host - Mailserver e.g. "smtp.office365.com"
emailOnError.smtp.port - Port used by mailserver e.g. 587, 25 or 465
emailOnError.smtp.proxy - If using mailproxy e.g. "http://proxy-host:1234"
emailOnError.smtp.authenticate - true or false, set to true will use username/password authentication
emailOnError.smtp.username - Mail account for authentication and also the sender of the email, e.g. "user@outlook.com"
emailOnError.smtp.password - Mail account password
emailOnError.smtp.sendInterval - Mail notifications on error are deferred until sendInterval minutes have passed since the last notification. Default 15 minutes
emailOnError.smtp.to - Comma separated list of recipients email addresses e.g: "someone@example.com"
emailOnError.smtp.cc - Comma separated list of cc email addresses
stream - See SCIM Stream for configuration details
endpoint - Contains endpoint specific configuration according to our plugin code.
./lib/scimdef-v2.json or scimdef-v1.json
exists. Original scimdef-v2.json/scimdef-v1.json can be copied from node_modules/scimgateway/lib to your plugin/lib and customized.X-Forwarded-For
, X-Forwarded-Proto
and X-Forwarded-Host
SEED
with some random characters will override default password seeding logic. This also allow copying configuration file with encrypted secrets from one machine to another. "process.env.<ENVIRONMENT>"
where <ENVIRONMENT>
is the environment variable used. E.g. scimgateway.port could have value "process.env.PORT", then using environment variable PORT."process.file.<path>"
where <path>
is the file used. E.g. key endpoint.password could have value "process.file./var/run/vault/secrets.json" All configuration values can be moved to multiple external files, each file containing one single value. Syntax in original configuration file used by the gateway will then be "process.text.<path>"
where <path>
is the file which contains raw (UTF-8
) character value. E.g. key endpoint.password could have value "process.text./var/run/vault/endpoint.password".
Example:
{
"scimgateway": {
...
"port": "process.env.PORT",
...
"loglevel": {
"file": "process.env.LOG_LEVEL_FILE",
...
"auth": {
"basic": [
{
"username": "process.file./var/run/vault/secrets.json",
"password": "process.file./var/run/vault/secrets.json"
},
...
],
"bearerJwt": [
"secret": "process.text./var/run/vault/jwt.secret",
"publicKey": "process.text./var/run/vault/jwt.pub",
...
],
...
},
"endpoint": {
...
"username": "process.file./var/run/vault/secrets.json",
"password": "process.file./var/run/vault/secrets.json",
...
}
}
jwt.secret file content example:
thisIsSecret
secrets.json file content example for plugin-soap:
{
"plugin-soap.scimgateway.auth.basic[0].username": "gwadmin",
"plugin-soap.scimgateway.auth.basic[0].password": "password",
"plugin-soap.endpoint.username": "superuser",
"plugin-soap.endpoint.password": "secret"
}
Gateway can be started from a command window running in administrative mode
3 ways to start:
bun c:\my-scimgateway
bun c:\my-scimgateway\index.ts
<package-root>bun .
Ctrl+c to stop
Start Windows Task Scheduler (taskschd.msc), right click on "Task Scheduler Library" and choose "Create Task"
General tab:
-----------
Name = SCIM Gateway
User account = SYSTEM
Run with highest privileges
Triggers tab:
-------------
Begin the task = At startup
Actions tab:
------------
Action = Start a program
Program/script = <install path>\bun.exe
Arguments = c:\my-scimgateway
Settings - tab:
---------------
Stop the task if runs longer than = Disabled (greyed out)
Verification:
<pakage-root>\logs
On Linux systems we may also run SCIM Gateway as a Docker image (using docker-compose)
Docker Pre-requisites:
docker-ce
docker-compose
Install SCIM Gateway within your own package and copy provided docker files:
mkdir /opt/my-scimgateway
cd /opt/my-scimgateway
bun init -y
bun install scimgateway
bun pm trust scimgateway
cp ./config/docker/* .
docker-compose.yml <== Here is where you would set the exposed port and environment
Dockerfile <== Main dockerfile
DataDockerfile <== Handles volume mapping
docker-compose-debug.yml <== Debugging
Create a scimgateway user on your Linux VM.
adduser scimgateway
Create a directory on your VM host for the scimgateway configs:
mkdir /home/scimgateway/config
Copy your updated configuration file e.g. /opt/my-scimgateway/config/plugin-loki.json to /home/scimgateway/config. Use scp to perform the copy.
NOTE: /home/scimgateway/config is where all important configuration and loki datastore will reside outside of the running docker container. If you upgrade scimgateway you won't lose your configurations and data.
Build docker images and start it up
docker-compose up --build -d
NOTE: Add the -d flag to run the command above detached.
Be sure to confirm that port 8880 is available with a simple http request
If using default plugin-loki and we have configured {"persistence": true}
, we could confirm scimgateway created loki.db:
su scimgateway
cd /home/scimgateway/config
ls loki.db
To list running containers information:
docker ps
To list available images:
docker images
To view the logs:
docker logs scimgateway
To execute command within your running container:
docker exec scimgateway <bash command>
To stop scimgateway:
docker-compose stop
To restart scimgateway:
docker-compose start
To debug running container (using Visual Studio Code):
docker-compose -f docker-compose.yml -f docker-compose-debug.yml up -d
Start Visual Studio Code and follow these debugging instructions
To upgrade scimgateway docker image (remove the old stuff before running docker-compose up --build):
docker rm scimgateway
docker rm $(docker ps -a -q); docker rmi $(docker images -q -f "dangling=true")
Entra ID could do automatic user provisioning by synchronizing users towards SCIM Gateway, and gateway plugins will update endpoints.
Plugin configuration file must include SCIM Version "2.0" (scimgateway.scim.version) and either Bearer Token (scimgateway.auth.bearerToken[x].token) or Entra ID Tenant ID GUID (scimgateway.auth.bearerJwtAzure[x].tenantIdGUID) or both:
scimgateway: {
"scim": {
"version": "2.0",
...
},
...
"auth": {
"bearerToken": [
{
"token": "shared-secret"
}
],
"bearerJwtAzure": [
{
"tenantIdGUID": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
]
}
...
}
token
configuration must correspond with "Secret Token" defined in Entra ID
tenantIdGUID
configuration must correspond with Entra ID Tenant ID
In Azure Portal:
Azure-Microsoft Entra ID-Enterprise Application-<My Application>-Provisioning-Secret Token
Note, when "Secret Token" is left blank, Azure will use JWT (tenantIdGUID)
Azure-Microsoft Entra ID-Overview-Tenant ID
User mappings attributes between AD and SCIM also needs to be configured
Azure-Microsoft Entra ID-Enterprise Application-<My Application>-Provisioning-Edit attribute mappings-Mappings
Entra ID default SCIM attribute mapping for USER must have:
userPrincipalName mapped to userName (matching precedence #1)
Entra ID default SCIM attribute mapping for GROUP must have:
displayName mapped to displayName (matching precedence #1)
members mapped to members
Some notes related to Entra ID:
Entra ID SCIM documentation
For using OAuth/JWT credentials, Entra ID configuration "Secret Token" (bearer token) should be blank. Plugin configuration must then include bearerJwtAzure.tenantIdGUID. Click "Test Connection" in Azure to verify
Entra ID do a regular check for a "non" existing user/group. This check seems to be a "keep alive" to verify connection.
Entra ID first checks if user/group exists, if not exist they will be created (no explore of all users like CA Identity Manager)
Deleting a user in Entra ID sends a modify user {"active":"False"}
which means user should be disabled. This logic is default set in attribute mappings expression rule Switch([IsSoftDeleted], , "False", "True", "True", "False")
. Standard SCIM "DELETE" method seems not to be used.
Using Symantec/Broadcom/CA Identity Manger, plugin configuration might have to use SCIM Version "1.1" (scimgateway.scim.version).
In the Provisioning Manager we have to use
Endpoint type = SCIM (DYN Endpoint)
or create our own custom endpoint type based on this one
SCIM endpoint configuration example for Loki plugin (plugin-loki)
Endpoint Name = Loki-8880
User Name = gwadmin
Password = password
SCIM Authentication Method = HTTP Basic Authentication
SCIM Based URL = http://localhost:8880
or:
SCIM Based URL = http://localhost:8880/<baseEntity>
Username, password and port must correspond with plugin configuration file. For "Loki" plugin it will be config\plugin-loki.json
"SCIM Based URL" refer to the FQDN (or localhost) having SCIM Gateway installed. Portnumber must be included. Use HTTPS instead of HTTP if SCIM Gateway configuration includes certificates.
"baseEntity" is optional. This is a parameter used for multi tenant or multi endpoint solutions. We could create several endpoints having same base url with unique baseEntity. e.g:
http://localhost:8880/client-a
http://localhost:8880/client-b
Each baseEntity should then be defined in the plugin configuration file with custom attributes needed. Please see examples in plugin-soap.json
IM 12.6 SP7 (and above) also supports pagination for SCIM endpoint (data transferred in bulks - endpoint explore of users). Loki plugin supports pagination. Other plugin may ignore this setting.
Using plugin-entra-id we could do user provisioning towards Entra ID
For testing purposes we could get an Azure free account
For some odd reasons Application needs to be member of "User administrator" for having privileges to manage office/mobile phone on users that is member of any administrator roles
Also note, enable/disable user (accountEnabled - through Graph API) will fail if user have an "Administrator" role other than above mentioned "User Administrator" e.g. "Group Administrator"/"Application Administrator". To be sure we can enable/disable all users, application needs to be member of "Global administrator" - 62e90394-69f5-4237-9190-012177145e10.
Edit index.ts
Set plugin to be started to entra-id
const plugins = ['entra-id']
Edit plugin-entra-id.json
Note, for Symantec/Broadcom/CA Provisioning we must use SCIM version 1.1
scimgateway: {
"scim": {
"version": "1.1"
},
username
and password
used to connect the SCIM Gateway must be defined.
"auth": {
"basic": [
{
"username": "gwadmin",
"password": "password",
"readOnly": false,
"baseEntities": []
}
],
Update tenantIdGUID
, clientID
and clientSecret
according to what you copied from the previous Entra ID configuration.
If using proxy, set proxy.host to "http://<FQDN-ProxyHost>:<port>"
e.g "http://proxy.mycompany.com:3128"
"endpoint": {
"entity": {
"undefined": {
"connection": {
"baseUrls": [
"not in use for Entra ID when tenantIdGUID is defined"
],
"auth": {
"type": "oauth",
"options": {
"tokenUrl": "oauth token_url - not in use when tenantIdGUID is defined",
"tenantIdGUID": "Entra ID Tenant ID (GUID) or Primary domain name - only used by plugin-entra-id",
"clientId": "oauth client_id - Entra ID: Application ID",
"clientSecret": "oauth client_secret - Entra ID: generated application secret value"
}
},
"proxy": {
"host": null,
"username": null,
"password": null
}
}
}
},
"map": {
...
}
}
Note, clientSecret and any proxy.password will become encrypted in this file on the first Azure connection.
For multi-tenant or multi-endpoint support, we may add several entities:
"endpoint": {
"entity": {
"undefined": {
...
},
"client-a": {
...
},
"client-b": {
...
}
}
}
For additional details, see baseEntity description.
Note, we should normally use certificate (https) for communicating with SCIM Gateway unless we install gateway locally on the manager (e.g. on the CA Connector Server). When installed on the manager, we could use http://localhost:port
or http://127.0.0.1:port
which will not be passed down to the data link layer for transmission. We could then also set {"localhostonly": true}
Create a new endpoint type "Azure - ScimGateway"
const plugins = ['entra-id']
in index.ts
plugin-entra-id.json
must also be known Note, metafile "Azure - ScimGateway.xml" is based on CA "Azure - WSL7" with some minor adjustments like using Microsoft Graph API attributes instead of Azure AD Graph attributes.
Provisioning Manager configuration
Endpoint type = Azure - ScimGateway (DYN Endpoint)
Endpoint configuration example:
Endpoint Name = AzureAD-8881
User Name = gwadmin
Password = password
SCIM Authentication Method = HTTP Basic Authentication
SCIM Based URL = http://localhost:8881
or
SCIM Based URL = http://localhost:8881/<baseEntity>
For details, please see section "CA Identity Manager as IdP using SCIM Gateway"
Create = POST http://localhost:8880/Users
(body contains the user information)
Update = PATCH http://localhost:8880/Users/<id>
(body contains the attributes to be updated)
Search/Read = GET http://localhost:8880/Users?userName eq
"userID"&attributes=<comma separated list of scim-schema defined attributes>
Search/explore all users:
GET http://localhost:8880/Users?attributes=userName
Delete = DELETE http://localhost:8880/Users/<id>
Discovery:
GET http://localhost:8880/ServiceProviderConfigs
Specification compliance, authentication schemes, data models.
GET http://localhost:8880/Schemas
Introspect resources and attribute extensions.
Note:
SCIM Gateway also works as an API Gateway when using url /api
or /<baseEntity>/api
Following methods for the none SCIM based api-plugin are supported:
GET /api
GET /api?queries
GET /api/{id}
POST /api + body
PUT /api/{id} + body
PATCH /api/{id} + body
DELETE /api/{id}
These methods can also be used in standard SCIM plugins
Please see example plugin: plugin-api.ts
For JavaScript coding editor you may use Visual Studio Code
Preparation:
lib\plugin-mssql.ts
and config\plugin-mssql.json
and rename both copies to your plugin name prefix e.g. plugin-mine.ts and plugin-mine.json (for SOAP Webservice endpoint we might use plugin-soap as a template) const plugins = ['mine']');
Now we are ready for custom coding by editing plugin-mine.ts Coding should be done step by step and each step should be verified and tested before starting the next (they are all highlighted by comments in existing code).
Template used by CA Provisioning role should only include endpoint supported attributes defined in our plugin. Template should therefore have no links to global user for none supported attributes (e.g. remove %UT% from "Job Title" if our endpoint/code do not support title)
CA Provisioning using default SCIM endpoint do not support SCIM Enterprise User Schema Extension (having attributes like employeeNumber, costCenter, organization, division, department and manager). If we need these or other attributes not found in CA Provisioning, we could define our own by using the free-text "type" definition in the multivalue entitlements or roles attribute. In the template entitlements definition, we could for example define type=Company and set value to %UCOMP%. Please see plugin-soap.ts using Company as a multivalue "type" definition.
Using CA Connector Xpress we could create a new SCIM endpoint type based on the original SCIM. We could then add/remove attributes and change from default assign "user to groups" to assign "groups to user". There are also other predefined endpoints based on the original SCIM. You may take a look at "ServiceNow - WSL7" and "Zendesk - WSL7".
For project setup:
Using Connector Xpress based on the original SCIM endpoint.
Delete defaults:
Group - Associations - with User Account
Group - Attributes - members
User Account - Attributes - Group Membership
Create new attribute:
User Account - Attributes: Groups - Flexi DN - Multivalue - groups
Create User - Group associations:
User Account - Accociations - Direct association with = Group
User Account - Accociations - with Group
Note, "Include a Reverse Association" - not needed if we don't need Group object functionality e.g list/add/remove group members
User Attribute = Physical Attribute = Groups
Match Group = By Attribute = ID
Objects Must Exist
Use DNs in Attribute = activated (toggled on)
Include a Reverse Association (if needed)
Group Attribute = Virtual Attribute = User Membership
Match User Account = By Attribute = User Name
Note, groups should be capability attribute (updated when account is synchronized with template):
advanced options - Synchronized = enabled (toggled on)
Plugins should have following initialization:
// start - mandatory plugin initialization
const ScimGateway: typeof import('scimgateway').ScimGateway = await (async () => {
try {
return (await import('scimgateway')).ScimGateway
} catch (err) {
const source = './scimgateway.ts'
return (await import(source)).ScimGateway
}
})()
const scimgateway = new ScimGateway()
const config = scimgateway.getConfig()
scimgateway.authPassThroughAllowed = false
// end - mandatory plugin initialization
If using REST, we could also include the HelperRest:
// start - mandatory plugin initialization
...
const HelperRest: typeof import('scimgateway').HelperRest = await (async () => {
try {
return (await import('scimgateway')).HelperRest
} catch (err) {
const source = './scimgateway.ts'
return (await import(source)).HelperRest
}
})()
...
// end - mandatory plugin initialization
Plugins should include following SCIM methods:
In addition following general API methods are available for use:
In code editor (e.g., Visual Studio Code), method details and documentation are shown by IntelliSense
MIT © Jarle Elshaug
[Improved]
[Fixed]
[Improved]
[Improved]
[Fixed]
[MAJOR]
Major version v5.0.0 marks a shift to native TypeScript support and prioritizes Bun over Node.js.
Besides going from JavaScript to TypeScript, following can be mentioned:
Code editor now having IntelliSense showing available methods and documentation details for scimgateway methods
index.ts having new logic for starting plugins e.g.: const plugins = ['ldap']
for starting plugin-ldap
If using Node.js: node must be version >= 22.6.0, scimgateway must be downloaded from github (because stripping types is currently unsupported for files under node_modules) and startup argument --experimental-strip-types
e.g.; node --experimental-strip-types index.ts
Plugins can use scimgateway.HelperRest()
for REST functionality. Previously this logic was included in each plugin that used REST.
// start - mandatory plugin initialization
...
const HelperRest: typeof import('scimgateway').HelperRest = await (async () => {
try {
return (await import('scimgateway')).HelperRest
} catch (err) {
const source = './scimgateway.ts'
return (await import(source)).HelperRest
}
})()
...
// end - mandatory plugin initialization
Note, HelperRest use fetch which is not fully supported by Node.js regarding TLS.
For TLS and Node.js, environment must instead be used and set before started, e.g.,:
export NODE_EXTRA_CA_CERTS=/package-path/config/certs/ca.pem
or
export NODE_TLS_REJECT_UNAUTHORIZED=0
Configuration secrets (password, secret, token, client_secret, ... ) defined in the endpoint
section of the configuration file, will automatically be encrypted/decrypted. If there are secrets not handled by the automated encryption/decryption, we may use scimgateway.getSecret()
. In the old version, corresponding method was named scimgateway.getPassword().
kubernetes configuration and logic have been removed. Kubernetes can use default /ping
url for healthchecks, and graceful shutdown is taken care of the gateway
In case using custom schemas defined in lib/scimdef-v1/v2.js, these files have now changed to scimdef-v1/v2.json
config/docker/Dockerfile
now using Bun
plugin-entra, modify licenses/servicePlans is not included anymore, only listing. For license management we instead use groups.
plugin-ldap, for LDAPS/TLS and Bun, we must use environments e.g.:
export NODE_EXTRA_CA_CERTS=/package-path/config/certs/ca.pem
or
export NODE_TLS_REJECT_UNAUTHORIZED=0
How to migrate existing plugins:
Remove old index.js, use the new index.ts and update const plugins = ['xxx']
to include your plugin name(s)
Rename plugin-xxx.js to plugin-xxx.ts
import must be used instead of require for loading modules e.g.:
const Loki = require('lokijs') => import Loki from 'lokijs'
Use the new mandatory settings:
// start - mandatory plugin initialization
const ScimGateway: typeof import('scimgateway').ScimGateway = await (async () => {
try {
return (await import('scimgateway')).ScimGateway
} catch (err) {
const source = './scimgateway.ts'
return (await import(source)).ScimGateway
}
})()
const scimgateway = new ScimGateway()
const config = scimgateway.getConfig()
scimgateway.authPassThroughAllowed = false
// end - mandatory plugin initialization
Use the new config
object (mentioned above) which contains the scimgatway.endpoint
configuration having automated encryption/decryption of any attributes named password, secret, client_secret, token and APIKey
The old scimgateway.getPassword() is not normally not needed because of scimgateway automated config
logic. If needed, use the new scimgateway.getSecret().
Use the new logging syntax:
replace: scimgateway.logger.debug(`${pluginName}[${baseEntity}] xxx`)
with: scimgateway.logDebug(baseEntity, `xxx`)
Use scimgateway.HelperRest() for REST functionlity, also supports Auth PassThrough
scimgateway.endpointMapper() may be used for inbound/outbound attribute mappings
In general when using TypeScript, variables should be type defined: let isDone: boolean = false
, catch (err: any)
, ...
[Improved]
[Improved]
[Fixed]
plugin-ldap, using OpenLDAP in combination with endpoint user mapping "type":"array"
and "typeInbound":"string"
for handling comma separated SCIM string mapping towards an endpoint array/multivalue attribute, did not return correct sort order of the comma separated string when using OpenLDAP. Mapping example:
"<endpointAttr>": {
"mapTo": "<scimAttr>",
"type": "array",
"typeInbound": "string"
},
[Fixed]
[Improved]
[Fixed]
Note, plugin-ldap now has following new configuration:
"ldap": {
"isOpenLdap": false,
...
"namingAttribute": {
"user": [
{
"attribute": "CN",
"mapTo": "userName"
}
],
"group": [
{
"attribute": "CN",
"mapTo": "displayName"
}
]
},
...
}
isOpenLdap
true/false decides whether or not OpenLDAP Foundation protocol should be used for national characters and special characters in DN. For Active Directory, default isOpenLdap=false should be used.
namingAttribute
can now be linked to scim mapTo
attribute and is not hardcoded like it was in previous version.
Previous userNamingAttr
and groupNamingAttr
shown below, is now deprecated
"ldap": {
...
"userNamingAttr": "CN",
"groupNamingAttr": "CN",
...
}
[Fixed]
[Improved]
[Improved]
[Fixed]
[Fixed]
[Fixed]
[Improved]
endpointMapper supporting comma separated string to be converted to array, e.g.:
SCIM otherMails = "myAlias1@company.com,myAlias2@company.com,myAlias3@company.com"
endpointMapper configuration for endpoint attribute emails of type array:
"map": { "user": { "emails": { "mapTo": "otherMails", "type": "array", "typeInbound": "string" }, ...
[Improved]
[Improved]
egress/outbound only traffic
and get loadbalancing/failover by adding more gateways subscribing to same channel.[Improved]
[Fixed]
[Improved]
REST endpoint helpers-template
that includes tokenAuth
(now used by plugin-api). Auth PassTrhough also supported for oauth/tokenAuth endpoint[Improved]
meta.location
which contains protocol and hostname from request-url, will be excluded from response e.g. "{...,meta":{"location":"https://my-company.com/<...>"}}
. If using reverse proxy and not including headers X-Forwarded-Proto
and X-Forwarded-Host
, originator will be the proxy and we might not want to expose internal protocol and hostname being used by the proxy request.Below is an example of nginx reverse proxy configuration supporting SCIM Gateway ipAllowList and correct meta.location response:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
[Improved]
[Improved]
"convertRolesToGroups": false
"skipConvertRolesToGroups": false
[Improved]
[Fixed]
[Improved]
REST endpoint helpers-template
to address and resolve endpoint throttlingNote, module soap is not default included anymore. SOAP based plugins e.g., plugin-soap therefore needs npm install soap
for including module in your package
[Improved]
scimgateway.scim.port
can now be set to 0 or removed for deactivating listenercimgateway.scim.usePutSoftSync
set to true
now includes additional logic that do not change existing user attributes not included in PUT body content[Fixed]
[Improved]
[Improved]
Plugin can set error statusCode returned by scimgateway through error object key err.name
. This can be done by adding suffix #code
to err.name where code is HTTP status code e.g., err.name += '#401'
. This can be useful for auth.PassThrough and other scenarios like createUser where user already exist (409) and modifyUser where user does not exist (404)
This change replace statusCode logic introduced in v4.2.11
[Fixed]
[Fixed]
/ping
now excluded from info logs. If we want ping logging, use something else than lowercase e.g., /Ping
or /PING
[Improved]
lib/scimdef-v2.js (or scimdef-v1.js)
exists. Original scimdef-v2.js/scimdef-v1.js can be copied from node_modules/scimgateway/lib to your plugin/lib and customized.[Improved]
Note, obsolete - see v4.2.15 comments
"statusCode":xxx
where xxx is HTTP status code e.g., 401. Plugin using REST will have statusCode automatically included in error message thrown by plugin. This could be useful for auth.PassThrough.[Fixed]
[Fixed]
[Fixed]
[Improved]
PUT /Users/<user>
will replace user with body content. If body contains groups and usePutGroupMemberOfUser=true, groups will be set on user object (groups are member of user) instead of default user member of groups [Fixed]
[Fixed]
[Fixed]
[Improved]
[Fixed]
[Fixed]
[Fixed]
[Improved]
Kubernetes health checks and shutdown handler support
Plugin configuration prerequisite: kubernetes.enabled=true
"kubernetes": {
"enabled": true,
"shutdownTimeout": 15000,
"forceExitTimeout": 1000
}
Thanks to Kevin Osborn
[Improved]
Authentication PassThrough for passing the authentication directly to plugin without being processed by scimgateway. Plugin can then pass this authentication to endpoint for avoid maintaining secrets at the gateway.
Plugin configuration prerequisites: auth.passThrough.enabled=true
"auth": {
...
"passThrough": {
"enabled": true,
"readOnly": false,
"baseEntities": []
}
...
}
Plugin binary prerequisites:
scimgateway.authPassThroughAllowed = true
// also need endpoint logic for handling/passing ctx.request.header.authorization
For upgrading existing custom plugins, above mention prerequisites needs to be included and in addition all plugin methods must include the ctx
parameter e.g.:
scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx)
// tip, see provided example plugins
Thanks to Kevin Osborn
[Fixed]
log.loglevel.file
not defined or set to "off"
. This fix will allow SCIM Gateway to run on systems having read-only disk like Google Cloud App Engine Standard [Improved]
[Fixed]
[Improved]
payloadSize
. If not defined, default "1mb" will be used. There are cases which large groups could exceed default size and you may want to increase by setting your own size e.g. "5mb"[Fixed]
GET /Users
, scimgateway automatically adds groups if not included by plugin. This operation calls plugin getGroups having attributes=['members.value', 'id', 'displayName']. Now, members.value
is excluded. This attribute was in use and could cause unneeded load when having many group members. [Fixed]
config\resources\Azure - ScimGateway.xml
now using standard text on manager attribute instead of selection dialogbox.[Fixed]
config\resources\Azure - ScimGateway.xml
introduced in v.4.1.7 had some missing logic Note, this version breaks compability with previous versions of plugin-azure-ad
[Improved]
config\resources\Azure - ScimGateway.xml
for defining the Azure endpoint, have been updated with some new attributes according to plugin-azure-ad.json attribute mappings[Improved]
[Improved]
SCIM Gateway related news:
[Fixed]
addresses
did not correctly catch duplicate entries scim.usePutSoftsync=true
will also prevent removing any existing roles that are not included in body.roles ref. v4.1.3[Fixed]
[Improved]
PUT (Replace User) now includes group handling. Using configuration scim.usePutSoftsync=true
will prevent removing any existing groups that are not included in body.groups
Example:
PUT /Users/bjensen
{
...
"groups": [
{"value":"Employees","display":"Employees"},
{"value":"Admins","display":"Admins"}
],
...
}
[Improved]
endpointMapper supporting one to many mappings using a comma separated list of attributes in the mapTo
Configuration example:
"map": {
"user": {
"PersonnelNumber": {
"mapTo": "id,userName",
"type": "string"
},
...
}
}
[Improved]
plugin-ldap support userFilter/groupFilter configuration for restricting scope
Configuration example:
{
...
"userFilter": "(memberOf=CN=grp1,OU=Groups,DC=test,DC=com)(!(memberOf=CN=Domain Admins,CN=Users,DC=test,DC=com))",
"groupFilter": "(!(cn=grp2))",
...
}
[Improved]
Supporting OAuth Client Credentials authentication
Configuration example:
"bearerOAuth": [
{
"client_id": "my_client_id",
"client_secret": "my_client_secret",
"readOnly": false,
"baseEntities": []
}
]
In example above, client using SCIM Gateway must have OAuth configuration:
client_id = my_client_id
client_secret = my_client_secret
token request url = http(s)://<host>:<port>/oauth/token
[Improved]
create user/group supporting externalId
plugin-restful renamed to plugin-scim
plugin-ldap having improved SID/GUID support for Active Directory, also supporting domain map of userPrincipalName e.g. Azure AD => Active Directory
"userPrincipalName": {
"mapTo": "userName",
"type": "string",
"mapDomain": {
"inbound": "test.onmicrosoft.com",
"outbound": "my-company.com"
}
postinstall copying example plugins may be skipped by setting the property scimgateway_postinstall_skip = true
in .npmrc
or by setting environment SCIMGATEWAY_POSTINSTALL_SKIP = true
Secrets now also support key-value storage. The key defined in plugin configuration have syntax process.text.<path>
where <path>
is the file which contains raw (UTF-8) character value. E.g. configuration endpoint.password
could have value process.text./var/run/vault/endpoint.password
, and the corresponding file contains the secret. Thanks to Raymond Augé
[MAJOR]
getUsers()
replacing deprecated exploreUsers(), getUser() and getGroupUsers()getGroups()
replacing deprecated exploreGroups(), getGroup() and getGroupMembers()baseEntity
allowed for corresponding admin userNote, using this major version require existing custom plugins to be upgraded. If you do not want to upgrade your custom plugins, the old version have to be installed using: npm install scimgateway@3.2.11
How to upgrade your custom plugins:
Replace: scimgateway.exploreUsers = async (baseEntity, attributes, startIndex, count) => {
With: scimgateway.getUsers = async (baseEntity, getObj, attributes) => {
See comments in provided plugins regarding the new getObj
. Also note that attributes
is now an array and not a comma separated string like previous versions
In the very beginning, add:
// mandatory if-else logic - start
if (getObj.operator) {
if (getObj.operator === 'eq' && ['id', 'userName', 'externalId'].includes(getObj.attribute)) {
// mandatory - unique filtering - single unique user to be returned - correspond to getUser() in versions < 4.x.x
} else if (getObj.operator === 'eq' && getObj.attribute === 'group.value') {
// optional - only used when groups are member of users, not default behavior - correspond to getGroupUsers() in versions < 4.x.x
throw new Error(`${action} error: not supporting groups member of user filtering: ${getObj.rawFilter}`)
} else {
// optional - simpel filtering
throw new Error(`${action} error: not supporting simpel filtering: ${getObj.rawFilter}`)
}
} else if (getObj.rawFilter) {
// optional - advanced filtering having and/or/not - use getObj.rawFilter
throw new Error(`${action} error: not supporting advanced filtering: ${getObj.rawFilter}`)
} else {
// mandatory - no filtering (!getObj.operator && !getObj.rawFilter) - all users to be returned - correspond to exploreUsers() in versions < 4.x.x
}
// mandatory if-else logic - end
In the new getUsers() replacing exploreUsers() "as-is", we then need some logic in the last "else" statement listed above.
We also need to add logic from existing getGroup() and getGroupMembers()
Please have a look at provieded plugins to see different ways of doing this logic.
Replace: scimgateway.exploreGroups = async (baseEntity, attributes, startIndex, count) => {
With: scimgateway.getGroups = async (baseEntity, getObj, attributes) => {
In the very beginning, add:
// mandatory if-else logic - start
if (getObj.operator) {
if (getObj.operator === 'eq' && ['id', 'displayName', 'externalId'].includes(getObj.attribute)) {
// mandatory - unique filtering - single unique user to be returned - correspond to getUser() in versions < 4.x.x
} else if (getObj.operator === 'eq' && getObj.attribute === 'members.value') {
// mandatory - return all groups the user 'id' (getObj.value) is member of - correspond to getGroupMembers() in versions < 4.x.x
// Resources = [{ id: <id-group>> , displayName: <displayName-group>, members [{value: <id-user>}] }]
} else {
// optional - simpel filtering
throw new Error(`${action} error: not supporting simpel filtering: ${getObj.rawFilter}`)
}
} else if (getObj.rawFilter) {
// optional - advanced filtering having and/or/not - use getObj.rawFilter
throw new Error(`${action} error: not supporting advanced filtering: ${getObj.rawFilter}`)
} else {
// mandatory - no filtering (!getObj.operator && !getObj.rawFilter) - all groups to be returned - correspond to exploreGroups() in versions < 4.x.x
}
// mandatory if-else logic - end
In the new getGroups() replacing exploreGroups() "as-is", we then need some logic in the last "else" statement listed above.
We also need to add logic from existing getGroup() and getGroupMembers()
Please have a look at provieded plugins to see different ways of doing this logic.
Delete deprecated exploreUsers(), getUser(), getGroupUsers(), exploreGroups(), getGroup() and getGroupMembers()
[Fixed]
[Fixed]
[Improved]
[Fixed]
[Fixed]
objectGUID
introduced in v.3.2.7 had some missing logic [Improved]
plugin-ldap supports using Active Directory objectGUID
instead of dn
mapped to id
configuration example:
"objectGUID": {
"mapTo": "id",
"type": "string"
}
[Fixed]
[Fixed]
[Fixed]
scim.skipTypeConvert = true
[Improved]
scim.skipTypeConvert
allowing overriding the default behaviour "type converted object" when set to true. See attribute list for details scimgateway.isMultivalue
used by plugin-loki have been changed, and custom plugins using this method must be updated
old syntax:
scimgateway.isMultivalue('User', key)
new syntax:
scimgateway.isMultiValueTypes(key)
[Fixed]
[Fixed]
find
instead of findOne
to ensure returning unique user [Fixed]
groups
(when "user member of groups"
) e.g. GET /Users/bjensen should return all user attributes including the virtual groups
attribute. Now this user attribute will be automatically handled by scimgateway if not included in the plugin response. [Improved]
[Fixed]
[Improved]
[Improved]
ipAllowList for restricting access to allowlisted IP addresses or subnets e.g. Azure AD IP-range
Configuration example:
"ipAllowList": [
"13.66.60.119/32",
"13.66.143.220/30",
...
"2603:1056:2000::/48",
"2603:1057:2::/48"
]
Example plugins now configured for SCIM v2.0 instead of v1.1
New configuration:
"scim": {
"version": "2.0"
}
Old configuration:
"scim": {
"version": "1.1"
}
[Improved]
pre_post_Action
[Fixed]
[Fixed]
[Fixed]
[Improved]
[Fixed]
[Fixed]
[Improved]
getApi supports body (apiObj).
Old syntax:
scimgateway.getApi = async (baseEntity, id, apiQuery) => {
New syntax:
scimgateway.getApi = async (baseEntity, id, apiQuery, apiObj) => {
[MAJOR]
scimgateway.scim.customUniqueAttrMapping
- replaced by getObj logic[UPGRADE]
Note, this is a major upgrade (^2.x.x => ^3.x.x) that will brake compatibility with any existing custom plugins. To force a major upgrade, suffix @latest
must be include in the npm install command, but it's recommended to do a fresh install and copy any custom plugins instead of upgrading an existing package
Old syntax:
scimgateway.getUser = async (baseEntity, userName, attributes) => {
scimgateway.getGroup = async (baseEntity, displayName, attributes) => {
scimgateway.modifyGroupMembers = async (baseEntity, id, members) => {
New syntax:
scimgateway.getUser = async (baseEntity, getObj, attributes) => {
const userName = getObj.identifier // gives v2.x compatibility
scimgateway.getGroup = async (baseEntity, getObj, attributes) => {
const displayName = getObj.identifier // gives v2.x compatibility
scimgateway.modifyGroup = async (baseEntity, id, attrObj) => {
// attrObj.members corresponds to members in deprecated modifyGroupMembers
getUser comments:
getObj = { filter: <filterAttribute>, identifier: <identifier> }
e.g: getObj = { filter: 'userName', identifier: 'bjensen'}
filter: userName and id must be supported
getGroup comments:
getObj = { filter: <filterAttribute>, identifier: <identifier> }
e.g: getObj = { filter: 'displayName', identifier: 'GroupA' }
filter: displayName and id must be supported
Please see provided example plugins
Using the new getObj parameter gives more flexibility in the way of lookup a user e.g:
http://localhost:8880/Users?filter=emails.value eq "jsmith@example.com"&attributes=userName,name.givenName
getObj = { filter: 'emails.value', identifier: 'jsmith@example.com'}
attributes = 'userName,name.givenName'
Configuration file, auth settings have changed and now using arrays allowing more than one user/object to be set. "readOnly": true
can also be set for allowing read only access for a spesific user (does not apply to bearerJwtAzure).
New syntax is:
"auth": {
"basic": [
{
"username": "gwadmin",
"password": "password",
"readOnly": false
}
],
"bearerToken": [
{
"token": null,
"readOnly": false
}
],
"bearerJwtAzure": [
{
"tenantIdGUID": null
}
],
"bearerJwt": [
{
"secret": null,
"publicKey": null,
"options": {
"issuer": null
},
"readOnly": false
}
]
}
[Fixed]
[Fixed]
[Improved]
"customMasking": ["SSN", "weight"]
Note, configurationfiles must be changed (old syntax still supported)
old syntax:
"loglevel": {
"file": "debug",
"console": "error"
},
new syntax:
"log": {
"loglevel": {
"file": "debug",
"console": "error"
},
"customMasking": []
},
By default SCIM Gateway includes masking of standard attributes like password
[Fixed]
Thanks to Luca Moretto
[Fixed]
[Fixed]
url.parse
[Fixed]
err.name = 'DuplicateKeyError'
) when failing on creating a duplicate user[Improved]
[Fixed]
err.name = 'DuplicateKeyError'
now gives correct status code 409 instead of defult 500 (see plugin-loki.js) [Fixed]
loglevel.colorize
is not needed)[Fixed]
[Improved]
[Fixed]
[Improved]
scim.customSchema
having value set to filename of a JSON schema-file located in <package-root>/config/schemas
[UPGRADE]
Configurationfiles for custom plugins should be changed
old syntax:
"scimversion": "1.1",
new syntax:
"scim": {
"version": "1.1",
"customSchema": null
},
Note, "1.1" is default, if using "2.0" the new syntax must be used.
[Fixed]
[MAJOR]
[UPGRADE]
Note, this is a major upgrade (^1.x.x => ^2.x.x) and will brake compatibility with any existing custom plugins. To force a major upgrade, suffix @latest
must be include in the npm install command, but it's recommended to do a fresh install and copy any custom plugins instead of upgrading an existing package
cd c:\my-scimgateway
npm install scimgateway@latest
Custom plugins needs some changes (please see included example plugins)
scimgateway.on(xxx, function (..., callback)
replaced with scimgateway.xxx = async (...)
returning a result or throwing an errordoRequest
method having endpoint failover logic through array baseUrls/baseServiceEndpoints
defined in corresponding plugin configuration file. attributes
included in exploreUsers and exploreGroups method[Fixed]
[UPGRADE]
getUser
and getGroup
methods needs to be updated. If user/group not found then return callback(null, null)
instead of callback(err) [Fixed]
[Improved]
[UPGRADE]
Configurationfiles for custom plugins should be changed
old syntax:
loglevel: "debug"
new syntax:
"loglevel": {
"file": "debug",
"console": "error",
"colorize": true
}
[Fixed]
[Fixed]
[UPGRADE]
[Fixed]
[Fixed]
[Fixed]
[Improved]
[Improved]
[UPGRADE]
[Improved]
[Fixed]
[Improved]
[Fixed]
Thanks to ywchuang
[Improved]
[Fixed]
[Fixed]
[Fixed]
[Fixed]
[Improved]
[UPGRADE]
Method getGroupMembers
must be updated for all custom plugins
Replace:
scimgateway.on('getGroupMembers', function (baseEntity, id, attributes, startIndex, count, callback) {
...
let ret = {
'Resources' : [],
'totalResults' : null
}
...
ret.Resources.push(userGroup)
...
callback(null, ret)
With:
scimgateway.on('getGroupMembers', function (baseEntity ,id ,attributes, callback) {
...
let arrRet = []
...
arrRet.push(userGroup)
...
callback(null, arrRet)
[Improved]
[Improved]
[UPGRADE]
config/plugin-xxx.json
needs to be updated regarding the new scimgateway.auth
section:
[Improved]
config\docker
includes docker configuration examples[Improved]
SEED
overrides default password seeding "process.env.XXX"
lets environment variable XXX define the port [UPGRADE]
config/plugin-xxx.json
needs to be updated:
[Improved]
plugin-loki
plugin-testmode
[UPGRADE]
lib/plugin-testmode.js
and config/plugin-testmode.json
[Fixed]
[Improved]
[Fixed]
[Improved]
[UPGRADE]
[Fixed]
[Improved]
[Improved]
[UPGRADE]
scimgateway.on('createGroup',...
Please see example plugins for details. [Fixed]
[Improved]
plugin-mssql
scimgateway.getArrayObject
[Fixed]
[Fixed]
[UPGRADE]
scimgateway.on('getGroupMembers',...
New arguments have been added "startIndex" and "count". Also a new return variable "ret". Please see example plugins for details.[Fixed]
[Improved]
plugin-restful
[Improved]
<Base URL>/[baseEntity]
for multi tenant or multi endpoint flexibility [UPGRADE]
[Doc]
[Fixed]
Initial version