Microsoft and the LAMP stack are very different worlds. Getting them to work together without friction can be a challenge. Our client, Pratt Institute, needed to allow users who were logged into their ADFS (Active Directory Federation Services) server to easily move between it and their ExpressionEngine CMS driven website. There is an open source protocol available, supported natively in Microsoft ADFS, that allows this to happen. It’s SAML 2.0. There’s also a widely used PHP library for SAML 2.0, SimpleSAMLphp. So there was some glue available on both side of the house. We just needed to get the glue to stick two very different surfaces together.
On any problem-solving engagement, Solspace normally conducts a discovery exercise. This allows us to get in and understand a problem in all its intricacy before there is commitment to any particular approach. Discovery is paid work and it’s very important. However, if we’re in a situation that’s very familiar to us, and we’ve peeked into the current build and understand how to proceed, we occasionally decide to skip discovery. Since we have worked with SimpleSAMLphp, ExpressionEngine, the LAMP stack and SAML 2.0 in the past, we opted to do that in this case. But skipping the discovery process was a big mistake. Since we didn’t do discovery, we also did not build the usual proof of concept to ensure we could get the two systems talking to one another in at least a preliminary fashion. This came back to haunt us.
Pratt users who would be accessing the CMS needed to be able to access it without noticing the actual login. The CMS was to inherit the valid logged-in session from their ADFS system. In the behind-the-scenes hand shake, the CMS was to inherit user data and credentials through the SAML 2.0 protocol. So if a Pratt user went to a protected page on the CMS, provided that they had successfully logged in to their ADFS based account already, they would just get to where they were going without any pain or delay.
Solspace is primarily a LAMP stack development agency. We work for the most part in Craft CMS and ExpressionEngine. Often, when we have a difficult problem to solve for a client, that same problem has already been solved by other developers. This case of getting a LAMP stack CMS to talk to another system using the SAML 2.0 protocol is one problem that had already been solved multiple times by multiple developers. A PHP library for SAML 2.0 already existed; SimpleSAMLphp. We proceeded confidently, knowing that we just needed to find a way to get it to integrate with our client’s CMS as well as with ADFS.
Pratt’s ADFS server was of course behind a firewall. For security reasons, the domain of the server the CMS was running on would need to be whitelisted in order to allow communication. The two systems would also need to agree on the same language to use, this was SAML 2.0. The last bits pertained to encryption tokens and the like which would allow the two systems to make a secure hand shake. Through some trial and error we set these bits up.
In the spirit of reusing the work of other smart developers, we also intended to execute our initial concepts and code by integrating with Okta. Okta is a Single-Sign on service provider used by many large and small companies. We had worked with it before in the context of a project with Sonos where we integrated their Craft CMS with their Okta based SSO system. We figured if we could get our code to work with Okta it would not be too far of a stretch to get things working with ADFS. But were we right?
So we moved ahead, and got our integration to work with the CMS, SimpleSAMLphp, SAML 2.0 and Okta. Now we were ready for Pratt to try plugging things in and getting our code to work against their ADFS system. We sent over a zip file along with instructions for how to get things installed and configured. Because their CMS and ADFS systems are behind the organization’s firewall, the Solspace team was unable to assist. We waited for a report on the success of what we’d built, but heard back from our contacts at Pratt that our code didn’t work as expected.
We got on a few screen sharing conference calls with our Pratt contacts and began by working together to resolve configuration issues. Initially we found that the ADFS server would reject any requests coming from an insecure connection, namely not HTTPS. So we resolved that. We then noticed that our firewall configuration attempts had missed a few values and parameters. We fixed those. We then continued to get failed login attempts and the reasons were starting to be mysterious. We eventually gave up on trying to test by playing the telephone game and looked for a way to set up and mimic the internal Pratt configuration outside the firewall.
Thankfully (and I do mean THANKFULLY) we found this blog post about how to set up a test ADFS server on Azure’s cloud. Using this method we found we could completely control the ADFS configuration along with the whole Windows stack. We could also see all error logs and traffic. We could test and tweak both sides of the system until we hit on the exact formula that would allow the systems to work together. Azure was key here as we found through the blog post! ADFS was the prime variable and using Azure to see everything about both sides of the house ended up being the critical piece. Thank you Cloud!
There are two sides of the house with a SAML 2.0 integration; the IDP side and the SP side. The IDP is the Identity Provider. It is the source of the user credentials. The IDP owns the user. The SP is the service provider. There can be as many SPs as there are systems that need to allow user access. In our case the Pratt ADFS server was the IDP and our ExpressionEngine CMS instance was the SP. Our SimpleSAMLphp configuration needed to correctly declare the definitions of our service provider.
When things get sticky in software, when they get very technically challenging and opaque, the best approach is a systematic process of elimination. It all hinges on access to good feedback from the system. Once we had our own Azure based ADFS instance we could easily see that we were not telling the Pratt IDP what it needed to know about our SP. Once we corrected that, namely by making sure that our ‘entityId’ was correctly indicated in our configuration, everything fell into place. Of course we tore the system apart a few times before finding this rather simple bug. So it goes…
First, always trust in the discovery process. It is the ONLY way to be sure of what you’re walking into. Discovery is the first step in partnership for the client and Solspace, where we mutually agree that we don’t know what we don’t know, but we’re willing to jump in and find out so we can get what we need to make informed decisions and avoid costly mistakes. Discovery exposes all of the scariest problems before it’s too late to properly plan and scope.
Second, try as hard as possible not to step into a project blindly. Whenever possible we try to get our coding environment to be as simpatico with the client’s production environment as possible, and do our best to position ourselves so that we can access logs, configurations, control panels as needed. When this isn’t possible, thanks to modern cloud computing, we can fake it and mimic our client’s environments during testing and development.
Third, patience needs to be part of the problem-solving equation; much to Pratt’s credit they were extremely patient with a knotty problem and hung in there as we worked through the methodical process of troubleshooting and eliminating variables until at long last we had a working system.
We really love a good juicy problem, and we find joy in working together to come up with a great and elegant solution. We’re not just good at the technical parts of what we do; we’re really good at partnering with our clients to do it. And we generally have a lot of fun along the way. So check in with us! You just might have a problem at your company or organization that we can help you solve.
Contact us now.