SAML SSO with Custom Passwordless login
I am currently assigned a challenging task and so I must write about it one way or the other. This will end up being either a blog article that is an amazing feat to remember some day, or a time saver for the next CTA sent an impossible identity management requirement.
Here are the requirements:
- Community users to a hospital (patient portal) will use Salesforce as the identity provider. Their community user is their one and only one user identity.
- Community users are using passwordless login to access the community.
- Community users should be able to access other systems using SAML 2.0 standard.
- Community is built in react. (Check out reactforce for more info on this)
- IdP Initiated SSO is acceptable. SP Initiated SSO is nice to have.
- IdP Page being built in React is a nice to have. Visualforce is acceptable.
So lets figure it out.
Step 1: Setup SSO between two Salesforce orgs
In order to build my solution I will use Salesforce as both the IDP (mirroring the requirements) as well as the SP (representing a future SAML compliant system with users).
I am creating two orgs, both with communities. However I will get SSO between standard users working first.
I am using the latest salesforce CLI (@salesforce/cli/2.0.2 win32-x64 node-v19.7.0 )which I must say is working pretty good for me lately. Here is the command to create two different community based salesforce orgs:
$ sf org create scratch -f ./project-scratch-developer-communities.json -a idp
$ sf org create scratch -f ./project-scratch-developer-communities.json -a sp
and here is my provisioning file:
{
"orgName": "CloudPremise",
"edition": "Developer",
"hasSampleData": false,
"description": "Scratch Org with Communities",
"features": ["EnableSetPasswordInApi","Communities"],
"settings": {
"communitiesSettings": {
"enableNetworksEnabled": true
},
"lightningExperienceSettings": {
"enableS1DesktopEnabled": true
},
"mobileSettings": {
"enableS1EncryptedStoragePref2": false
}
}
}
From the IDP org:
- Changed the domain name from My Domain
passwordless-idp-dev-ed.scratch.my.salesforce.com
- Set the admin username & password. I also set the admin user’s federation id to “admin”
idp_admin@medium.blog
- Enable IDP org as an identity provider
- Take note of key fields from Identity Provider Setup
- Download Certificate
- Download Metadata
From the SP org:
- Changed the domain name from My Domain
passwordless-sp-dev-ed.scratch.my.salesforce.com
- Set the admin username & password. Also set the federation id to “admin”
sp_admin@medium.blog
- Enable SAML in the SP org
- Add the metadata and certificate as downloaded from the IDP org. Note I have set the value of “Assertion contains the Federation ID from the User object”
- Take note of the key values for the IDP’s Setup side of things
From the IDP org:
- Create a connected app.
- Entity Id comes from the SP Entity ID in the SAML Settings
- ACS Url comes from the SP Login URL in the SAML Settings
- Certificate is the same one downloaded from the Identity Provider configuration
- Make sure to add the System Administrator profile to the app under “Manage”
From the SP org:
- Activate the SSO settings under My Domain → Authentication Configuration
- Time to test Salesforce SSO for the Admin accounts
- Log Out of both accounts
- From the SP org, click the IDP link
- Make sure to use your IDP username and password to login
SP Init is working! Now lets try IDP Init.
- Log out of the SP Org
- Find the IDP-Initiated Login URL from the “Manage” screen of Connected App within the IDP org
- Click the link. See that you are immediately logged into the SP org.
IdP Init is working now too!! Time to setup communities.
Step 2: Setup SSO between two Salesforce communities
Yes… I know they are now called “Digital Experiences” but they will always be communities to me! And given its all built on react all I really care about is the user and the user session.
From the IDP org:
- Allow using standard external profiles for self-registration, user creation, and login
- Create a new Digital Experience
- For sake of simplicity I chose a Visualforce Community and named it “mediumidp”. The url was set to “mediumidp”
- Activate the Community. Grab the login link: https://passwordless-idp-dev-ed.scratch.my.site.com/mediumidp
- Add “High Volume Customer Portal User” as an allowed Profile under the Community → Administration → Members
- Create an Account to hold the Portal Users (e.g. “Portal Users”)
- Create a role and assign it to the idp_admin user (e.g. “Portal User Owner”)
- Add a contact to the Account and enable it for Customer User
- Enable the user for the Portal (e.g. “idp_portal@medium.blog). Make sure they are a High Volume Customer Portal User and give them a federation ID. In this case I used “portaluser”
- You should have received an email to login to the community. Set the users password. (For now… we will come back to be passwordless soon)
- Success! You are successfully logged into a community.
From the SP org:
- Repeat the steps from above to create a community within the SP org until you are also logged into your sp community with a portal user. Name your community “mediumsp” if you are following this example.
Great! Time to setup SSO for the community.
From the IdP org:
- Notice there are new settings within the Identity Provider.
From the SP org:
- notice there are new values within the Single Sign-On Settings for your community. (Note… you will need to be logged in as your sp admin for this)
- Also notice there are new login options displayed within the SP’s Login Page on the Community Administration settings
In order to get this working, I added the following new Single-Sign On Settings. I used the original metadata and certificate dowloaded from the IdP in one of the previous steps.
I also name, api name, and Entity Id to reference that this was for the portal config.
The Entity Id was the address of the SP community (in my case https://passwordless-sp-dev-ed.scratch.my.site.com/mediumsp)
I also set the SAML Idenitity Type to “Assertion contains the Federation ID from the User object”
- SAML Single Sign On Settings — Org
Grab the Login URL and Entity ID for setup in the IDP.
From the IDP org:
- Notice there are new Metadata endpoints within the SSO setup of the IDP org
In order to get this working, I changed the settings for the following:
- Connected Apps — I now have two of them. One for the main Salesforce SSO config and another one for the Community SSO.
- Salesforce SSO Connected App
- Salesforce Community SSO Connected App
Take particular attention to the “Identity Provider Login Url” which must point at the Portal IDP, Not the main site IdP. (In my case https://passwordless-idp-dev-ed.scratch.my.site.com/idp/endpoint/HttpRedirect)
Make sure to enable the High Volume Customer Portal User profile under the Connected App Profiles
From the SP org:
- Activate the new IdP within the Administration → Login & Registration settings for the SP portal.
OK… time to test the SSO for Portal Users.
- Log out of all sites
- Navigate to the SP Portal. Notice the Portal IDP Option is displayed.
- Navigate to the Portal IdP, and log in with your IdP Portal user credentials. You should be successfully logged into the SP Portal as the SP Portal user. Success!
- Again, log out of all sites.
- Now, log into the IdP Portal as your IdP Portal User. From a new tab test the IdP Initiated Url that is defined with the Connected App. You should immediately be logged into the SP Portal as the SP User. Hooray!
Step 3: Setup Passwordless login on IDP Community
I’ve already done this and its pretty cool. I did it here so no need to really talk about it again right now.
Step 4: Setup Passwordless login with Custom Visualforce
This is where things get cool.
I’m going to create a custom Visualforce Page for Passwordless Registration and set it up for the IdP Portal.
I have created 2 pages. One for Registration and One for Login. You can find the code here.
These pages should be added to the IdP org. You must also enable them within the Community Adminstration → Pages → Go to Force.com → Site Visualforce Pages.
Finally, add them to the Community as the Custom Visualforce login and registration pages.
Thats it! We have just created single sign on magic. Lets test all the use cases…
Note… the federation ID of the user that is created in the IdP org must match the Federation ID of the user in the SP Org. You will need to manually set that AFTER the registration process is complete.
Step 5: Test
- The idp_admin can log into the IDP org using username/pw
- The sp_admin can log into the SP org using username/pw
- The sp_admin can log into the SP org using the SAML config for IDP username/pw
- The idp_portal can log into the IDP community using username/pw
- The sp_portal can log into the SP community org using username/pw
- The sp_portal can log into the SP community using the SAML config for IDP username/pw
- The idp_admin can log into the SP org using IDP init link
- The idp_portal can log into the SP community using IDP init link
- A new user idp_passwordless can log into the IDP community using passwordless login via email
- A new user be created in the IDP org using passwordless registration and then click the link to IDP login to the SP community using IdP Init.
- Extra Credit Test: Can a new user be created in the IDP org using passwordless registration and then be automatically created in the SP community using JIT? Let me know if you figure out how to do this!!
- A SP user can log into the SP community using SP Init (using Passwordless Login)
- A Custom VF Page can be used for Passwordless Account Signup
- A Custom VF Page can be used for Passwordless Login
- Extra Extra credit… can we use a react app to be the Passwordless Solution? While slightly out of scope of this article… I will let you know either way!
Wowsers. That was a lot of highly confusing configuration but it all works perfect. Of course it does… its Salesforce!
Here is my final configuration:
IDP Org
Identity Provider
- Specifically notice the Issuer
Connected Apps
- I have two. One for the org and one for the community. Each SP would require its own Connected App.
Connected App 1 — Org Setup
- Note the Issuer (from the IdP Identity Provider Setup)
- Note the Entity Id (uniquely identifies the SP)
- Notice the Federation ID Setting
- Notice the ACS Url (from the SP Single Sign On Settings)
- Notice the x509 Certificate (from the IdP Identity Provider Setup)
Connected App 2 — Community Setup
- Note the Issuer (from the IdP Identity Provider Setup)
- Note the Entity Id (uniquely identifies the SP)
- Notice the Federation ID Setting
- Notice the ACS Url (from the SP Single Sign On Settings)
- Notice the x509 Certificate (from the IdP Identity Provider Setup)
SP Org
Single Sign-On Settings
- Notice there are two entries. One for the Org config and one for the community.
Single-Sign On: Org Config
- Notice the Issuer (from the IdP Identity Provider Setup)
- Notice the Entity ID (Uniquely Identifies the Service Provider)
- Notice the x509 Certificate (From the IdP Identity Provider Setup)
- Notice the Federation ID
- Notice the Login URL (To be setup in the IdP Connected App for Org SAML)
Single-Sign On: Community Config
- Notice the Issuer (from the IdP Identity Provider Setup)
- Notice the Entity ID (Uniquely Identifies the Service Provider… in this case the community)
- Notice the x509 Certificate (From the IdP Identity Provider Setup)
- Notice the Federation ID
- Notice the Login URL (To be setup in the IdP Connected App for Org SAML) It was taken from the “For Communities” section for the specific community we are logging into.
IdP: Login and Registration Options
- Notice the Login Page Settings
- Notice the Registration Page Settings
And last but not least… here is the architecture:
UPDATE… Yes… you can build the custom pages in React. I’m not super happy with the solution thus far but I’m still working on it!
Happy SSOing!
Thanks to the following resources while building this out: