PropelAuth Logo
← Back to Blog

Adding SCIM to your product

You've already implemented Enterprise SSO. Your customers can log in with Okta, Entra, or their Identity Provider (IdP) of choice. But now they're asking:

  • "When someone leaves, their access needs to be revoked immediately"
  • "Can you automatically provision users when they're added to our IdP?"
  • "We need user groups synced from Entra"

What they're asking for is SCIM (System for Cross-domain Identity Management) - automated user provisioning and deprovisioning that ensures their employee directory stays perfectly in sync with your application.

What is SCIM and why do you need it?

Enterprise SSO only handles the login process. It doesn't tell you when someone joins the company, changes departments, or leaves the company. Without SCIM, you won't know a user should be deactivated until they try (and fail) to log in next time.

SCIM automatically syncs user information between your customer's IdP and your application. When someone joins your customer's company and gets added to Okta, they're automatically created in your product. When they leave and IT disables their account, they're immediately locked out of your product too.

The value proposition is clear: your customers' IT teams manage users in one place (their IdP), and those changes automatically propagate to all their SaaS tools, including yours.

How does SCIM work?

Conceptually, SCIM is actually pretty simple. Your customer's IdP (Okta, Entra, etc.) will make API calls to your application to keep users in sync. These API calls do things like:

  • Fetch the current list of provisioned users
  • Create new users
  • Update user information when it changes
  • Deactivate or delete users when they're removed

The challenge isn't the concept, it's the implementation. SCIM has a very specific format for these API requests and responses. The specification itself is detailed but also somewhat vague, and each IdP tends to implement it slightly differently. Some use PUT requests with the full user object, others use PATCH with only changed fields. Sometimes those patches provide a field and a value, other times they provide a JSON object and no path, but instructions like replace or remove.

Let's break down the key parts of adding SCIM support to your product.

Adding SCIM Support to your product

There are two main parts to adding SCIM support:

  1. Providing an API key or token for authentication
  2. Setting up the SCIM endpoints to handle provisioning requests

Providing a SCIM API key

The SCIM endpoints you're about to create are sensitive - they can create, update, and delete users in your application. You need to ensure only authorized requests from your customer's IdP can access them.

The standard approach is to provide your customer with an API key, which they enter into their IdP's SCIM configuration. This key is then included in the Authorization header of each request to your SCIM endpoint.

Here's where your customers would enter the API key in Okta:

okta scim api key setup

With PropelAuth BYO, we handle all the API key generation, storage, and validation for you. If you build it yourself, make sure to securely store the keys and validate them on each request.

Setting up the SCIM endpoints

Your customers will also need a "SCIM Base URL" - the root URL where your SCIM endpoints live. For example: https://api.yourapp.com/scim/v2/. The IdP will make requests to endpoints like:

  • GET /scim/v2/Users - List users
  • POST /scim/v2/Users - Create a new user
  • GET /scim/v2/Users/{id} - Get a specific user
  • PUT /scim/v2/Users/{id} - Replace a user's information
  • PATCH /scim/v2/Users/{id} - Update specific fields of a user
  • DELETE /scim/v2/Users/{id} - Delete a user
  • Similar endpoints for /Groups if you support group syncing

You can see the full specification in the SCIM 2.0 standard and SCIM Protocol. One thing to note is that most IdPs don't implement the full spec. Filters, for example, can technically be complex and invliolve multiple attributes and logical operators, but in practice, most IdPs only use simple equality checks on a single attribute (like userName eq "example@acme.com").

If you use PropelAuth BYO, you can forward any SCIM request to us, and we'll return the exact response to send back to the IdP, plus any critical actions you need to take in your system. Our goal is to standardizes the SCIM implementation across all IdPs as best we can.

Matching SCIM users to SSO users

Here's where things get tricky. You already have users logging in via SSO, and now SCIM is creating/updating/deleting users. How do you match them up?

You'd think SSO and SCIM would use the same unique identifiers, but that's often not the case. Different IdPs use different default configurations, and everything is customizable.

There are two common approaches for handling this:

Match on email address: Simple, works across all IdPs, and easy to understand. However, email addresses can change, which can cause matching issues.

Configure the externalId to match the SSO ID: SCIM's externalId field is an optional field that's meant to be an immutable unique identifier. You can provide instructions to your customers on configuring this to match their SSO subject claim. More robust than email matching, but requires customer configuration.

As auth people, we generally recommend using the externalId approach, but there are a lot of products that do the first one and just add a warning about email changes.

With PropelAuth BYO, we provide a UI that lets you see the full user object coming from SCIM to help you debug possible matching issues.

byo scim user object

Decisions to make when implementing SCIM

Just-in-time provisioning vs pre-provisioning

When you set up SSO without SCIM, users are typically created "just-in-time" (JIT), meaning that you create the user when they log in for the first time.

One of the key points of SCIM, however, is to allow your customers to provision users before they log in. And this raises an important question: what happens when a user logs in but is NOT provisioned via SCIM?

This may seem like a weird edge case, but it happens somewhat commonly. Here's a few examples that come up a lot:

  • Azure / Entra SCIM syncs are not immediate. You'll often see a 30-40 minute delay between when a user is given access to your product in Entra and when SCIM sends you a provision request. If that user tries to log in during that window, they'll be allowed to login (as they do have access), but you won't have a SCIM record for them yet.
  • If you are using Okta for OIDC and SCIM, you need to have two separate applications in Okta (SAML and SCIM doesn't have this issue, but that's a whole separate discussion). If your customer gives a user access via the SSO application but not the SCIM application, that user will be able to log in, but won't be provisioned via SCIM.

How you handle this is up to you. Some products will choose to reject logins for users that aren't provisioned via SCIM (the argument being "all provisioning is now SCIM provisioning"). Others will allow JIT provisioning to continue working alongside SCIM provisioning (the argument being "if the user can log in, they should be able to").

For my personal take, it's pretty reasonable to allow JIT provisioning to continue working, if for no other reason than it avoids the "I gave my employee access in Entra but they still can't log in" support tickets.

Supporting custom properties / attributes

If I have any specific grievances with the SCIM spec, it's that they didn't do a great job of explaining how to handle custom attributes. There are a core set of attributes (email, name, active, etc.) and even extensions for enterprise-specific attributes, but many products need to support custom attributes specific to their domain.

The good news here is that every IdP does have support for custom attributes, the real question is just where your users should put them.

There are two main approaches here:

Send the custom attributes as top level fields: Want to collect a favoriteSport attribute? Just have your customer map that attribute in their IdP to a top-level favoriteSport field in SCIM. It's a pretty straightforward approach, but it does clutter up the top-level namespace. We've also seen a cases where customers have built their own SCIM clients that don't expect custom fields, leading to errors.

Send the custom attributes in an extension schema: SCIM supports the concept of extension schemas, which are basically custom namespaces for additional attributes. You can define your own schema (e.g. urn:ietf:params:scim:schemas:extension:YOUR_PRODUCTS_NAME_HERE:2.0:User) and have your customers map their custom attributes into that schema. This keeps things more organized, but requires a bit more setup on both sides.

The best approach is to define your own extension, meaning customers will send custom attributes like this:

{
    //...
    "urn:ietf:params:scim:schemas:extension:YOUR_PRODUCTS_NAME_HERE:2.0:User": {
        "favoriteSport": "soccer"
    }
}

Some customers will still accidentally send them as top-level fields, so be prepared to handle both cases if you want to be flexible.

For PropelAuth BYO, we have a detailed guide on handling custom attributes via SCIM, which also covers features like fallbackPaths (allowing you to specify multiple possible paths for an attribute) and Warn if missing which can warn you if a customer isn't sending an important attribute.

scim ui custom attributes

Delete vs Deactivate

We've covered most of the complexities of SCIM, but one final thing to call out is that deletion and deactivation are not the same thing.

Deactivating a user (literally, setting the active field to false), means the user still exists in your system, but can't log in. This is a reversible operation. Later on, users can be reactivated and ideally retain all of their data and settings.

Deleting a user, on the other hand, is a permanent operation. The user is removed from your system entirely, and all of their data is typically deleted as well.

Entra / Azure, for example, will initially just deactivate users via SCIM. After a grace period, they will then send a delete request. This is handy to avoid accidental data loss, but it's just something to keep in mind in case a customer writes in about it.

PropelAuth BYO makes adding SCIM simple

PropelAuth BYO is a self-hostable authentication sidecar that makes adding SCIM to your product easy. You keep your existing auth system and we handle the SCIM complexity for you.

All you have to do is set up a single catch-all route (e.g. /scim/v2/*) and forward all SCIM requests to the PropelAuth BYO sidecar. We'll return to you the exact response to return back to the IdP, plus any critical actions you need to take in your system (like creating or deleting users).

You also get:

  • Pre-built SCIM endpoints that work with all major IdPs
  • Automatic handling of IdP-specific quirks
  • API key management
  • Detailed logs of all SCIM operations

Check out PropelAuth BYO to add SCIM without the complexity.

Related Posts