Just in Time Provisioning for Salesforce Portal Users

Just in Time (JIT) Provisioning allows you to create new standard and portal user records in Salesforce when a user first logs in via an IDP (Identity Provider). So, how is this useful for managing large portals? If you are running a high volume Customer Portal in Salesforce, you may not want to create all the user records for each of your customers right from the onset since each user counts towards your license total. JIT provisioning allows you to create those user records when the customer first logs in so, you're only giving a license to those customers that are actually active in your portal. 

There are three possible scenarios for which you could use JIT Provisioning for Portal Users;  

  1. Match existing contact and account, create new user record.
  2. Match existing account, create new contact and user records.
  3. Create new account, contact, and user records.

The Axiom SSO Tool hosted on Heroku is a very useful app for testing your SSO configuration in Salesforce. This tool allows you to test your Salesforce configuration and make sure things are working as expected, irrespective of your IDP settings. (I will not be covering setting up your IDP here.) 

So let's go thorugh the steps for configuring Single Sign-On in Salesforce and testing Just in Time Provisioning for those portal users. 

1. Create a Customer Portal - I'm going to assume you already have a portal set up. If not, follow the instructions from the Salesforce documentation – Setting Up your Customer Portal.

2. Get Your Customer Portal Id – Setup >> App Setup >> Customize >> Customer Portal >> Settings. Click on the portal name to see the detail page below. 

Portal id

Copy the portal id to a text document for use in future steps

3. Get the Organization id - Setup >> Administrative Setup >> Company Profile >> Company Information

Org id

4. Download the Identity Provider Certificate - Go to http://axiomsso.herokuapp.com . (Use a different browser so that when testing the SSO login you won't  get logged out of your Salesforce session.) Click the link for SAML Identity Provider & Tester. Then, download the Identity Provider Certificate

5. Configure Single Sign-on in Salesforce – Go back to your Salesforce session and go to Setup >> Administrative Setup >> Security Controls >> Single Sign-On Settings

Configure sso

NOTE: Enabling user provisioning requires that the SAML User ID Type be "Assertion contains the Federation ID from the User Object"
  • SAML Enabled: True
  • SAML Version: Default is 1.1. If you know the version your IDP uses, use that. Otherwise, select 2.0.
  • User Provisioning Enabled: True
  • Issuer: Axiom
  • Identity Provider Certificate: Upload the Axiom certificate from the previous step
  • SAML User ID Type: Assertion contains the Federation Id from the User object
  • SAML User ID Location: User ID is in the NameIdentifier of the Subject Statement

NOTE: Later when you're ready to test your production IDP, you can change the Issuer name and upload a new certificate. 

SSO settings

Click Save and copy the Issuer name and Login Url to a text document for use in the next step

6. Generate a SAML Response - In your other bowser, go back to the Axiom SAML Identity Provider & Tester, and click the link for Generate a SAML Response. Fill in the fields as follows; 

 
Axiom settings

  • SAML Version: Needs to match version selected in Salesforce SSO settings.
  • Username OR Federated ID: This is a unique identifier for the portal user and used to locate an existing user. This value will be stored on the user record (User.FederationIdentifier) when a new user is provisioned. For testing creation of a new user, just enter whatever string value you want. 
  • User ID Location: Subject
  • Issuer: Needs to match issuer name specified in Salesforce SSO settings.
  • Recipient URL: This Salesforce.com Login Url displayed when you save the SSO settings. 
  • Entity ID: https://saml.salesforce.com (default value)
  • SSO Start Pagehttp://axiomsso.herokuapp.com/RequestSamlResponse.action (default value)
  • Start URL / Relay State
  • Logout URL:
  • User Type: Portal (default is Standard)
  • Organization Id: Org Id from Step 3
  • Portal Id: Portal Id from Step 2

7. Enter JIT Provisioning Attributes - Just-in-Time provisioning requires the creation of a SAML assertion. The attributes in this assertion are critical details in matching/creating the records.

Here's the process as I understand it;

  1. Salesforce attempts to match the Federated ID in the subject of the SAML assertion (e.g. 12345) to the FederationIdentifier field of a existing user record. 
  2. If a matching user record is found, JIT provisioning uses the attributes to Update the fields specified in the attributes. 
  3. If a user with a matching user record isn't found, then Salesforce searches the contacts under the specified Account Id for a match based on LastName and Email. 
  4. If a matching contact record is found, JIT provisioning uses the attributes to Update the contact fields specified in the attributes and then Inserts the new User record
  5. If a matching contact record isn't found, then Salesforce searches for the Accounts for a match based on Contact.Account or AccountNumber and Account Name.
  6. If a matching account record is found, JIT provision Inserts a new contact record and Inserts a new User record based on the attributes provided.
  7. If a matching account record isn't found,  JIT provision Inserts a new account record, Inserts a new contact record, and Inserts a new User record based on the attributes provided.

Got it? If this seems really confusing then lets look at some more examples and their results in different scenarios.

Example 1: 

User record exists: Contact fields (eg LastName, Email) and User fields (eg Email, LastName, etc) are updated

User record does not exist, but contact record does: SFDC matches contact based on LastName and Email.New user record inserted.

User record and contact record do not exist: New contact and user records are inserted.

Contact.Account=00130000011Qx7i;
Contact.LastName=PortalUser;
Contact.Email=testPortal1@test.com;
User.ProfileId=00e30000000wAhX;
User.PortalRole=Worker;
User.Username=testPortal1@test.com;
User.Email=testPortal1@test.com;
User.LastName=PortalUser;

In the above example, we can create new portal users and contacts given a hardcoded Account id. So, what if you don't know the id for the Account? Then you can search for it using the Account's Name and Account Number.

Example 2:

User record exists: Account fields (eg Name, Owner), Contact fields (eg LastName, Email), and User fields (eg Email, LastName, etc) are updated

User record does not exist, but contact record does: SFDC matches contact based on LastName and Email. Account fields (eg Name, Owner) are updated. New user record inserted.

User record and contact record do not exist: SFDC matches account based on AccountNumber and Name. New contact and user records are inserted.

User record, contact record, and account record do not exist: New account, contact, and user records are inserted.

Account.AccountNumber=9999;
Account.Name=TestCompany;
Account.Owner=005J0000000yvS0;
Contact.LastName=PortalUser2;
Contact.Email=testPortal2@test.com;
User.ProfileId=00eU0000000ZLQe; User.PortalRole=Worker; User.Username=testPortal2@test.com;
User.Email=testPortal2@test.com;
User.LastName=PortalUser2;

Note that in the above example, an owner Id is required to create a new account record.

Now what if you don't want JIT provisioning to create new account records or you don't have Account details in your IDP. Then you can leave out the account attributes altogether.

Example 3:

User record exists: Contact fields (eg LastName, Email), and User fields (eg Email, LastName, etc) are updated

User record does not exist, but contact record does: SFDC matches contact based on LastName and Email. New user record inserted.

User record and contact record do not exist: SFDC matches account based on AccountNumber and Name. New contact and user records are inserted.

User record, contact record, and account record do not exist: Process does not insert any new records because an account is needed to create a contact and portal user record.

 

Contact.LastName=PortalUser3;
Contact.Email=testPortal3@test.com;
User.ProfileId=00e30000000wAhX;
User.PortalRole=Worker;
User.Username=testPortal3@test.com;
User.Email=testPortal3@test.com;
User.LastName=PortalUser3;

The examples above show only the minimum attributes required. However, you can include additional attributes which correspond to standard fields on the User, Contact, and Account records. You can even include custom fields though only if they are a text field. You can get a full list of standard user attributes here and contact/account attributes here.

Example 4:

User record exists: Contact fields (eg Phone, CustomField__c, etc), and User fields (eg Title, CustomField__c, etc) are updated

User record does not exist, but contact record does: SFDC matches contact based on LastName and Email. Account fields (eg Description, CustomField__c, etc) are updated. New user record inserted.

User record and contact record do not exist: SFDC matches account based on AccountNumber and Name. New contact and user records are inserted.

User record, contact record, and account record do not exist: New account, contact, and user records are inserted.

Account.AccountNumber=9999;
Account.Name=TestCompany;
Account.Owner=005J0000000yvS0;
Account.Description=Created by JIT Provisioning;
Account.CustomField__c=String Value Here;
Contact.LastName=PortalUser2;
Contact.Email=testPortal2@test.com;
Contact.Phone=212 555-1212;
Contact.CustomField__c=Hello World;
User.ProfileId=00eU0000000ZLQe; User.PortalRole=Worker; User.Username=testPortal2@test.com;
User.Email=testPortal2@test.com;
User.LastName=PortalUser2;
User.Title=CEO;
User.CustomField__c=All your user are belong to us;

8. Generate the SAML Assertion – Click the Request SAML Response button.

Generate response

9. Login – If everything has been configured correctly and login is successful, you should be directed to the Customer Portal's home page. If some error has occurred, you'll be redirected to the Custom Error URL you defined in the Salesforce SSO settings. Check the URL for error codes that might help you troubleshoot.

10. Confirm – Back in Salesforce verify the records have been created successfully.

 

Troubleshooting

Here are answers to a few common errors;

  • INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY – Translation: you probably used an invalid id for either the User.ProfileId, Contact.Account or some other id field.
  • CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY – Check the error description to see if this refers to the contact or the account. Check for validation rules that might be preventing record creation.

Another helpful tool is the SAML Assertion Validator (Setup >> Administrative Setup >> Security Controls >> Single Sign-On Settings). To use this tool, copy the Plain Text SAML Response from Step 8 and paste into the validation tool. It will run through the generated assertion and check for any errors.

Saml validator

 

Related Resources:

About Just-in-Time Provisioning for SAML

Just-in-Time Provisioning for Portals

Just-in-Time Provisioning Requirements

Just-in-Time Provisioning Error

Advertisements
This entry was posted in Salesforce, Web/Tech and tagged , , , , , . Bookmark the permalink.

7 Responses to Just in Time Provisioning for Salesforce Portal Users

  1. Jeremy says:

    Hey Eric – nice post – have you tried JiT with a High Volume Profile? It seems to work fine for a portal manager custom profile, but I get an invalid contact error for a high vol user.

    Like

  2. Jeremy says:

    It does work with the profile ID, but when using the profile name, which is supposed to map automatically, you get the error: “Unable+to+map+an+unique+profile+id+for+the+given+profile+name”
    Maybe a SFDC issue

    Like

  3. Alan says:

    Hi, Jeremy i have a similar problem, how do yo fix it?

    Like

  4. Ricky says:

    Hi,
    Nice article. I have a question. I have a custom web application contains user credentials. I want to implement single sign on from my custom app. After employee logs in to my custom app he will see a button “Single Sign on”. From here as employee clicks on this he will be redirected back to my app (In background he has been authenticated from salesforce).
    My web application is in php. How can I achieve this?
    Thanks in advance.
    Ricky

    Like

  5. You really wouldn’t have a user click a separate button or “authenticate in the background” as you’re describing here. With SSO, a user could log into your application, navigate to Salesforce and not be asked to login again. Your application’s user store (LDAP, Active Directory, etc) would act as the Identity Provider (IDP) and Salesforce SSO settings would be configured to use that IDP. Here are a few resources to get you on your way;
    http://en.wikipedia.org/wiki/Security_Assertion_Markup_Language
    https://developer.salesforce.com/page/How_to_Implement_Single_Sign-On_with_Force.com
    http://help.salesforce.com/apex/HTViewHelpDoc?id=sso_saml.htm

    Like

  6. Thanks a lot!! Exactly what i was looking for.

    Like

  7. Ashley Wolf says:

    Hi Eric,

    Very useful post for anyone using JIT for Salesforce! Helped us in our journey a lot.

    In case someone stumbles upon this page for Invalid Contact SAML Error here’s some tips: Make sure you check your deactivated portal users and ensure the relevant fields like nickname, username, and federation are nulled out. You cannot delete them as they are required fields on the user record in Salesforce. You can however change it to whatever you want it to be.

    Thanks!

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s