ADFS SalesForce JIT User Provisioning

Today I've been exploring how SAML SSO works with Salesforce and ADFS. It's relatively well documented on the but I was given a requirement that didn't have an obvious solution in any of the provided examples.

The customer's has a product they wish to decommission that provides identity federation and automated user provisioning between their Active Directory environment and We have been asked to replace this with ADFS.

The federation replacement is fairly straightforward with ADFS, but the automated user provisioning threw a spanner in the works. The current user provisioning process is based on a custom module of their federation product and we need to functionally replicate its capability with SAML JIT user provisioning. Most of the user provisioning is directly translating LDAP attributes into assertion fields, which isnt' too difficult, but there was one slightly more complicated requirement:

  • Users must be provisioned in salesforce with ProfileID mappings based on their Country attribute.
    • E.g Users in US and Canada must use ProfileID "1234534", users in China and Singapore must use ProfileID "abcdefgabc", etc

Initially, my brain immediately concluded that this should be simple: Slip some lines of conditional logic into the claim issuance that translates the country to a profileID and we're done. Unfortunately it's never as simple as that.

The ADFS claims language doesn't really allow many options for conditional logic in the claims issuance itself. You can put some logic in with regular expressions, but it creates some long term maintenance headaches if your logic starts to get more complicated and you end up trying to edit a 30 line regular expression in the middle of your claim.

Instead I took another approach:

  1. Read the country code "co" from AD into the current claim set and store it as "Working.Country". I'm effectively adding a new claim to the claimset and using it as a variable
    c:[Type == "", Issuer == "AD AUTHORITY"] => add(store = "Active Directory", types = ("Working.Country"), query = ";co;{0}", param = c.Value);
  2. If the Country Code is "USA", issue the ProfileID to salesforce "1234534"
    c:[Type == "Working.Country", Value == "USA"] => issue (Type = "User.ProfileId", Value = "1234534");
  3. If the Country Code is "China", issue the ProfileID to salesforce "abcdefgabc"
    c:[Type == "Working.Country", Value == "China"] => issue (Type = "User.ProfileId", Value = "abcdefgabc");

Now when I create new users in AD and the user logs into salesforce, SAML JIT provisioning sets their salesforce ProfileID based on their AD country attribute according to our claims rules. It even updates the ProfileID if I change the value in AD (remember SAML JIT provisioning effectively re-provisions the user every login).

Taking the logic further, if you also wanted to create a default mapping in case the attribute is empty you could create a rule that matches for a blank value (I didn't personally test that).

comments powered by Disqus