docs/documentation/platform/scim/pingfederate.mdx
If you're using Infisical Cloud, then it is available under the **Enterprise Tier**. If you're self-hosting Infisical,
then you should contact [email protected] to purchase a self-hosted license to use it.
Prerequisites:
**1. Download the connector**
Sign in at [pingidentity.com](https://www.pingidentity.com) with the email tied to your PingFederate license, then
open the [PingFederate downloads page](https://www.pingidentity.com/en/resources/downloads/pingfederate.html).
Click the **Add-ons** tab. Under **SaaS Connectors**, download the **SCIM Connector** `.zip`.

**2. Install the connector**
Extract the `.zip`. Inside you'll find a `dist/` directory containing a single jar named
`pf-scim-quickconnection-<version>.jar`.
Stop PingFederate and copy that jar into `<pf-install>/server/default/deploy/`. Leave PingFederate stopped — the next step edits another config file.
</Step>
<Step title="Enable the Outbound Provisioning runtime in PingFederate">
PingFederate ships with the provisioning runtime **disabled** by default. You must enable it before any SCIM channel
will run.
Edit `<pf-install>/bin/run.properties` and change:
```properties
pf.provisioner.mode=OFF
```
to:
```properties
pf.provisioner.mode=STANDALONE
```
Use `STANDALONE` for a single-node install. For high availability, see Ping's [Deploying provisioning failover](https://docs.pingidentity.com/pingfederate/13.0/server_clustering_guide/pf_deploy_provis_failover.html) guide.
Start PingFederate.
<Info>
To verify the SCIM Connector is now available, go to **Applications → Integration → SP Connections → Create Connection → Connection Type tab**, check **Outbound Provisioning**, and open the **Type** dropdown — you should see **SCIM 2.0 Service Provider** alongside the bundled SCIM 1.1 entry. If you only see SCIM 1.1, check PingFederate's `server.log` for connector load errors. Cancel out of the wizard once you've confirmed; you'll return to it in a later step.

</Info>
</Step>
<Step title="Configure the Provisioner Data Store">
PingFederate persists provisioning state (last-sync timestamps, externalId mappings, etc.) in a dedicated database.
This is **separate** from your user source — it's PingFederate's internal bookkeeping. The provisioner data store
must be a JDBC database; LDAP data stores cannot be used for this purpose.
1. Create an empty schema in your RDBMS for PingFederate's provisioner state.
2. Run the schema script that matches your RDBMS against that schema. The scripts ship with PingFederate and live at `<pf-install>/server/default/conf/provisioner/sql-scripts/`:
- `provisioner-mssql.sql` — SQL Server
- `provisioner-mysql.sql` — MySQL
- `provisioner-oracle.sql` — Oracle
- `provisioner-postgresql.sql` — PostgreSQL
3. Install the JDBC driver for your database. PingFederate only bundles the H2 driver — other databases require you to download the driver `.jar` from the vendor and copy it into `<pf-install>/server/default/lib/`, then restart PingFederate. If you skip this step, the Add Data Store step will fail with "You must provide a valid driver class name" even when the class name is correct.
4. In the PingFederate admin console: **System → Data Stores → Add Data Store → JDBC**. Configure the connection and save.




5. Go to **System → Server → Protocol Settings → Outbound Provisioning** and select the JDBC data store you just created as the **Provisioner Data Store**. Save.

Leave the **Synchronization Frequency** at the default 60 seconds unless you have a reason to change it.
</Step>
<Step title="Add your LDAP directory as a Data Store">
PingFederate's SCIM channel pulls users from a **source** Data Store and provisions them outbound to Infisical.
Most deployments use Active Directory as the source — these steps assume AD or another LDAP directory. If you
provision users from a relational database instead, see the JDBC note at the end of this step.
In the PingFederate admin console: **System → Data Stores → Add New Data Store** → select **LDAP**.


- **LDAP Type**: select **Active Directory** (or **Generic** / **Oracle Directory Server** / etc. for non-AD directories). The LDAP Type affects which attribute behaviors PingFederate assumes (binary handling for AD's `objectGUID`, etc.) — picking the wrong one will silently produce incorrect change detection.
Fill out the Data Store form:

- **Hostname**: your AD / LDAP server (e.g. `ad.corp.example.com`). For HA, add multiple hosts. Click **Add** to commit each hostname into the list.
- **Use LDAPS** (recommended) or **Use StartTLS**: enable one if your directory supports TLS. If using LDAPS, ensure the directory's CA certificate is trusted by PingFederate (Security → Certificate & Key Management → Trusted CAs).
- **Authentication Method**: Simple.
- **User DN**: a service account DN with read access to the user subtree, e.g. `CN=pf-svc,OU=Service Accounts,DC=corp,DC=example,DC=com`.
- **Password**: the service account password.
Click **Test Connection**. PingFederate should return "Connection Successful." If not, the bind credentials or
network reachability is wrong — fix that before continuing.
Save the Data Store.

<Note>
**Using JDBC instead?** Choose **Database** rather than **LDAP** at the Add Data Store step and configure the
JDBC URL, driver, and credentials for your user table. The rest of this guide is otherwise identical, except
the source attribute names in the channel mapping table (Step 8) will be your **column names** instead of
LDAP attribute names.
</Note>
</Step>
<Step title="Generate a SCIM token in Infisical">
In Infisical, head to the **Organization Settings** page and select the **Provisioning** tab. Under SCIM Configuration,
press the **Enable SCIM provisioning** toggle to allow PingFederate to provision/deprovision users for your organization.

Press **Configure** and then **Create** to generate a SCIM token for PingFederate.

Copy the **SCIM URL** and **New SCIM Token** — you'll paste them into PingFederate's SP Connection in a later step.
The token is shown once and cannot be retrieved again.

</Step>
<Step title="Create the SP Connection">
Navigate to **Applications → Integration → SP Connections** and click **Create Connection**.

**1. Connection Template tab**
Select **USE A TEMPLATE FOR THIS CONNECTION** → from the dropdown, pick **SCIM Connector** → Next.

**2. Connection Type tab**
- Check **Outbound Provisioning**.
- The **Type** dropdown that appears next to Outbound Provisioning should say **SCIM Connector**.
- Click Next.

**3. General Info tab**
- **Partner's Entity ID (Connection ID)**: e.g. `infisical-scim`.
- **Connection Name**: e.g. `Infisical SCIM`.
- Leave the rest (Base URL, Company, Contact, Application metadata) empty or default.
- Click Next.

**4. Outbound Provisioning tab**
Click **Configure Provisioning** — this opens the Target / Channel configuration wizard, which we cover in the next two steps.

</Step>
<Step title="Configure the SCIM target">
Inside the Configure Provisioning wizard, the first tab is **Target** — this is where you tell PingFederate how to reach Infisical.
| Field | Value |
|---|---|
| **SCIM URL** | The SCIM URL you copied from Infisical in Step 5 |
| **SCIM Version** | `2.0` |
| **Authentication Method** | `OAuth 2 Bearer Token` |
| **Access Token** (under OAUTH 2 BEARER TOKEN) | The SCIM token you copied from Infisical |
| **Unique User Identifier** | `userName` |
| **Filter Expression** | `userName eq "%s"` |
| **Authorization Header Type** | leave blank (defaults to `Bearer`) |
| **Users API Path / Groups API Path** | leave blank (defaults to `/Users` and `/Groups`) |
| **Results Per Page** | `1000` |
| **Provisioning Options → User Create / User Update / User Disable-Delete** | all checked |
| **Provision Disabled Users** | unchecked |
| **Remove User Action** | `Disable` for a first run (changes deprovisioned users to inactive in Infisical rather than hard-deleting them). Switch to `Delete` later if you want true hard deprovisioning. |
| **Group Name Source** | `Common Name` |
| **Use PATCH for Group Updates** | Optional, but recommended if you provision groups. |
Leave the Basic Authentication and OAuth 2 Client Credentials sections empty — only OAuth 2 Bearer Token is used.


<Warning>
**The SCIM URL host must be reachable from the PingFederate runtime, not from your browser.** If PingFederate
runs on a different host than Infisical, make sure the hostname in the SCIM URL is one PingFederate's container
or server can actually resolve and connect to — not just one you can hit from your laptop. If you see
"Connection refused" errors in PingFederate's `provisioner.log`, this is almost always why.
</Warning>
Click Next to move on to the channel configuration.
</Step>
<Step title="Configure the SCIM channel">
On the Manage Channels tab, click **Create**. This opens the per-channel wizard with six tabs.

**1. Channel Info**
- **Channel Name**: `infisical-users` (or any identifier).
- **Max Threads**: `1` for dev, increase for production as needed.
- **Timeout (Secs)**: `60` (default).

**2. Source**
- **Active Data Store**: select the LDAP data store you configured in Step 4.

**3. Source Settings**
This tab tells PingFederate how to detect changes and identify users in your LDAP source.
| Field | Value (AD) | Value (Generic LDAP / OpenLDAP) |
|---|---|---|
| **Entry GUID Attribute** | `objectGUID` | `entryUUID` |
| **GUID Type** | `Binary` | `Text` |
| **Member of Group Attribute** | `memberOf` | `memberOf` (if the `memberof` overlay is enabled) — otherwise leave blank |
| **Group Member Attribute** | `member` | `member` |
| **User Objectclass** | `user` | `inetOrgPerson` |
| **Group Objectclass** | `group` | `groupOfNames` |
| **Changed Users/Groups Algorithm** | `USN` (AD-specific) or `Timestamp` | `Timestamp` |
| **USN Attribute** (only if USN algorithm) | `uSNChanged` | n/a |
| **Timestamp Attribute** (only if Timestamp algorithm) | `whenChanged` | `modifyTimestamp` |
| **Account Status Attribute** | `userAccountControl` | varies by directory (e.g. `pwdAccountLockedTime` if password policy overlay) |
| **Account Status Algorithm** | `Flag` | `Flag` |
| **Default Status** | `true` (users active by default) | `true` |
| **Flag Comparison Value** | `514` (the disabled bit) | varies |
| **Flag Comparison Status** | `false` (when flag matches, user is inactive) | `false` |


<Note>
**At least one of Member of Group Attribute or Group Member Attribute is required**, even if you're not actually provisioning groups. Fill in `member` for Group Member Attribute as a safe default if you don't plan to use the values.
</Note>
<Warning>
**With the `Active Directory USN` algorithm, point the LDAP data store at a single domain controller.** `uSNChanged` is a per-DC counter that AD does **not** replicate, so if PingFederate spreads queries across multiple DCs (e.g. the multi-host HA setup in Step 4) it can read a different DC than the one whose USN it last recorded and silently miss changes. For a multi-DC / HA source, either pin the data store to one DC, or switch **Changed Users/Groups Algorithm** to `Timestamp` with **Timestamp Attribute** `whenChanged` (which *is* replicated).
</Warning>
**4. Source Location**
- **Base DN**: the search root for *both* users and groups — it must contain both (use a common ancestor like the domain root if they're in different OUs). e.g. `CN=Users,DC=corp,DC=example,DC=com` for AD.
- **Users → Group DN**: leave blank, or set to a group to provision only its members.
- **Users → Filter**: AD `(&(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))` (enabled users); OpenLDAP `(objectClass=inetOrgPerson)`.
- **Groups → Filter** (only if provisioning groups): AD `(objectClass=group)`; OpenLDAP `(objectClass=groupOfNames)` — **scope it** (e.g. `(&(objectClass=group)(cn=infisical-*))`) or you'll also pull AD's built-in groups. Leave **Group DN** and **Nested Search** empty (Infisical doesn't support nested groups).

<Note>
Infisical groups are flat — members are users only (nested groups are ignored), and a member resolves only if that user was also provisioned, so keep group members in your **Users** scope. The group's CN (set by **Group Name Source**, Step 7) is what you map under **Settings → Provisioning → SCIM Group to Organization Role Mappings**.
</Note>
**5. Attribute Mapping**
Click **Edit** on each SCIM field below. The edit form has two dropdowns: **Root Object Class** and **Attribute**.
<Note>
**Attribute mapping trap**: the Root Object Class dropdown only shows attributes defined *directly* on that
class — it does **not** show inherited attributes. For LDAP, `sn` is defined on the `person` class and inherited
by `inetOrgPerson`. So mapping `familyName` requires selecting Root Object Class = `person`, not `inetOrgPerson`.
If the attribute you expect is missing from the dropdown, try a parent object class.
</Note>
The minimum set Infisical requires:
| SCIM Field | Root Object Class (AD) | Attribute (AD) | Root Object Class (LDAP) | Attribute (LDAP) |
|---|---|---|---|---|
| `userName` | `user` | `userPrincipalName` (or `mail`) | `inetOrgPerson` | `mail` |
| `workEmail` | `user` | `mail` | `inetOrgPerson` | `mail` |
| `givenName` | `user` | `givenName` | `inetOrgPerson` | `givenName` |
| `familyName` | `user` | `sn` | `person` | `sn` |
For each row: click **Edit** → pick **Root Object Class** → pick **Attribute** → click **Add Attribute** → click **Done**.
`active` doesn't need an explicit mapping — PingFederate derives it from the Account Status Attribute settings in Source Settings.


<Note>
The `userName`'s email domain **must be a verified email domain** for your Infisical organization
(**Settings → Organization → Email Domains**). If it isn't, SCIM user creation is **rejected** with a
`403 – "…not a part of the accepted domains…"` and the user is **not** created — groups have no such check, so
you'll see the group sync without its members. Verify the domain **before** provisioning.
</Note>
**6. Activation & Summary**
- Set **Channel Status** to `Active`.
- Scroll through the summary and confirm Source Settings, Source Location, and Attribute Mapping all reflect what you intended.
- Click **Done**.


</Step>
<Step title="Activate the SP Connection">
After saving the channel you'll be back on Configure Channels. Click **Done** to close that screen.

On the parent SP Connection wizard's **Outbound Provisioning** tab, click **Next**.

On **Activation & Summary**:
- Set **Connection Status** to `Active`.
- Review the summary.
- Click **Save**.


<Warning>
**Both the channel AND the parent SP Connection must be Active.** If either one is Inactive, provisioning silently does nothing — there's no error in the UI. Double-check both before troubleshooting other things.
</Warning>
</Step>
<Step title="Verify provisioning works">
PingFederate runs the channel on the cadence set in **System → Server → Protocol Settings → Outbound Provisioning → Synchronization Frequency** (default 60 seconds). The first sync should run within that window of activation.
Verify on three levels:
**1. PingFederate's provisioner log**
Tail `<pf-install>/log/provisioner.log`. Look for lines like:
```
[GenericUserProvisioningChannel] Starting provisioning cycle for channel: infisical-users
[ProvisioningEventLogger] Finished pushing user/group updates to the target. Completed in 331 ms. Total users created 2.
```
If you see `Connection refused` or `Service Unavailable` errors, the SCIM URL host isn't reachable from PingFederate — re-check Step 7.
**2. Infisical's user list**
Go to **Settings → Organization → Access Control → Users**. Provisioned users appear as pending (invited) accounts.
**3. Infisical's SCIM event log**
Go to **Organization Settings → Provisioning → SCIM Events**. Each successful POST/PUT/PATCH from PingFederate appears here with the SCIM payload — useful for diagnosing attribute-mapping mistakes.
</Step>
- The SCIM URL uses `localhost` or `127.0.0.1` but PingFederate runs in a different container or on a different host.
- DNS doesn't resolve the host from PingFederate's network zone.
- A firewall is blocking outbound HTTPS from PingFederate to Infisical.
Fix the SCIM URL on the **Target** tab (SP Connection → Outbound Provisioning → Configure Provisioning → Target). The next sync (within the configured frequency) will use the new URL.
To diagnose: search PingFederate's `provisioner.log` for `Processed N users` and `Users added: N` lines from the most recent cycle. A zero `Users added` count means the filter is too restrictive or change-detection thinks nothing has changed.
To force a re-provision: delete the entry for that channel from the Provisioner Data Store's tracking tables, then trigger a new sync. (For development only — never do this in production.)