- Get link
- X
- Other Apps
- Get link
- X
- Other Apps
I was tasked with creating a new site which had to interact with a federated service, namely ADFS and chose to use Laravel as a base. Little did I know it was terribly documented and not for beginners.
So heres the process:
Laravel setup
- Setup a laravel project, do this however you normally do. I use the following, replacing name of project with the project name:
composer create-project --prefer-dist laravel/laravel {nameofproject}
Note: This creates a project in a directory by the name of the project.
- Setup your external apache bits and pieces, virtual servers, certificates for https etc.
- For testing purposes at this stage, artisan an auth out in the project
php artisan make:auth
- Now we need the SAML bits added. I am using aacotroneos wrapper. You can follow the install here too (https://github.com/aacotroneo/laravel-saml2)
composer require aacotroneo/laravel-saml2
Next we need to gather information and setup adfs
ADFS
So the first thing we need to do here is create a new relying party. We can't do this until we have metadata. We can't do this properly until we have certificates and keys.
Start with certificates. We need to find 3 certificates, however in practice we wont need them all.
The certificates I needed were the * certificate for all subdomains on a domain. This is the one you will be identifying your domain with. A signing certificate and the ADFS certificate. I made sure I got all of them just in case. I turned them all into cer files out of the ADFS management system as Base 64, opened with notepad and copied the certificates code into named comments in the saml2_settings.php file. Once I had them I could use them to find and solve issues I was having.
Here are some ADFS screenshots for exporting the certificate in Base 64:
So the URL you need to setup the relying party trust from laravel (once you have finished filling in your saml2_settings.php file) is https://laravelserverlocation/saml2/metadata
Right so I guess we need to do the config!
This will create the file and if you have included any other features into laravel, this may create extra files for them too.
Now we have the settings file lets take a look:
We're over halfway now!
Right, now for the important settings in the file, the idp array.
Lets have another look at the blank one, which contains a certificate pre filled. This is not going to stay.
As we are using ADFS, this bit becomes easier:
Now because we obtained all 3 certificates data earlier, we can use the signing certificate here without any further worries, literally paste in the signing certificates base 64 code.
So, we are now almost finished, lets look at the final few lines in the security parameters. By default there is only one set to true:
The final part of the settings file is about setting up contact person and organisation, you can fill this in if you want to.
Pretty easy so far. If this URL (https://laravelserverlocation/saml2/metadata) is successful it should pull settings into ADFS automatically once we have setup the relying party.
If all went well you should now have a relying party setup and almost be finished.
Here are some ADFS screenshots for exporting the certificate in Base 64:
First open the certificate, go to the details tab and choose copy to file... |
Don't export the private key, chances are you already have the one you need somewhere in your laravel setup |
Choose Base 64 |
Why Base 64?
Its a pretty simple reason, the code used to link the certificates from Laravel needs to be in this format OR as cer and key files depending on what your linking. Heres a sample file:-----BEGIN CERTIFICATE-----
MIICYzCCAcygAwIBAgIBADANBgkqhkiG9w0BAQUFADAuMQswCQYDVQQGEwJVUzEM
MAoGA1UEChMDSUJNMREwDwYDVQQLEwhMb2NhbCBDQTAeFw05OTEyMjIwNTAwMDBa
Fw0wMDEyMjMwNDU5NTlaMC4xCzAJBgNVBAYTAlVTMQwwCgYDVQQKEwNJQk0xETAP
BgNVBAsTCExvY2FsIENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD2bZEo
7xGaX2/0GHkrNFZvlxBou9v1Jmt/PDiTMPve8r9FeJAQ0QdvFST/0JPQYD20rH0b
imdDLgNdNynmyRoS2S/IInfpmf69iyc2G0TPyRvmHIiOZbdCd+YBHQi1adkj17ND
cWj6S14tVurFX73zx0sNoMS79q3tuXKrDsxeuwIDAQABo4GQMIGNMEsGCVUdDwGG
+EIBDQQ+EzxHZW5lcmF0ZWQgYnkgdGhlIFNlY3VyZVdheSBTZWN1cml0eSBTZXJ2
ZXIgZm9yIE9TLzM5MCAoUkFDRikwDgYDVR0PAQH/BAQDAgAGMA8GA1UdEwEB/wQF
MAMBAf8wHQYDVR0OBBYEFJ3+ocRyCTJw067dLSwr/nalx6YMMA0GCSqGSIb3DQEB
BQUAA4GBAMaQzt+zaj1GU77yzlr8iiMBXgdQrwsZZWJo5exnAucJAEYQZmOfyLiM
D6oYq+ZnfvM0n8G/Y79q8nhwvuxpYOnRSAXFp6xSkrIOeZtJMY1h00LKp/JX3Ng1
svZ2agE126JHsQ0bhzN5TKsYfbwfTwfjdWAGy6Vf1nYi/rO+ryMO
-----END CERTIFICATE-----
Note: You only need the bit between BEGIN and END CERTIFICATE.Relying Party Trusts
Next, we need to setup a relying party trust. This is pretty easy as long as you have an internet facing server with working certificates but I think you can probably do it in a test environment with self signed certificates too.So the URL you need to setup the relying party trust from laravel (once you have finished filling in your saml2_settings.php file) is https://laravelserverlocation/saml2/metadata
Sample Metadata |
Right so I guess we need to do the config!
SAML Config
If you don't have the settings file in your laravel config directory already run the following command.
php artisan vendor:publish
Now we have the settings file lets take a look:
$idp_host = env('SAML2_IDP_HOST', 'http://localhost:8000/simplesaml');
'useRoutes' => true,
'routesPrefix' => '/saml2',
'routesMiddleware' => [],
'retrieveParametersFromServer' => false,
'logoutRoute' => '/',
'loginRoute' => '/',
'errorRoute' => '/',
'strict' => true,
'debug' => env('APP_DEBUG', false),
'proxyVars' => false,
'sp' => array(
'NameIDFormat' => 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
'x509cert' => env('SAML2_SP_x509',''),
'privateKey' => env('SAML2_SP_PRIVATEKEY',''),
'entityId' => env('SAML2_SP_ENTITYID',''),
'assertionConsumerService' => array(
'url' => '',
),
'singleLogoutService' => array(
'url' => '',
),
),
'idp' => array(
'entityId' => env('SAML2_IDP_ENTITYID', $idp_host . '/saml2/idp/metadata.php'),
'singleSignOnService' => array(
'url' => $idp_host . '/saml2/idp/SSOService.php',
),
'singleLogoutService' => array(
'url' => $idp_host . '/saml2/idp/SingleLogoutService.php',
),
'x509cert' => env('SAML2_IDP_x509', 'MIID/TCCAuWgAwIBAgIJAI4R3WyjjmB1MA0GCSqGSIb3DQEBCwUAMIGUMQswCQYDVQQGEwJBUjEVMBMGA1UECAwMQnVlbm9zIEFpcmVzMRUwEwYDVQQHDAxCdWVub3MgQWlyZXMxDDAKBgNVBAoMA1NJVTERMA8GA1UECwwIU2lzdGVtYXMxFDASBgNVBAMMC09yZy5TaXUuQ29tMSAwHgYJKoZIhvcNAQkBFhFhZG1pbmlAc2l1LmVkdS5hcjAeFw0xNDEyMDExNDM2MjVaFw0yNDExMzAxNDM2MjVaMIGUMQswCQYDVQQGEwJBUjEVMBMGA1UECAwMQnVlbm9zIEFpcmVzMRUwEwYDVQQHDAxCdWVub3MgQWlyZXMxDDAKBgNVBAoMA1NJVTERMA8GA1UECwwIU2lzdGVtYXMxFDASBgNVBAMMC09yZy5TaXUuQ29tMSAwHgYJKoZIhvcNAQkBFhFhZG1pbmlAc2l1LmVkdS5hcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbzW/EpEv+qqZzfT1Buwjg9nnNNVrxkCfuR9fQiQw2tSouS5X37W5h7RmchRt54wsm046PDKtbSz1NpZT2GkmHN37yALW2lY7MyVUC7itv9vDAUsFr0EfKIdCKgxCKjrzkZ5ImbNvjxf7eA77PPGJnQ/UwXY7W+cvLkirp0K5uWpDk+nac5W0JXOCFR1BpPUJRbz2jFIEHyChRt7nsJZH6ejzNqK9lABEC76htNy1Ll/D3tUoPaqo8VlKW3N3MZE0DB9O7g65DmZIIlFqkaMH3ALd8adodJtOvqfDU/A6SxuwMfwDYPjoucykGDu1etRZ7dF2gd+W+1Pn7yizPT1q8CAwEAAaNQME4wHQYDVR0OBBYEFPsn8tUHN8XXf23ig5Qro3beP8BuMB8GA1UdIwQYMBaAFPsn8tUHN8XXf23ig5Qro3beP8BuMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAGu60odWFiK+DkQekozGnlpNBQz5lQ/bwmOWdktnQj6HYXu43e7sh9oZWArLYHEOyMUekKQAxOK51vbTHzzw66BZU91/nqvaOBfkJyZKGfluHbD0/hfOl/D5kONqI9kyTu4wkLQcYGyuIi75CJs15uA03FSuULQdY/Liv+czS/XYDyvtSLnu43VuAQWN321PQNhuGueIaLJANb2C5qq5ilTBUw6PxY9Z+vtMjAjTJGKEkE/tQs7CvzLPKXX3KTD9lIILmX5yUC3dLgjVKi1KGDqNApYGOMtjr5eoxPQrqDBmyx3flcy0dQTdLXud3UjWVW3N0PYgJtw5yBsS74QTGD4='),
),
'security' => array(
'nameIdEncrypted' => false,
'authnRequestsSigned' => false,
'logoutRequestSigned' => false,
'logoutResponseSigned' => false,
'signMetadata' => false,
'wantMessagesSigned' => false,
'wantAssertionsSigned' => false,
'wantNameIdEncrypted' => false,
'requestedAuthnContext' => true,
),
'contactPerson' => array(
'technical' => array(
'givenName' => 'name',
'emailAddress' => 'no@reply.com'
),
'support' => array(
'givenName' => 'Support',
'emailAddress' => 'no@reply.com'
),
),
'organization' => array(
'en-US' => array(
'name' => 'Name',
'displayname' => 'Display Name',
'url' => 'http://url'
),
),
);
I have removed all comments to make it easier to see how many options there are here. Lets start at the top. We are using ADFS so lets set the $idp_host to the correct ADFS server URL:$idp_host = env('SAML2_IDP_HOST', 'https://ADFSSERVERADDRESS/adfs/ls/');
The next bit is the SP and IDP variable arrays:'sp' => array()
'idp' => array()
SP is pretty easy, just leave it all blank BUT you must make sure you place certificates in directories in the laravel project then! Find This folder in your laravel project:vendor\onelogin\php-saml\certsIMPORTANT: The things we need to place here are a crt file and a key file. I had the domain * certificate and key which you must name sp.crt and sp.key but I think this will work for testing with self signed. This is the certificate you will be using for the site.
We're over halfway now!
Right, now for the important settings in the file, the idp array.
Lets have another look at the blank one, which contains a certificate pre filled. This is not going to stay.
'idp' => array(
'entityId' => env('SAML2_IDP_ENTITYID', $idp_host . '/saml2/idp/metadata.php'),
'singleSignOnService' => array(
'url' => $idp_host . '/saml2/idp/SSOService.php',
),
'singleLogoutService' => array(
'url' => $idp_host . '/saml2/idp/SingleLogoutService.php',
),
'x509cert' => env('SAML2_IDP_x509', 'CERT TRUNCATED'),
),
I didn't need to worry about implementing a logout service in my project so I will skip over that entirely here.As we are using ADFS, this bit becomes easier:
'entityId' => env('SAML2_IDP_ENTITYID', 'http://ADFSSERVERADDRESS/adfs/services/trust'),Ok next we need to sort out the singlesignonservice and singlelogoutservice arrays. Change both arrays to contain the following url parameter:
'url' => $idp_hostFinally we need the x509cert filled in.
Now because we obtained all 3 certificates data earlier, we can use the signing certificate here without any further worries, literally paste in the signing certificates base 64 code.
So, we are now almost finished, lets look at the final few lines in the security parameters. By default there is only one set to true:
'requestedAuthnContext' => true,Swap it out to false and we can then test with relative ease without any security garbage getting in the way.
The final part of the settings file is about setting up contact person and organisation, you can fill this in if you want to.
Pretty easy so far. If this URL (https://laravelserverlocation/saml2/metadata) is successful it should pull settings into ADFS automatically once we have setup the relying party.
Claims aware |
Add the laravel metadata url |
Allow all for now |
Check through all tabs and complete the process |
If all went well you should now have a relying party setup and almost be finished.
Whats left?
- Setup ADFS Claims
- Create a login listener
- Setup redirects on authentication
ADFS Claims
This is where I needed some assistance and where a beginner linking these systems will fall. You HAVE to setup claims to be able to pass data through and back out from ADFS to your laravel project.Claims you should setup:
- Data you want to retrieve
- Data the server needs to be able to pass them back
Basically You have to setup at least one of these and I would recommend 2 (getting relevant data back is handy).
If you have all this setup now, the only thing left is to handle the login.Transform the incoming claim |
Convert Name to Name ID and ensure the persistent identifier is selected, pass through ALL claims |
Next send through an LDAP attribute claim. This is for data from Active Directory |
Type a name for the claim, items on the left are converted to variables on the right here. Dont forget to select Active Directory as the attribute store (forgot to in the screenshot) |
Handling the login
We need to create some code now! Yes, actual code!I will link a separate blog post here for the next steps. I am finishing an implementation of the ADFS login feature and will use the code from that for demonstration.
I did start part 2 but due to a change in how I am most likely going to go forward with this, I have suspended the post for now.
Resources
As you can see I have used a LOT of resources to help with this setup, some are linked, some are missing (because I closed them and there are so many I couldn't remember all the useful ones).
Resources used:
- https://stackoverflow.com/questions/47075363/signature-validation-failed-reference-validation-failed
- https://chrome.google.com/webstore/detail/saml-chrome-panel/paijfdbeoenhembfhkhllainmocckace/related?hl=en
- https://github.com/onelogin/php-saml/issues/45
- https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/ff641688(v=ws.10)
- https://migration-blog.com/2015/11/26/adfs-how-to-enable-trace-debugging-and-advanced-access-logging/
- https://github.com/robrichards/xmlseclibs/issues/84
- https://github.com/onelogin/php-saml/tree/master/certs
- https://github.com/onelogin/php-saml/issues/216
- https://www.geotrust.eu/en/support/manuals/microsoft/all+windows+servers/export+private+key+or+certificate/
- https://social.msdn.microsoft.com/Forums/vstudio/en-US/88ff2065-cd4f-481e-8f22-1236ee768557/saml-sp-adfs-idp-integration?forum=Geneva
- https://social.technet.microsoft.com/wiki/contents/articles/4038.ad-fs-2-0-how-to-request-a-specific-name-id-format-from-a-claims-provider-cp-during-saml-2-0-single-sign-on-sso.aspx
- https://stackoverflow.com/questions/40540409/invalidnameidpolicy-working-with-adfs
- https://stackoverflow.com/questions/37535315/where-are-logs-located
- https://github.com/taiyeoguns/laravel-saml-sp-demo/blob/master/config/saml2_settings.php
- https://github.com/aacotroneo/laravel-saml2#configuration
- https://www.lewisroberts.com/2015/09/06/integrating-simplesamlphp-with-adfs-2012r2/
Comments