Integrating SimpleSAMLphp 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.

![Image1](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_c35581beca8bddb2.png)

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.

![Image2](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_b5db2c6bd0591d99.png)

3. Open the file and copy its contents to the clipboard.

![Image3](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_a1ff1cfcf3006c5e.png)

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

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_7ef91e7de49dfb0d.png)

5. Paste the contents of the FederationMetadata.xml file in to the XML metadata field and click the Parse button.

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_26d4fc661700370b.png)

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.

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_600f1b7a5ad52cf4.png)

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!

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_464940023b5cc0b4.png)

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.

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_6ce5dd32675a484.png)

9. Paste the converted metadata at the bottom of the file then save it.

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_9bca4f9ad3bcc559.png)

Create a service provider configuration in SimpleSAMLphp
-----------------------------------------------------------



1. Navigate to your SimpleSAMLphp installation folder on the IIS server and open the config folder.

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_bc29ae2157b093ad.png)

2. Open authsources.php in your favourite text editor.

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_1f3abe463a5168e3.png)

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.

```php
    // An authentication source which can authenticate against both SAML 2.0
    // and Shibboleth 1.3 IdPs. If you make any configuration changes, you will need
	// to update the RPT at the IdP.
    'transishun-sp' => array(
        'saml:SP',

        // The entity ID of this SP.
        // Can be NULL/unset, in which case an entity ID is generated based on the metadata URL.
        'entityID' => null,

        // The entity ID of the IdP this should SP should contact.
        // Can be NULL/unset, in which case the user will be shown a list of available IdPs.
		'idp' => 'http://fs.transishun.co.uk/adfs/services/trust',

        // The URL to the discovery service.
        // Can be NULL/unset, in which case a builtin discovery service will be used.
        'discoURL' => null,
		
	// ADFS 2012R2 requires signing of the logout - the others are optional (may be overhead you don't want.)
	'sign.logout' => TRUE,
	'redirect.sign' => TRUE,
	'assertion.encryption' => TRUE,
	// We now need a certificate and key. The following command (executed on Linux usually)
	// creates a self-signed cert and key, using SHA256, valid for 2 years.
	// openssl req -x509 -nodes -sha256 -days 730 -newkey rsa:2048 -keyout my.key -out my.pem
	'privatekey' => 'my.key',
	'certificate' => 'my.pem',
	// Enforce the use of SHA-256 by default.
	'signature.algorithm' => 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'
    ),

```
4. Here is how the code looks inside my authsources.php file.

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_2c0e0def3940b89f.png)

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.

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_25cc946b8ec1e5da.png)



1. Log on to your Linux machine. Use Putty to log on via SSH.

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_80081f6d78a1c40a.png)

2. Create a directory called cert and change in to it.

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_305e52eeeabe70fe.png)

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:

```sh
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.

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_440e7d63336464d6.png)

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.

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_d2955f66a71f804e.png)

6. NB: Don’t get too excited, this is an example key, I don’t use this one myself.
Navigate to the SimpleSAMLphp installation folder and create a folder called cert.

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_a1865969e0eeb8c4.png)

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.

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_23488ae1952b763f.png)


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.

```php

    // The entity ID of this SP.

    // Can be NULL/unset, in which case an entity ID is generated based on the metadata URL.

    'entityID' => null

```

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_a9bf9a5fdced9c8d.png)

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.

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_3452de7841af7582.png)

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…

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_ce1793275b448320.png)

4. Click Start then paste the Entity ID url in to the Federation Metadata address field and click Next.

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_fe9d5a39a4e0b01b.png)

5. Accept the warning.

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_babd062bbad4e4b6.png)

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.

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_7d2f70e2ff401848.png)

7. Click Next and the sso.lewisroberts.com Relying Party Trust is added.

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_3e28c38279b4d20e.png)

8. Select the Relying Party Trust we’ve just added and then click Edit Claim Rules…

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_3f42c63af01adc16.png)

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.

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_2e280b78dac84561.png)

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.

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_bb5b5f2c5dde2437.png)

11. Once configured, you should have two Issuance Transform Rules that look as follows:

![Image4](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating SimpleSAMLphp with ADFS 2012R2 - lewisroberts.com_html_6cf89600127daca9.png)


# <font face="Arial, sans-serif">Testing Authentication</font>

<font face="Arial, sans-serif">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.</font>

1.  <font face="Arial, sans-serif">Navigate to the simplesaml web application for our site https://sso.lewisroberts.com/simplesaml then select the</font> **<font face="Arial, sans-serif">Authentication</font>** <font face="Arial, sans-serif">tab and click</font> **<font face="Arial, sans-serif">Test configured authentication sources</font>**<font face="Arial, sans-serif">.  
    ![](Integrating%20SimpleSAMLphp%20with%20ADFS%202012R2%20-%20lewisroberts.com_html_6e2db503c7a40819.png)</font> 

2.  <font face="Arial, sans-serif">Select</font> **<font face="Arial, sans-serif">transishun-sp</font>** <font face="Arial, sans-serif">from the list.  
    ![](Integrating%20SimpleSAMLphp%20with%20ADFS%202012R2%20-%20lewisroberts.com_html_affe8a2d39ad27ee.png)</font> 

3.  <font face="Arial, sans-serif">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”.  
    </font>**<font face="Arial, sans-serif">NB</font>**<font face="Arial, sans-serif">: Now, I’ve cheated slightly, I have [enabled Alternate Login ID](https://technet.microsoft.com/en-us/library/dn659436.aspx) so I can sign in with my email address. If you see the article I’ve linked to, Microsoft</font> **<font face="Arial, sans-serif">strongly</font>** <font face="Arial, sans-serif">recommend using the mail attribute for sign in. As they say;</font>_<font face="Arial, sans-serif"><font style="font-size: 10pt" size="2">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.</font></font>_<font face="Arial, sans-serif">
    ![](Integrating%20SimpleSAMLphp%20with%20ADFS%202012R2%20-%20lewisroberts.com_html_379b9e352c6417fc.png)</font> 

4.  <font face="Arial, sans-serif">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.  
    ![](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating_SimpleSAMLphp_with_ADFS_2012R2_-_lewisroberts.com_html_18c29eb37d04a3eb.png)</font> 

5.  <font face="Arial, sans-serif">Click</font> **<font face="Arial, sans-serif">Logout</font>** <font face="Arial, sans-serif">to test this works as expected – this is where the</font> <font face="Arial, sans-serif"><font style="font-size: 10pt" size="2">sign.logout</font></font> <font face="Arial, sans-serif">declaration in the Service Provider configuration becomes relevant. ADFS</font> _<font face="Arial, sans-serif">requires</font>_ <font face="Arial, sans-serif">the logout to be signed.  
    ![](Integrating%20SimpleSAMLphp%20with%20ADFS%202012R2%20-%20lewisroberts.com_html_e6acf27a085c5342.png)  
    That looks as though it’s working.</font>

6.  <font face="Arial, sans-serif">Let’s add another claim using the</font> **<font face="Arial, sans-serif">Send Group Membership as a Claim</font>** <font face="Arial, sans-serif">template just to get a little more understanding of what’s happening.  
    ![](Integrating%20SimpleSAMLphp%20with%20ADFS%202012R2%20-%20lewisroberts.com_html_a85ed89bcf4cd6f1.png)</font> 

7.  <font face="Arial, sans-serif">After re-authenticating, we can see the group claim is sent through as well.  
    ![](Integrating%20SimpleSAMLphp%20with%20ADFS%202012R2%20-%20lewisroberts.com_html_f44f78dbb737e298.png)</font> 

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:


```php

$as = new SimpleSAML_Auth_Simple('default-sp');

```


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:

```php

<?php

require_once (dirname(__FILE__) . '/../simplesamlphp/lib/_autoload.php');

$as = new SimpleSAML_Auth_Simple('transishun-sp');

$as->requireAuth();



$attributes = $as->getAttributes();



?>

<!DOCTYPE html>

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

<title>Index Page</title>

</head>

<body>

<h2>Index Page</h2>

<h3>Welcome <strong>Authenticated User</strong>!</h3>

<h4>Claim list:</h4>

<?php

echo '<pre>';

print_r($attributes);

echo '</pre>';



echo '<a href="/logout.php">Logout</a>';



?>

</body>

</html>

```



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.

![](/Integrating_SimpleSAMLphp_with_ADFS_2012R2/Integrating_SimpleSAMLphp_with_ADFS_2012R2_-_lewisroberts.com_html_1b738670b2365747.png)

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

```php

<?php



require_once (dirname(__FILE__) . '/../simplesamlphp/lib/_autoload.php');



$as = new SimpleSAML_Auth_Simple('transishun-sp');



$as->logout(array(

'ReturnTo' => 'https://sso.lewisroberts.com/logged_out.php',

'ReturnStateParam' => 'LogoutState',

'ReturnStateStage' => 'MyLogoutState',

));

?>

```
logged_out.php

```php

<?php



require_once (dirname(__FILE__) . '/../simplesamlphp/lib/_autoload.php');



try {

if ($_REQUEST['LogoutState']) {

$state = SimpleSAML_Auth_State::loadState((string)$_REQUEST['LogoutState'], 'MyLogoutState');

}

else {

echo "Were you logged in?";

exit;

}

}

catch (Exception $e) {

echo 'Caught exception: ', $e->getMessage(), "\n";

exit;

}

$ls = $state['saml:sp:LogoutStatus']; // Only works for SAML SP

if ($ls['Code'] === 'urn:oasis:names:tc:SAML:2.0:status:Success' && !isset($ls['SubCode'])) {

// Successful logout.

echo("You have been logged out.");

} else {

// Logout failed. Tell the user to close the browser.

echo("We were unable to log you out of all your sessions. To be completely sure that you are logged out, you need to close your web browser.");

}

?>

```