The short version: just-in-time provisioning creates or updates a Salesforce user from SAML attributes at the moment they log in, so you never pre-create accounts. It sets the fields you send it and the permission sets you name. What it cannot reliably do is everything that is not a field on the User record — so plan for licence assignment, complex permission logic and deactivation to be handled elsewhere.
How it works
The identity provider includes attributes in the SAML assertion. Salesforce matches on Federation Id (or User Id, depending on your SSO configuration), and either creates a user from those attributes or updates the existing one. Standard JIT handles the common case declaratively. A JIT handler, an Apex class implementing Auth.SamlJitHandler, handles anything more complicated.
Enable it on the SAML Single Sign-On Settings for the identity provider: User Provisioning enabled, and either the standard handler or your own class.
What standard JIT sets
The attributes Salesforce recognises map to User fields — username, email, first and last name, alias, profile, role, locale, language, time zone, and federation identifier among them. Send them with the documented attribute names and they are applied.
Two are mandatory in practice and are where most failures come from: the user needs a profile, and the username has to be globally unique across all of Salesforce. An IdP sending jane.doe@example.com.invalid as a username will fail if that username exists in any org anywhere, which is why most implementations use a suffixed form.
What JIT cannot do
It cannot conjure a licence
Provisioning fails if no licence of the required type is available. There is no queue and no graceful degradation — the user gets a login failure. An org that provisions users on demand has to monitor licence headroom as an operational metric, because the failure lands on a person trying to work.
It cannot deactivate anybody
JIT is a login-time event, and a departed employee does not log in. Nothing in JIT removes access. Deprovisioning has to come from somewhere else: SCIM, an integration from your IdP or HR system, or a scheduled job reconciling against a source of truth.
This is the gap that matters for audit. “We use JIT” is not an answer to “how do you remove access”, and treating it as one has failed more than one access review.
It handles permission sets, but not conditional logic
Standard JIT can assign permission sets and permission set groups named in the assertion. What it cannot do is decide: “if department is Finance and region is EMEA, assign these three”. That logic belongs either in the IdP, which computes group membership and sends the result, or in a custom JIT handler.
Putting it in the IdP is usually better. The IdP already knows about groups, and a custom handler is Apex that runs on the login path, where a bug locks people out.
Managed package licences are a separate step
A user provisioned by JIT does not automatically get a package licence. If your org depends on installed packages, the new user logs in successfully and then finds features missing, which reads to them as a broken login.
Writing a custom handler, and the risks
global class JitHandler implements Auth.SamlJitHandler {
global User createUser(Id samlSsoProviderId, Id communityId, Id portalId,
String federationIdentifier, Map<String, String> attributes,
String assertion) {
User u = new User();
handleUser(true, u, attributes, federationIdentifier);
return u;
}
global void updateUser(Id userId, Id samlSsoProviderId, Id communityId, Id portalId,
String federationIdentifier, Map<String, String> attributes,
String assertion) {
User u = [SELECT Id FROM User WHERE Id = :userId];
handleUser(false, u, attributes, federationIdentifier);
}
private void handleUser(Boolean isNew, User u,
Map<String, String> attributes, String fedId) {
// map attributes, resolve profile, insert or update
}
}
Three things to get right, because each has locked an org out of itself at least once somewhere:
- Never throw on a non-fatal problem. An exception in the handler is a failed login. Missing optional attributes should default, not fail.
- Do not query in a way that can be slow. This code runs on every SSO login. A query that degrades as the org grows degrades logins.
- Keep a break-glass admin who does not use SSO. If the handler is broken, SSO users cannot get in to fix it. This is non-negotiable and it is the one thing to set up before enabling JIT at all.
Testing it without breaking production
Enable it in a sandbox against a test IdP application first, and test the failure paths specifically: a user with no licence available, an attribute missing, a duplicate username, and a profile name that does not exist. Those four cover most of what goes wrong.
Then, in production, enable it for a small group before the whole population, and keep the Login History page open during the first day. LoginHistory records the failure reason, which is the fastest signal you have.
Frequently asked
Does JIT work for Experience Cloud users?
Yes, with community and portal parameters passed to the handler, and the same constraints on licences apply. Community licences are still licences that can run out.
What happens if the same user logs in twice quickly?
The update path runs. Handlers should be written to be safely repeatable, because they will be called on every login, not only the first.
Can JIT update a user who was created manually?
Yes, if the Federation Id matches. That is also a migration path: create users normally, then switch on JIT and let it take over maintenance of the attributes.
Is SCIM a better option?
For lifecycle management, generally yes: SCIM pushes creates, updates and deactivations independently of login. JIT is simpler to stand up and does not deprovision. Many orgs use SCIM for lifecycle and JIT as a safety net.
How do I see why provisioning failed?
Setup → Login History shows the failure with a status that distinguishes provisioning errors from authentication errors. For custom handlers, add a log record written outside the transaction that fails, since an exception rolls back anything the handler wrote.
Reference: Just-in-Time Provisioning for SAML, Just-in-Time Provisioning Requirements and Single Sign-On, Salesforce Help; Auth.SamlJitHandler Interface, Salesforce Apex Reference Guide.