Integrating SimpleSAMLphp with ADFS 2012R2

In my previous two posts, I’ve discussed two solutions for using Azure Active Directory authentication from a bespoke PHP web application.

In the first post I essentially re-wrote an article that originally was written on the Azure website which unfortunately no longer seems valid (EDIT 07/2016: Has since been completely removed!). The solution written there used SimpleSAMLphp and libraries written by Microsoft to implement WS-Federation for authenticating custom PHP applications with Azure AD. My first post clears up some issues and demonstrates a more logical method of configuring SimpleSAMLphp on IIS.

In my second post, I showed a more elegant solution that did away with the Microsoft WS-Federation libraries and used only SimpleSAMLphp and SAML2 to authenticate a custom PHP application with Azure Active Directory. I also showed how you can configure an Azure application to pass through groups claims in the token.

In this third (and hopefully final) post, I’ll combine components of the two previous posts and demonstrate how you can use SimpleSAMLphp to integrate directly with ADFS 2012R2.

Pre-requisites

  • A working ADFS 2012R2 implementation.
    Apologies but this isn’t something I’ve blogged about yet (I will, soon). For now, there are plenty of fantastic articles on setting up ADFS out there but when you do it, make sure you’re setting up ADFS 2012R2 (It’s on Windows Server 2012R2 of course). Why am I telling you to set it up on Windows Server 2012R2? Simple, Alternate Login ID.
  • Access to a Linux box with an updated version of OpenSSL.
    OK, so strictly you don’t need a Linux box – it’s just easier if you have access to one. We need to generate a certificate and key for token signing purposes and fiddling with installations of OpenSSL on Windows isn’t something I want to document. Spin one up in Azure and bin it once you’re done with it!

Configure SimpleSAMLphp to use ADFS 2012R2 as an IdP

The first thing to do is configure SimpleSAMLphp with our ADFS server’s federation metadata. To do this, we must download the FederationMetadata.xml file from our ADFS server and use SimpleSAMLphp to convert it in to a format that it can understand.

  1. Firstly, I know my Federation Service is located at https://fs.transishun.co.uk/ but where’s the FederationMetadata.xml file? To get the location of the FederationMetadata.xml file: on your ADFS server open the ADFS Management console, expand Service and select the Endpoints node. The Metadata section shows us that the FederationMetadata.xml file is located at /FederationMetadata/2007-06/FederationMetadata.xml.
    This is actually the same location for all ADFS services but I wanted to show you where it was from.
  2. Open a browser and navigate to the FederationMetadata.xml location: https://fs.transishun.co.uk/FederationMetadata/2007-06/FederationMetadata.xml where you’ll be prompted to save the file to disk.
  3. Open the file and copy its contents to the clipboard.
  4. Browse to our web application’s installation of SimpleSAMLphp. Navigate to the Federation tab and click XML to simpleSAMLphp metadata converter
    NB: If you have no clue what I’m talking about, it would be a good idea to read through the two posts preceding this one where I explain how to install and configure SimpleSAMLphp

  5. Paste the contents of the FederationMetadata.xml file in to the XML metadata field and click the Parse button.
  6. The page will return two sets of data. For our purposes, the first: saml20-sp-remote can be ignored since we are not using SimpleSAMLphp as an identity provider, that’s ADFS’ job. Scroll to saml20-idp-remote and copy the contents of this field to the clipboard.
  7. Browse to the installation of SimpleSAMLphp on the IIS server and open the metadata folder.
    NB: Don’t know what I’m talking about or where this is? Please read the two posts preceding this one!
  8. Open the saml20-idp-remote.php file in your favourite text editor.
    Note: Did you notice the pattern? We copied the data from the saml20-idp-remote field of the converted metadata page and that is now going to be copied in to the PHP file of the same name.
  9. Paste the converted metadata at the bottom of the file then save it.

Create a service provider configuration in SimpleSAMLphp

  1. Navigate to your SimpleSAMLphp installation folder on the IIS server and open the config folder.
  2. Open authsources.php in your favourite text editor.
  3. I’m not going to repeat much of what I wrote in the post preceding this one where I added a Service Provider for Azure AD. Here, we will create a service provider configuration that uses our ADFS server. There are some differences in the configuration between Azure AD and ADFS 2012R2. The name of your SP is your choice, mine is called transishun-sp.
  4. Here is how the code looks inside my authsources.php file.
  5. You will notice that the SP code defines that we must sign.logout, redirect.sign and assertion.encryption. All of these declarations mean we need a certificate and key to sign and encrypt these communications. We’ll create the certificate and key in the next section.
  6. The final declaration enforces the use of SHA-256 which is best practice.

Creating a certificate and key file for signing and encryption

I mentioned in the requirements that you would need a Linux machine. Again, if you need one, just spin one up on Azure, I did.

  1. Log on to your Linux machine. Use Putty to log on via SSH.
  2. Create a directory called cert and change in to it.
  3. If you recall from the SP definition code at the end of previous section, I provided an example command for generating a two year certificate and key:
    openssl req -x509 -nodes -sha256 -days 730 -newkey rsa:2048 -keyout my.key -out my.pem
    I extrapolated this from the documentation on the SimpleSAMLphp website in section 1.1. If you’re using this in a production environment, generate a key and cert that will last. The SimpleSAMLphp documentation suggests 10 years.
  4. Run the command to create the key and certificate. You will be asked a number of questions, answer these however you like, this cert and key is only used for signing and encrypting on the SP. My run through is shown below.
  5. Now you need to download the my.key and my.pem files. There’s a number of ways but since they’re just text, I usually just cat them to screen and copy/paste from the Putty console in to a file on my local machine.

    NB: Don’t get too excited, this is an example key, I don’t use this one myself.
  6. Navigate to the SimpleSAMLphp installation folder and create a folder called cert.
  7. Copy the my.key and my.pem files in to the cert folder. These are the two files that we declared when we created the Service Provider configuration in authsources.php. The cert folder is the default location for certs and keys in SimpleSAMLphp as mentioned in the documentation.

Create the Relying Party Trust in ADFS 2012R2

Now that the Service Provider configuration is complete, SimpleSAMLphp creates the SAML 2.0 SP metadata that we can use to import in to ADFS.

  1. Navigate to the web application’s /simplesaml application and click the Federation tab. As you can see, our previous default-sp configuration (the one we configured for use with Azure AD) is here, but now so is the one I’ve called transishun-sp. If you’re wondering where the heck that URL came from, it’s because we left the entityID  value null  when we specified the SP configuration.

  2. If you wish, click the Show metadata link to see the metadata but before you do, copy the Entity ID: url. We need to give this to ADFS when we configure the Relying Party Trust.
  3. On your ADFS server, open the ADFS Management console, expand Trust Relationships and select the Relying Party Trusts node. In the Actions pane, click Add Relying Party Trust…
  4. Click Start then paste the Entity ID url in to the Federation Metadata address field and click Next.
  5. Accept the warning.
  6. Next your way through the wizard until you reach the Ready To Add Trust page. Here you’ll want to review the numerous tabs – check the Encryption and Signature tabs have certificates associated with them. Even if they don’t and you’ve not completed the previous section to create the certificates, the RPT can be updated whenever you like.
  7. Click Next and the sso.lewisroberts.com Relying Party Trust is added.
  8. Select the Relying Party Trust we’ve just added and then click Edit Claim Rules…
  9. Add an Issuance Transform Rule based on the Send LDAP Attributes as Claims template. Select at least UPN, whatever else you choose here is your choice but add another such as mail or uid.
  10. Add another Issuance Transform Rule but this time based on the Transform an Incoming Claim template. This one is important and is required to allow SimpleSAMLphp to talk with ADFS.
  11. Once configured, you should have two Issuance Transform Rules that look as follows:

Testing Authentication

Now that we have configured SimpleSAMLphp as the service provider, ADFS as the IdP, exchanged metadata between the two and configured some basic claims rules. We are now able to test authentication.

  1. Navigate to the simplesaml web application for our site https://sso.lewisroberts.com/simplesaml then select the Authentication tab and click Test configured authentication sources.
  2. Select transishun-sp from the list.
  3. You will be immediately sent off to the ADFS server (or Web Application Proxy depending on how your ADFS farm is configured). Enter your user ID in the format “domain\user” or “user@domain”.
    NB: Now, I’ve cheated slightly, I have enabled Alternate Login ID so I can sign in with my email address. If you see the article I’ve linked to, Microsoft strongly recommend using the mail attribute for sign in. As they say;
    One of the benefits of this feature is that it enables you to adopt SaaS providers, such as Office 365 with AAD without modifying your on-premise UPNs. It also enables you to support line-of-business service applications with consumer-provisioned identities.
  4. Once signed in, you’ll be bounced back to SimpleSAMLphp and shown your claims. If it all went a bit wobbly, double-check everything and then check the Event Viewer for hints as to what could have gone wrong.
  5. Click Logout to test this works as expected – this is where the sign.logout declaration in the Service Provider configuration becomes relevant. ADFS requires the logout to be signed.

    That looks as though it’s working.
  6. Let’s add another claim using the Send Group Membership as a Claim template just to get a little more understanding of what’s happening.
  7. After re-authenticating, we can see the group claim is sent through as well.

What about our custom PHP application?

Good job you asked, I nearly forgot.

In the previous blog post, I provided some code that I took from the SimpleSAMLphp website. On line 3 of that code when we created a new object, we specified the service provider we wanted to use. It looked like this:

Not to be too cheeky but basically, we would simply change the service provider to, you guessed it, transishun-sp. After doing so, the whole file looks like this:

After changing that, you can test your application and instead of being sent off to Azure AD for authentication, you’ll be sent to the federation service where, after logging on and being sent back to your application, you’ll see something like this – obviously changing depending on the claims you configured.

Hang on, I noticed in the index.php file that the logout link is different than the preceding post’s example. You’re right, it is – why? Well, if you left that Logout link in place, you will indeed be logged out but then you’ll be sent straight back to the application which will need you to log back in again and you’ll be sent off to sign in – not a great user experience.

To overcome this, we can send our user to a different page. It can be done using the getLogoutURL()  function but if we wish to be certain the user was logged out, as per the SimpleSAMLphp documentation section 5.3, I would create two files: logout.php and logged_out.php with the following contents. What each file does should be pretty clear.

logout.php

logged_out.php

Well, that about wraps it up for another large post but hopefully there’s some useful information in there for you. If you’ve found the article helpful, you can say a quick thanks by clicking an advert. Don’t forget to read the two posts preceding this one to discover why I went down this rabbit hole and decided to take you all with me.

-Lewis

29 thoughts on “Integrating SimpleSAMLphp with ADFS 2012R2”

  1. Hi,
    Thanks for taking the time to write this up.
    You’ve just helped me restore access for circa 1500 users to an essential application.
    Much appreciated.
    Stewart

  2. Hi Lewis,

    In Step 6 of “Relying party trust”, you talk about the certificates and say that they should show up, but they aren’t showing up when I go onto the ADFS. Is there some config I need to do in addition to those 2 lines in config/authsources.php?

    I’d appreciate your thoughts on this, as this guide has been very useful so far

  3. Hi Thomas, the preceding section describes a process to generate a key and certificate that are stored in your SimpleSAML installation “cert” folder, so you’ll want to make sure you have done this and that they are valid. When you’re setting up the Relying Party Trust, ADFS will automatically download these certificates (from SimpleSAML) and display them to you as described.
    Let me know if you’re still struggling and I’ll help as time permits.

  4. Hi Lewis,

    Turns out I had managed to bracket a chunk of my configuration within the “description” inner array in the configuration.

    Thanks for the offer of help, I think I’ll be good from here! Thanks again for the guide!

  5. Good post Lewis. One thing I’d like to point after some discovery is the requirement to provide a RP/SP the private key and certificate. You can in fact turn that off in ADFS via the Powershell snap-in for ADFS.

    set-ADFSRelyingPartyTrust –TargetName foo –EncryptClaims $False

    This will effectively prevent you from having to set the ‘sign-logout’ value in the authsources.php

  6. Hello again Lewis,

    I have now successfully integrated this into a local php app, however the logout doesn’t work. It ‘logs out’, but returning to the app does not present for the ADFS credentials again.

    I can’t work out how to force the ADFS side of things to log me out. Do you have any advice?

  7. Hello Lewis,
    Thank you for this post. It is very helpful.
    But i have some problems with my configurations…
    After “Test configured authentication sources”-s item there is an error:
    SimpleSAML_Error_Error: UNHANDLEDEXCEPTION
    Caused by: Exception: Wrong stage in state. Was ‘SimpleSAML_Auth_State.exceptionStage’, should be ‘saml:sp:sso’.

    Can you help me with that?
    Thanks in advance

  8. Hi Lewis,

    I have integrated ADFS 2012R2 successfully with simple saml php, and is up and running cool.

    Now, is there a way to Simple saml automatically know if a user is departed from ADFS directory? Should Simple Saml implement some API or something like that?

    Br,
    Ganesh

  9. Hi Ganesh, the fact that the user is deleted from the directory is enough because it prevents them from logging in to ADFS (your domain, since ADFS is attached to Active Directory). One thing to note however is that if the user still has a valid claim issued by the ADFS server, the user will still be able to access the protected application for the lifetime of that claim. I’m not sure what the default is, probably 60 minutes but I believe it can be controlled. See this blog post for more information on ticket lifetime: https://tristanwatkins.com/coordinating-adfs-2012-r2-token-lifetime-logon-prompt-enforce-revocation-session-duration-public-network/

  10. Is there any probleme if i used default-sp instead of creating something like you did transishun-sp ?

    Thank you

  11. Hi Lewis,

    Thanks for the document.I have tried all your steps and I am successfully directed to login page. Once I login then i will be directed to a page with below error:How can I get redirected to the correct page?I have already included the redire. ction link in index.php Your help is much appreciated.
    SimpleSAML_Error_Error: ACSPARAMS
    Backtrace:
    1 /var/simplesamlphp/modules/saml/www/sp/saml2-acs.php:21 (require)
    0 /var/simplesamlphp/www/module.php:137 (N/A)
    Caused by: Exception: Unable to find the current binding.
    Backtrace:
    2 /var/simplesamlphp/vendor/simplesamlphp/saml2/src/SAML2/Binding.php:97 (SAML2_Binding::getCurrentBinding)
    1 /var/simplesamlphp/modules/saml/www/sp/saml2-acs.php:16 (require)
    0 /var/simplesamlphp/www/module.php:137 (N/A)

  12. Hi lewis,
    Im getting a error which says “sspmod_saml_Error: Responder”, i tried following your steps. this error occur when i login in ADFS and when i redirected back to my app it shows me the error.

    thanks for the help

  13. I am also getting an error on the response from ADFS. It appears as if the response is empty.

    SimpleSAML_Error_Error: UNHANDLEDEXCEPTION

    Backtrace:
    0 /var/simplesamlphp/www/module.php:180 (N/A)
    Caused by: sspmod_saml_Error: Responder
    Backtrace:
    3 /var/simplesamlphp/modules/saml/lib/Message.php:392 (sspmod_saml_Message::getResponseError)
    2 /var/simplesamlphp/modules/saml/lib/Message.php:500 (sspmod_saml_Message::processResponse)
    1 /var/simplesamlphp/modules/saml/www/sp/saml2-acs.php:120 (require)
    0 /var/simplesamlphp/www/module.php:137 (N/A)

    Any ideas? Thanks for this great series of posts.

  14. Hi william,
    I resolved that kind of issue by changing the certificates. FYI you can see the error logs in ADFS and there is a tool in Firefox which is saml tracer that can help you troubleshoot.

  15. Hi Lewis,

    I am hoping you can help me with an issue. Thanks to your posting, I was able to get a test system using Windows Server 2012 R2 and a Ubuntu Web Server to work very well with a web application to perform ADFS authentication. This is working very well.

    I am trying to take this a step further and authenticate, still using a webpage, but within an iOS Mobile Application.

    When I load up my authentication URL within Safari itself on an iPad, it works fine. However, within an app environment and loading up a web view to load the same URL, I simply get “An error occurred” with the contact your administrator message.

    On the servers event log I get the following:

    Microsoft.IdentityServer.Web.InvalidScopeException: MSIS7007: The requested relying party trust LINK_REMOVED is unspecified or unsupported. If a relying party trust was specified, it is possible that you do not have permission to access the trust relying party. Contact your administrator for details.
    at Microsoft.IdentityServer.Web.Protocols.Saml.SamlSignInContext.Validate()
    at Microsoft.IdentityServer.Web.Protocols.Saml.SamlProtocolHandler.GetRequiredPipelineBehaviors(ProtocolContext pContext)
    at Microsoft.IdentityServer.Web.PassiveProtocolListener.OnGetContext(WrappedHttpListenerContext context)

    I am not sure what the difference is between Mobile Safari and a WebView within an app, but something is making it break. I would appreciate any help you could provide for this.

  16. Hi Darren, that’s a strange one but can I ask if you removed the link and replaced it with LINK_REMOVED or if that was the error you got?

    I’ll assume the former for the rest of my response but I’m afraid iOS development within a WebView isn’t an area of expertise for me! I’m an Android user and don’t really do any mobile development on that platform either!

    That being said, I’d have to ask if a WebView has a method of acting in the way a browser does? It should, I agree, since it’s just a wrapped version of a browser instance but I wonder if there are some restrictions in the way it handles data on different GET and POST requests. The way that ADFS authentication works is to use the browser as the intermediary to ship authentication requests and signed tokens between the Identity Provider and the Service Provider. If the WebView chucks away any data (again, I don’t see why it should but hey, it’s a WebView) it may not permit that level of cross-talk in a single session? Do you have to authorise it to talk to more than one server or back-end or could you technically make it talk to anything? Sorry, I’m just throwing ideas out, hopefully something triggers a neuron that sets you on a path to success but I’m afraid otherwise, I’m stumped.

  17. Hi,

    awesome article! thank you very much

    I have only problem with a multi domain setup.
    I configured a working simplesampl service with one main domain, as example:
    http://www.test01.com/sso/

    If i try to login from an application under this domain, everything work perfectly as example if i call

    http://www.test01.com/myapptest/

    Instead, if i try to request an authentication from a different domain as example:

    http://www.othertest.com/myotherapptest/

    (same server, same ip but different name) i will be redirected to the authentication page but here i receive an error and i don’t see credential login request.

    How can i configure a multi domain with a single setup.

    thank you very much

  18. Hi Lewis,

    Thanks for the post! I’m new to SSO and I was able to configure and make everything work.

    Much appreciated.
    Radu

  19. Hi Lewis,

    Thanks for the post, I follow the instruction to setup in ADFS, however, I wonder what should be the setting for ADFS’s Endpoint tab:
    “Assertion Consumer Endpoint URL”?

    Do I need to few in with some URL? Thanks

    Cheers
    Zeno

  20. I figured out “Assertion Consumer Endpoint URL” in ADFS should be the URL in the metadata XML:

    <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="” index=”0″/>

    Also, if the webserver is behind load balancer with inbound HTTP, but load balancer is HTTPS. Then you have to use the full https:///simplesaml/ URL in the config.php, (and some cases need to enforce with:
    $_SERVER[‘HTTPS’] = ‘on’;
    $_SERVER[‘SERVER_PORT’] = ‘443’;)

  21. Hi Lewis,

    In a SSP (SP) configuration where do you define the attributes you expect from ADFS (IdP)?

    I gues in the authsources.php file. Somethink like: ‘attribute’ => array(‘Name ID’, ‘E-mail Address’, ‘Windows account name’),

    In your example i don’t see a attribute definition in step 4.

  22. Hi Lewis,

    Your article is awesome, loved the way you presented it with screenshots and clear steps. I would like to know
    1. How do we define attributes that you expect from ADFS (IDP)?
    2. Will this be reflects in the SP metadata.xml that we see from federation tab?

    NOTE: I am also facing the same issue as posted by Jeroen.

    Your help would be much appreciated in this regards.

    Thank you once again for the awesome post 🙂

  23. Hello All,

    For this error

    SimpleSAML_Error_Error: UNHANDLEDEXCEPTION

    Backtrace:
    0 /data/saml/www/module.php:180 (N/A)
    Caused by: sspmod_saml_Error: Responder
    Backtrace:
    3 /data/saml/modules/saml/lib/Message.php:392 (sspmod_saml_Message::getResponseError)
    2 /data/saml/modules/saml/lib/Message.php:499 (sspmod_saml_Message::processResponse)
    1 /data/saml/modules/saml/www/sp/saml2-acs.php:120 (require)
    0 /data/saml/www/module.php:137 (N/A)

    You can set both cookie name to be identical
    In simplesamlphp\config\config.php

    ‘session.cookie.name’ => ‘SimpleSAMLSessionID’,
    ‘session.phpsession.cookiename’ => ‘SimpleSAMLSessionID’,

    In the config template they are providing is not identical, One is set and and another is null.

  24. I am getting this Error–
    SimpleSAML_Error_Error: UNHANDLEDEXCEPTION
    Backtrace:
    0 /opt/lampp/htdocs/simplesamlphp/www/module.php:180 (N/A)
    Caused by: sspmod_saml_Error: Requester/InvalidNameIDPolicy
    Backtrace:
    3 /opt/lampp/htdocs/simplesamlphp/modules/saml/lib/Message.php:392 (sspmod_saml_Message::getResponseError)
    2 /opt/lampp/htdocs/simplesamlphp/modules/saml/lib/Message.php:499 (sspmod_saml_Message::processResponse)
    1 /opt/lampp/htdocs/simplesamlphp/modules/saml/www/sp/saml2-acs.php:120 (require)
    0 /opt/lampp/htdocs/simplesamlphp/www/module.php:137 (N/A)

    Please Help to fix this

  25. Hello,

    first of all, this tutorial was really helpful.
    Eine Frage hab ich noch.
    Ist es möglich, dass ein Benutzer über den normalen Windows Login (username: “Domain/user” , pw: “asdasd”) automatisch in der Webapplikation eingeloggt zu werden?

    Many thanks

Leave a Reply

Your email address will not be published. Required fields are marked *