Your B2B SaaS supports SSO. Enterprise prospects can log in with Azure Entra ID or Auth0. But their IT admin still has to manually create user accounts in your application. That's where SCIM comes in.
What is SCIM?
SCIM (System for Cross-domain Identity Management) is an open standard for automating user provisioning. It defines a REST API that identity providers like Azure Entra ID and Auth0 call to create, update, and deactivate user accounts in your application.
The current version is SCIM 2.0, defined in RFC 7642, RFC 7643, and RFC 7644. It specifies:
- User and Group resources with a standard JSON schema
- CRUD operations via REST endpoints (
/Users,/Groups) - Filtering and pagination for listing resources
- Bulk operations for initial provisioning of large user bases
Why enterprise customers require it
Without SCIM, this is what happens when a company with 2,000 employees adopts your SaaS:
Without SCIM
- IT admin manually creates 2,000 accounts (or uploads a CSV, if you built that)
- Employee joins the company → IT admin remembers to add them to your app
- Employee changes department → IT admin remembers to update their role
- Employee leaves → IT admin remembers to deactivate their account
- Security audit asks "how do you ensure offboarded employees lose access?" → "We hope the IT admin remembers"
With SCIM
- IT admin configures SCIM provisioning once in Azure Entra ID or Auth0
- Employee joins → automatically provisioned in your app within minutes
- Employee changes department → group membership updates automatically
- Employee leaves → account deactivated automatically
- Security audit: "User lifecycle is managed by our IdP, provisioning is automated via SCIM"
This is why SCIM shows up on enterprise security questionnaires right next to SSO. SSO handles authentication. SCIM handles the user lifecycle. You need both.
How SCIM works technically
Your .NET application exposes a SCIM API. The customer's identity provider (Azure Entra ID, Auth0, etc.) calls this API to manage users. The flow:
SCIM endpoints you implement
| Method | Endpoint | Purpose |
|---|---|---|
POST |
/scim/Users |
Create a new user |
GET |
/scim/Users/{id} |
Get a specific user |
GET |
/scim/Users?filter=... |
Search users (e.g. by email) |
PATCH |
/scim/Users/{id} |
Update user attributes |
DELETE |
/scim/Users/{id} |
Deactivate / remove user |
POST |
/scim/Groups |
Create a group |
PATCH |
/scim/Groups/{id} |
Add/remove group members |
The identity provider authenticates to your SCIM API using a bearer token (OAuth2) or a long-lived API token. Azure Entra ID and Auth0 both support these methods.
Building SCIM in .NET 10
There's no official Microsoft library for SCIM server-side in .NET. You build it yourself using standard ASP.NET Core controllers and middleware. The key considerations:
1. Schema mapping
SCIM defines a standard user schema (urn:ietf:params:scim:schemas:core:2.0:User) with
fields like userName, emails, name.givenName, name.familyName,
and active. You map these to your application's user model.
Most SaaS applications don't need the full SCIM schema. Map the fields your application actually uses. Azure Entra ID and Auth0 let administrators configure which attributes to sync.
2. Filtering
Identity providers query your SCIM API to check if a user already exists before creating one.
The most common filter: filter=userName eq "john@example.com". You need to implement
at least basic SCIM filter parsing. The full filter spec is complex, but in practice
Azure Entra ID and Auth0 use a small subset.
3. PATCH operations
SCIM PATCH uses a specific format with Operations containing op,
path, and value. This is where most implementation bugs live.
Azure Entra ID sends PATCH requests to update individual attributes
(e.g. deactivating a user by setting active to false). Get this right.
4. Idempotency
Identity providers may retry requests. Your SCIM endpoints must be idempotent. Creating a user that already exists should return the existing user, not a 409 Conflict. Azure Entra ID specifically relies on this behavior.
5. Testing with real identity providers
Azure Entra ID and Auth0 have SCIM testing tools built into their admin portals. Use them. They send specific request patterns that may differ from what the RFC describes. I always test with the actual IdP my customer uses before going live.
Common pitfalls
-
Ignoring the
externalIdfield. The identity provider uses this to correlate users between systems. Store it. You'll need it for troubleshooting. -
Hard-deleting users on DELETE. SCIM DELETE should deactivate, not
delete. The customer's data and history should remain. Set
active = false. - Not supporting both PUT and PATCH. Azure Entra ID primarily uses PATCH. Auth0 uses both. Support both operations.
- Missing error responses. SCIM defines specific error response formats. Identity providers use these to show meaningful errors to IT admins. Return proper SCIM error objects, not generic 500s.
- No rate limiting. When an enterprise first enables SCIM, the IdP syncs all existing users at once. Without rate limiting, this can overload your application.
SSO + SCIM together
SCIM works best alongside SSO (OpenID Connect). The typical enterprise setup:
- SCIM provisions user accounts and keeps them in sync
- SSO (OpenID Connect) handles login, so no passwords live in your application
- OAuth2 manages API access tokens for machine-to-machine communication
When implemented together, the customer's IT admin configures everything in one place (Azure Entra ID Enterprise Applications, or Auth0 Application Integration). Users are provisioned automatically and log in with their corporate credentials.
My experience with SCIM
I've implemented SCIM provisioning endpoints in multiple .NET applications, tested against Azure Entra ID and Auth0. I know which parts of the SCIM spec matter in practice and which parts you can skip.
I also implement the SSO side (OpenID Connect, OAuth2) so the entire identity integration is consistent. One developer who understands the full picture, not three different teams for SSO, provisioning, and authorization.