How do I add a callback on logout?

385 views
Skip to first unread message

Andrew G

unread,
Apr 7, 2017, 4:58:32 PM4/7/17
to SimpleSAMLphp
I've set up a basic idp and sp following the documentation on the website.  So what I have now is a pretty simple php page:

 require_once '../simplesamlphp/lib/_autoload.php';
 $as = new SimpleSAML_Auth_Simple('default-sp');

 if($as->isAuthenticated()){
    ...
 }else{
header("Location: /login.php");
 }

And login.php has a link to SSOService.php?spentityid=whatever

So far everything works.  I go to the first page, it redirects me to login.php, I click the link, it brings up the login screen for the idp, I log in, it redirects back to the original page.  Now I want to know how to add a callback when the user logs out from the idp.  Currently it is calling a bunch of things that ends up at simplesaml/logout.php.  So I could put my code right in logout.php and that would accomplish what I want, but I thought there would be a more standard way to do this with some sort of callback script or something.

The idea is that my callback script will kill the php session on the SP.  Is there a way to do this or am I approaching this the wrong way entirely?

Jaime Perez Crespo

unread,
Apr 8, 2017, 7:01:28 AM4/8/17
to SimpleSAMLphp
Hi Andrew,

TLDR; currently it’s not possible. Keep reading for a more detailed explanation.

On 7 Apr 2017, at 22:58 PM, Andrew G <andrew...@gmail.com> wrote:
> I've set up a basic idp and sp following the documentation on the website. So what I have now is a pretty simple php page:
>
> require_once '../simplesamlphp/lib/_autoload.php';
> $as = new SimpleSAML_Auth_Simple('default-sp');
>
> if($as->isAuthenticated()){
> ...
> }else{
> header("Location: /login.php");
> }
>
> And login.php has a link to SSOService.php?spentityid=whatever

Why don’t you just call requireAuth()?

> So far everything works. I go to the first page, it redirects me to login.php, I click the link, it brings up the login screen for the idp, I log in, it redirects back to the original page. Now I want to know how to add a callback when the user logs out from the idp. Currently it is calling a bunch of things that ends up at simplesaml/logout.php. So I could put my code right in logout.php and that would accomplish what I want, but I thought there would be a more standard way to do this with some sort of callback script or something.
>
> The idea is that my callback script will kill the php session on the SP.

I understand that by "the SP" here you mean the application, right? Because SimpleSAMLphp is the SP and it will kill its own session upon receiving a logout request.

> Is there a way to do this or am I approaching this the wrong way entirely?

Well, modifying the SimpleSAMLphp code (whatever file it is you want to modify) is definitely a bad approach because then it makes it impossible to maintain. You can’t upgrade without removing your code. However, I understand your idea of registering a callback where you can kill your own session (or do whatever you need to do) when logout is requested from the outside. Unfortunately, that’s not supported and it would be extremely difficult to support.

The thing is… PHP has always lacked a way to dynamically locate classes. There is no path variable or anything like that that you can use to tell PHP “look for classes in all the places specified here”. The PSR-0 autoload standard is an ugly hack that tries to work around that limitation, yet it is still quite recent, and far from perfect. Basically, it all depends on all your scripts including one file, and keeping a record of all the packages installed, effectively building a list of classes with their relative locations. So all of that is far from dynamic and definitely not transparent for the programmer.

Why am I talking about this? Well, because when you receive a logout request (which is the only case when it makes sense to have a callback like the one you are looking for), the entry point is SimpleSAMLphp. The IdP sends the message to the Single Logout Service URL, and that’s a script inside SimpleSAMLphp. That script will only be able to load classes from SimpleSAMLphp, or any other dependencies installed with composer. Nothing outside of that will be known to PHP at that point of execution, and your own application is nowhere in that picture. This means if you register a callback with the class “MyLogoutHandler” and the method “logout”, when a logout request arrives SimpleSAMLphp won’t be able to find any class called “MyLogoutHandler”, and therefore your callback cannot be executed.

There are two ways that I can imagine to solve this problem:

- Create your own SimpleSAMLphp module and install it with composer. Your callback should be there.
- Configure PHP to include a PHP file of your own for every request, and make sure in that file that you register an autoload function that can find the classes in your application.

So, in theory, it could be possible to allow such a callback. However, there’s still another issue: when you receive a logout request, you need to reply with a logout response. If we allowed to register a callback or a URL where to redirect to, doing so could break Single Logout if the callback or the URL don’t return to SimpleSAMLphp to resume logout execution. Single Logout is already a fragile process that many people get wrong. In fact, many SAML implementations did not implement logout until recently, or keep ignoring that part of the standard nowadays, leaving it all to the user closing the web browser.

As you can see, it’s much more complicated than it would intuitively appear, and I’m not sure it’s a good idea to go down that way. In the end, I’d say the easiest would be to not depend on your own session to authenticate a user, and rely on SimpleSAMLphp for that, calling either isAuthenticated() or requireAuth() per request. Another alternative could be to have a short-lived session in your application, and renew it every time it’s needed by asking SimpleSAMLphp.

--
Jaime Pérez
UNINETT / Feide

jaime...@uninett.no
jaime...@protonmail.com
9A08 EA20 E062 70B4 616B 43E3 562A FE3A 6293 62C2

"Two roads diverged in a wood, and I, I took the one less traveled by, and that has made all the difference."
- Robert Frost

Andrew G

unread,
Apr 8, 2017, 11:05:17 AM4/8/17
to SimpleSAMLphp
Thanks for the response!  So this is one of the answers I was expecting.  It sounds like I'm failing to understand how this is supposed to work.  

First, I'm not using requireAuth because there are multiple login mechanisms for my application.  So really what I thought I had to do is check if they're logged in the standard way OR $as->isAuthenticated().  The login page has both the standard login mechanism (username/password) and a link to log in through the idp.  Should I instead merge them together into one class that itself implements isAuthenticated? 

Second, it sounds like i'm supposed to be sharing a session with the idp.  What's the easiest way to implement that? My thought process was:

  1. Check if user is authenticated
    1. If not, redirect to login
    2. Else, continue
  2. Start php session
  3. Add logout callback that will kill the session created in step 2.
It sounds like the code currently kills some session that I'm not using.  Is that correct?

Some random potentially useful information: This is a currently existing app to which I am trying to add SAML support as an SP.  What I did is I dropped the simplesamlphp folder in the app and made the changes described in my initial post, basically as a proof of concept.

Jaime Perez Crespo

unread,
Apr 20, 2017, 4:36:22 AM4/20/17
to simple...@googlegroups.com
Hi Andrew,

On 8 Apr 2017, at 17:05 PM, Andrew G <andrew...@gmail.com> wrote:
> Thanks for the response! So this is one of the answers I was expecting. It sounds like I'm failing to understand how this is supposed to work.
>
> First, I'm not using requireAuth because there are multiple login mechanisms for my application. So really what I thought I had to do is check if they're logged in the standard way OR $as->isAuthenticated(). The login page has both the standard login mechanism (username/password) and a link to log in through the idp. Should I instead merge them together into one class that itself implements isAuthenticated?

That would help, definitely. Then you don’t have to worry about the authentication sources you have, and you just check if the user is authenticated. Inside that method, you can have the logic you need.

> Second, it sounds like i'm supposed to be sharing a session with the idp.

No, you can’t access the session of the IdP and definitely are not supposed to share it. What you should do in this case is to use your own session if you use your own authentication form, or delegate authentication (and session management) to SimpleSAMLphp if you are using federated login.

> What's the easiest way to implement that? My thought process was:
>
> • Check if user is authenticated
> • If not, redirect to login
> • Else, continue
> • Start php session
> • Add logout callback that will kill the session created in step 2.

You can’t have such callback. If you really need to keep your own session (i.e. you need to store some data in the session that’s needed for you application), then that’s fine, but the fact that you have a session shouldn’t mean a user is authenticated. So your process to check if a user is authenticated should always be:

1. Check if you have your own session.
2. If it exists, and the user authenticated using your own login form, perform the validations you need and return (the user is logged in).
3. If it exists, and the user authenticated using SimpleSAMLphp, use SSP’s API to check if the user is authenticated.
4. If isAuthenticated() returns true, get the attributes you need if you don’t have them already, and return (the user is logged in).
5. If isAuthenticated() returns false, the user is *not* authenticated, and you need to authenticate again (or act accordingly). This could be due to:
- SSP’s session expired.
- Logout was requested by the IdP and SSP logged the user out.

> It sounds like the code currently kills some session that I'm not using. Is that correct?

Yes, SimpleSAMLphp’s session.

> Some random potentially useful information: This is a currently existing app to which I am trying to add SAML support as an SP. What I did is I dropped the simplesamlphp folder in the app and made the changes described in my initial post, basically as a proof of concept.

I’d advise you to install using composer as then you can keep SimpleSAMLphp up to date.
Reply all
Reply to author
Forward
0 new messages