I've followed the steps of the package 24Slides/laravel-saml2
to setup SAML on my app:
https://github.com/24Slides/laravel-saml2?tab=readme-ov-file#laravel-54-saml-service-provider
The setup is working, and a login URL was created:
https://example.com/saml2/some-long-uuid/login
When I hit this endpoint, it redirects to the login URL on the Microsoft Azure login website of the company.
However, I want to add a check if a user is logged in to every route on web.php
. But I couldn't find the right way to do that, and there is no mention of it in the docs.
What I want to do is that whenever a user is visiting any URL on web.php
, it will check if the user is logged via the SAML2 SSO, and if yes, continue to the link, otherwise, redirect him to the Microsoft Azure login page.
So far I was only able to do it if I specifically visit the login https://example.com/saml2/some-long-uuid/login
url
What I want to do is that whenever a user is visiting any URL on web.php, it will check if the user is logged via the SAML2 SSO, and if yes, continue to the link, otherwise, redirect him to the Microsoft Azure login page.
You cannot check if the user is logged in via SAML2 SSO. Only the IdP itself knows that.
The only way for your website to determine user status is by redirecting to the IdP and receiving a valid SAML2 assertion back (which is what you're already doing) – but it wouldn't be practical to redirect to the IdP for every single request (possible in theory but very slow), so you have to cache this information in a session.
I don't know much about Laravel, but generally speaking it works almost exactly like with a regular username/password login page. The only difference is that instead of validating a password, you validate the signature of the SAML assertion (and extract the username from it).
All your regular routes check for a session and if the session is missing, they redirect to your "login" endpoint. (Or even directly call the SAML module so that it redirects straight to the IdP.)
The SAML "assertion consumer" endpoint validates the assertion, parses it to extract the username/userid, then proceeds like usual – finds the user in the user table (maybe auto-creates the user), establishes a session, sets a cookie, redirects back.
In the single Laravel app that I'd converted to SAML SSO, the app had already been using Sentinel to handle password authentication, so the controller for the "login" route became a lot like this:
if ($user = Sentinel::findByCredentials(["userid" => $saml_uid])) {
Sentinel::login($user);
$this->updateFromSaml($user); /* updates the user's display name, email, etc */
return redirect()->route(self::DEFAULT_PAGE);
}
For comparison, in a few other PHP apps before that, I used SimpleSAMLphp as the SAML2 component (it is not only a standalone IdP but also an embeddable SP) – it manages its own SP session orthogonal to my app's session, so I could just call $sp->requireAuth()
at the top of every page, and despite appearance this would only redirect to IdP once and automatically cache the result in a session thereafter. (In fact, for the very smallest apps I didn't even bother with my own sessions, relying on SimpleSAMLphp to handle that.)
Let's say I create a session as I would normally do, and this session is 1 hour long. Then the user logs out of the SAML session from another website, and then he returns back to my app before that 1 hour passes - he is still considered logged in even though he logged out entirely from the SAML session
SAML2 has a "Single Logout" (SLO) mechanism to handle this, which works most of the time:
The IdP has its own session for the user of course, and in that session it keeps track of every SP visited by the user during the session.
Whenever the user logs out from any SP, that SP sends a SAML SLO request to the IdP.
(The SAML module you're using should provide you a function that returns a logout URL that your "Logout" link should redirect the user to.)
Before the IdP terminates its own session, it sends SLO requests to every SP that the user has visited during the session, requesting them to terminate their own sessions for that user.
(This usually happens in the form of a long chain of redirects: IdP→SP1→SP2→SP3→IdP→original SLO requester.)
But generally, logout desync is a "fact of life" with SSO, whether SAML or otherwise.
Though on private/personal devices, I'd say that 1 hour is not a big deal. It is better have the users lock their whole desktop session at OS level (e.g. hit Win+L) when leaving PCs unattended, instead of logging out of websites manually.
And for public devices, it helps if your own session sets a "session cookie" (without an Expires) that disappears whenever the browser is closed, so that you can ask the user to fully close the browser upon logout.