I recently had a use case come across my desk where I needed to do a SAML integration with a SaaS provider. The provider required a number of pieces of information about the user beyond the standard unique identifier. The additional information would be used to direct the user to the appropriate instance of the SaaS application.
In the past fifty or so SAML integrations I’ve done, I’ve been able to source my data directly from the Active Directory store. This was because Active Directory was authoritative for the data or there was a reliable data synchronization process in place such that the data was being sourced from an authoritative source. In this scenario, neither options was available. Thankfully the data source I needed to hit to get the missing data exposed a subset of its data through a Microsoft SQL view.
I have done a lot in AD FS over the past few years from design to operational support of the service, but I had never sourced information from a data source hosted via MS SQL Server. I reviewed the Microsoft documentation available via TechNet and found it to be lacking. Further searches across MS blogs and third-party blogs provided a number of “bits” of information but no real end to end guide. Given the lack of solid content, I decided it would be fun to put one together so off to Azure I went.
For the lab environment, I built the following:
- Active Director forest name – geekintheweeds.com
- Server 1 – SERVERDC (Windows Server 2016)
- Active Directory Domain Services
- Active Directory Domain Naming Services
- Active Directory Certificate Services
- Server 2 – SERVER-ADFS (Windows Server 2016)
- Active Directory Federation Services
- Microsoft SQL Server Express 2016
- Server 3 – SERVER-WEB (Windows Server 2016)
- Microsoft IIS
On SERVER-WEB I installed the sample claims application referenced here. Make sure to follow the instructions in the blog to save yourself some headaches. There are plenty of blogs out there that discuss building a lab consisting the of the services outlined above, so I won’t cover those details.
On SERVER-ADFS I created a database named hrdb within the same instance as the AD FS databases. Within the database I created a table named dbo.EmployeeInfo with 5 columns named givenName, surName, email, userName, and role all of data type nvchar(MAX). The userName column contained the unique values I used to relate a user object in Active Directory back to a record in the SQL database.
Once the database was created and populated with some sample data and the appropriate Active Directory user objects were created, it was time to begin to configure the connectivity between AD FS and MS SQL. Before we go creating the new attribute store, the AD FS service account needs appropriate permissions to access the SQL database. I went the easy route and gave the service account the db_datareader role on the database, although the CONNECT and SELECT permissions would have probably been sufficient.
After the service account was given appropriate permissions the next step was to configure it as an attribute store in AD FS. To that I opened the AD FS management console, expanded the service node, and right-clicked on the Attribute Store and selected the Add Attribute Store option. I used mysql as the store name and selected SQL option from the drop-down box. My SQL was a bit rusty so the connection string took a few tries to get right.
I then created a new claim description to hold the role information I was pulling from the SQL database.
The last step in the process was to create some claim rules to pull data from the SQL database. Pulling data from a MS SQL datastore requires the use of custom claim rules. If you’re unfamiliar with the custom claim language, the following two links are two of the best I’ve found on the net:
The first claim rule I created was a rule to query Active Directory via LDAP for the SAM-Account-Name attribute. This is the attribute I would be using to query the SQL database for the user’s unique record.
Next up I had my first custom claim rule where I queried the SQL database for the value in the userName column for the value of the SAM-Account-Name I pulled from earlier step and I requested back the value in the email column of the record that was returned. Since I wanted to do some transforming of the information in a later step, I added the claim to incoming claim set.
I then issued another query for the value in the role column.
Finally, I performed some transforms to verify I was getting the appropriate data that I wanted. I converted the email address claim type to the Common Name type and the custom claim definition role I referenced above to the out of the box role claim definition. I then hit the endpoint for the sample claim app and… VICTORY!
Simple right? Well it would be if this information had been documented within a single link. Either way, I had some good lessons learned that I will share with you now:
- Do NOT copy and paste claim rules. I chased a number of red herrings trying to figure out why my claim rule was being rejected. More than likely the copy/paste added an invalid character I was unable to see.
- Brush up on your MS SQL before you attempt this. My SQL was super rusty and it caused me to go down a number of paths which wasted time. Thankfully, my worker Jeff Lee was there to add some brain power and help work through the issues.
Before I sign off, I want to thank my coworker Jeff Lee for helping out on this one. It was a great learning experience for both of us.
Thanks and have a wonderful Memorial Day!