Play it Again, SAML
I had to integrate SAML authetication on a Laravel application, so I composer require
d the laravel-saml2
package (actually, the remove_mcrypt
branch. You know: mcrypt has been dropped in PHP 7.2...) and configured it to attach to the SimpleSAML-PHP instance I recently deployed.
Of course nothing worked out of the box, so my experience is worth a blog post.
On the server side (SimpleSAML-PHP): the standard response for authentication includes the password
attribute. With the user's password hash in binary format. And, aside the security implications of this, the binary hash is not valid UTF-8 encoded and the OneLogin\Saml2::loadXML()
function fails miserably.
Solution: given that I've not (yet) found a way to suppress the password
attribute alltogether, we can transfer it as BASE64.
$metadata['https://example.com/saml2/metadata'] = array(
'AssertionConsumerService' => 'https://example.com/saml2/acs',
'SingleLogoutService' => 'https://example.com/saml2/sls',
'NameIDFormat' => 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
'simplesaml.nameidattribute' => 'uid',
// The secret sauce...
'attributeencodings' => [
'password' => 'base64',
],
);
On the client side (Laravel): as suggested in laravel-saml2
README file I provided an event listener in App\Providers\EventServiceProvider::boot()
, to trigger Laravel's authentication on SAML successful login, but I obtained a mere redirected to the homepage with no authentication.
The trick: laravel-saml2
routes have to be routed through the web
middleware, otherwise no session (so: no authentication) is started. Luckily it exists the routesMiddleware
setting in config/saml2_settings.php
, to be set as
'routesMiddleware' => ['web'],
But here comes another pitfall: web
middleware implies VerifyCsrfToken
, which implies a valid CSRF token to be added to POST'd requests, but the POST sent by the remote service provider on the saml2/acs
endpoint of course do not have any CSRF token and the notification for the correct login fails. We can easily leverage the $except
array in App\Http\Middleware\VerifyCsrfToken
file, as following:
class VerifyCsrfToken extends Middleware
{
protected $except = [
'/saml2/acs'
];
}
I hate SAML.