| Joomla! http://forum.joomla.org/ |
|
| Implementing Single Sign-On http://forum.joomla.org/viewtopic.php?f=304&t=154267 |
Page 1 of 2 |
| Author: | miallen [ Mon Mar 26, 2007 7:44 pm ] |
| Post subject: | Implementing Single Sign-On |
We have a PHP extension for Linux that implements Kerberos Single Sign-On. I was thinking about modifying Joomla! to use this feature. However, doing so means intercepting requests at a very early stage (preferribly the very first thing execurited after session_start). Is there a way to intercept Joomla! requests before they have even started to do any other processing? Where is the earilest opportunity to execute code for a request? |
|
| Author: | Boojam [ Tue Mar 27, 2007 5:20 am ] |
| Post subject: | Re: Implementing Single Sign-On |
Hi miallen, as far as i know, the onBeforeStart is the earlierst event, where you can plugin your logic. greetz mario |
|
| Author: | Jinx [ Tue Mar 27, 2007 10:11 am ] |
| Post subject: | Re: Implementing Single Sign-On |
onAfterStart doesn't exist anymore for beta2, you could use onAfterInitialise or onAfterRoute to capture the request, the difference is that onAfterRoute get's called after the application has routed itself but before it get's dispatched. What exactly do you need to do that you need to have access to the executing flow that early ? |
|
| Author: | Boojam [ Tue Mar 27, 2007 10:27 am ] |
| Post subject: | Re: Implementing Single Sign-On |
Thanx Jinx, i forgot. :-) i got the same problem, with an application, where i need to authentificate towards a ldap directory, only. In the old beta, i did this with the onLogin event out of the example.php. but now i have to synchronize the accounts, at the one hand the ldap users and at the other the joomla accounts. because i have to forward the password to a local daemon of the application, i have to integrate it. is there a common way or a possiblity to externalize the user/group management of joomla. i would love it, if i could leave that in a ldap directory? greetz mario |
|
| Author: | Jinx [ Tue Mar 27, 2007 12:13 pm ] |
| Post subject: | Re: Implementing Single Sign-On |
Boojam wrote: i got the same problem, with an application, where i need to authentificate towards a ldap directory, only. In the old beta, i did this with the onLogin event out of the example.php. This still works. Quote: is there a common way or a possiblity to externalize the user/group management of joomla. i would love it, if i could leave that in a ldap directory? Externalising the whole user/group management is not going to be possible in 1.5. We need a more pluggable and extendable ACL API for that. What you can do is use the user and authentication plugins to store your user information in another data store. You try to experiment with creating a seperate component to handle advanced user management. Depeding on what you exactly need this could be a solution. If you just need to sync account then the plugins architecture of 1.5 should get it done for you. I have experimented with this in the past, for example the User - Joomla! plugin has a auto-create user setting which creates users on the fly when they try to login for the first time. I have tested this against the different authentication plugins and it worked great. This might be a solution for you too ? |
|
| Author: | Boojam [ Tue Mar 27, 2007 12:23 pm ] |
| Post subject: | Re: Implementing Single Sign-On |
Thanx Jinx, my approach was just too complicated. for sure. now i see it clear, thanx alot. sometimes my head is full of [censored]. :-) ok, by the way i try to integrate the application http://www.openexchange.com works fine. greetz mario |
|
| Author: | miallen [ Tue Mar 27, 2007 1:18 pm ] |
| Post subject: | Re: Implementing Single Sign-On |
Jinx wrote: onAfterStart doesn't exist anymore for beta2, you could use onAfterInitialise or onAfterRoute to capture the request, the difference is that onAfterRoute get's called after the application has routed itself but before it get's dispatched. What exactly do you need to do that you need to have access to the executing flow that early ? To properly implement HTTP Negotiate and NTLM protocols (aka IWA supported by IE) there needs to be event triggered before *anything* else. This is because of an awkward flow of requests and responses. When a client first issues a request the server checks for a 'WWW-Authenticate: Negotiate' header. If it is not present an 'Authenticate: Negotiate' header is set and a 401 Unauthorizes response is sent back to the client. This indicates to the client that they should resubmit the request with an 'Authenticate: Negotiate Because of this fact, it is common to simply call die() when sending the initial 401. Because of that, there should not be any initialization or any work performed beforehand as it would be totally unecessary and may even interfere with the authentication protocol (e.g. setting headers). Would it be possible to make this change? |
|
| Author: | Jinx [ Tue Mar 27, 2007 2:51 pm ] |
| Post subject: | Re: Implementing Single Sign-On |
In that case, you can just use the onAfterInitialise event fired by the application. This system event is the first event fired by the application and it is as high as we can go. For a proof of concept you can study the cache plugin in 1.5. It uses the onAfterRoute event to send an already cached page. You will need to do almost the same thing in your plugin to handle HTTP auth headers. Especially have a look at line 82-88 in the cache.php file. Here we interrupt the default application flow. This should get you started. |
|
| Author: | miallen [ Tue Mar 27, 2007 3:55 pm ] |
| Post subject: | Re: Implementing Single Sign-On |
Jinx wrote: In that case, you can just use the onAfterInitialise event fired by the application. This system event is the first event fired by the application and it is as high as we can go. Ok. But realize *every* request is authenticated. Performance will suffer if initialization is performed only to call die() with every request. But I have it working with onAfterInitialise so I'm happy. However, there is a new awkward situation. After SSO has taken place I need to set the user's information. It seems that procedure is somewhat specific to JAuthenticateResponse. I'm digging into that now but can someone direct me as to how I might do that from within a system handler as opposed to an authenticate handler? Thanks, Mike |
|
| Author: | Jinx [ Tue Mar 27, 2007 4:40 pm ] |
| Post subject: | Re: Implementing Single Sign-On |
miallen wrote: Ok. But realize *every* request is authenticated. Performance will suffer if initialization is performed only to call die() with every request. We are still looking at optimising the applicaiton flow so that no more is loaded then needs to be loaded at a specific point during the cycle. If you turn on debugging you can see how long the app needs to reach a certain trigger point in it's flow. Things can always improve but with the current architecture you will need to rely on these plugin triggers and we will need to load a few libs to get all in place to handle them. Quote: However, there is a new awkward situation. After SSO has taken place I need to set the user's information. It seems that procedure is somewhat specific to JAuthenticateResponse. I'm digging into that now but can someone direct me as to how I might do that from within a system handler as opposed to an authenticate handler? Well, there are a few ways of handling this. You could create a authentication plugin for you specific needs and only publish that one. If you do it that way you can still use the authentication API and fire it through the $mainframe->login event which will do all the hard work for you. Would that do the trick ? |
|
| Author: | miallen [ Tue Mar 27, 2007 5:07 pm ] |
| Post subject: | Re: Implementing Single Sign-On |
Jinx wrote: Well, there are a few ways of handling this. You could create a authentication plugin for you specific needs and only publish that one. If you do it that way you can still use the authentication API and fire it through the $mainframe->login event which will do all the hard work for you. Would that do the trick ? Let me explain something that might not be obious to people (not necessarily you but other people who may be following). Single Sign-On means that the user enters their credentials a "single" time when they login to their workstation (via Ctrl-Alt-Del for example). After that, they have what's called a Kerberos TGT that can be used like a password to access all other reasources on the network for up to 10 hours. The objective is to more convenient since the user only actually types in a password once when they come in in the morning (and it's more secure). Anyway, with that out of the way, I hope it is clear that an authentication plugin cannot really be used since at no time would the user actually login [1]. However, if I understand you correctly I believe you are suggesting that I might fire a onLoginUser event artifically? From looking at the code, it seems one solution would be to perform SSO in the system plugin and then check the session for something that indicates the user's information has already been set. If it has, no information is set. If it has not an onLoginUser event is triggered from within the system plugin. Does that seem like a reasonable solution? Mike [1] They could also login using explicitly provided credentials (e.g. to provide alternative credentials) and our plugin will support that as well. |
|
| Author: | Jinx [ Tue Mar 27, 2007 6:46 pm ] |
| Post subject: | Re: Implementing Single Sign-On |
Quote: Anyway, with that out of the way, I hope it is clear that an authentication plugin cannot really be used since at no time would the user actually login The user won't but the system will. Instead of firing the login function through the UI (submitting the login form) you let the system plugin fire it based on the information in the request. If no authentication info is present you simply proceed as normal (anon user) if info is present you force the authenticate and login events through the JApplication::login function. You can fire this function from your system plugin to force the application to handle it. The login function will fire the authentication plugins and user plugins in sequence. All you then need to do is create a kerberos authentication plugin. If you use this approach users will be able to be auto-logged in or they can still login through the UI. |
|
| Author: | miallen [ Wed Mar 28, 2007 6:11 am ] |
| Post subject: | Re: Implementing Single Sign-On |
Jinx wrote: ... you force the authenticate and login events through the JApplication::login function. That worked pretty well. If anyone is using Active Directory we have placed the plugin info here (for now): http://www.ioplex.com/d/readme_plexcel_ ... plugin.txt It's actually two plugins, one is a "system" plugin for doing the SSO part and the other is the authentication plugin which is called by the SSO plugin and by the UI. We would be happy to work with anyone who tries this. It seems to work perfectly on two systems we have here. It would be nice if the extension installer could enumerate and install multiple plugins from the same zip file. Has anyone given any thought to doing that? Thanks for your help Johan. No doubt I'll be back. Now I'm looking at trying to get contacts from AD. Mike |
|
| Author: | pvh123 [ Wed Mar 28, 2007 11:32 am ] |
| Post subject: | Re: Implementing Single Sign-On |
Mike, I have been working with sso and security tools in the past and I was just wondering a practical point: What if the user changes its password? I suppose it will only be its Joomla user PW and will be aggregated right down into kerberos, ending up in the apps feeded by Kerberos? Pieter |
|
| Author: | Jinx [ Wed Mar 28, 2007 12:29 pm ] |
| Post subject: | Re: Implementing Single Sign-On |
miallen wrote: It's actually two plugins, one is a "system" plugin for doing the SSO part and the other is the authentication plugin which is called by the SSO plugin and by the UI. We would be happy to work with anyone who tries this. It seems to work perfectly on two systems we have here. You might want to consider putting this plugin up on the extension site for download by other people. Quote: It would be nice if the extension installer could enumerate and install multiple plugins from the same zip file. Has anyone given any thought to doing that? This is a feature we have on the list for a future version, there is a workaround for components but not for plugins and modules. Quote: Thanks for your help Johan. No doubt I'll be back. Now I'm looking at trying to get contacts from AD. Great to hear things are working, means we designed it properly
|
|
| Author: | miallen [ Wed Mar 28, 2007 1:47 pm ] |
| Post subject: | Re: Implementing Single Sign-On |
pvh123 wrote: Mike, I have been working with sso and security tools in the past and I was just wondering a practical point: What if the user changes its password? I suppose it will only be its Joomla user PW and will be aggregated right down into kerberos, ending up in the apps feeded by Kerberos? Pieter From a practical authentication prespective it shouldn't matter if someone changes their password. For SSO the client will submit a new but otherwise perfectly valid GSSAPI token. For UI logon their session will techinically be dated but should work fine. However, we chose to only set the user's information for the first of any SSO authentications on a session. So if they change something like a phone number in AD it may be the old value until they press 'Logout' or close and reopen their browser. Currently there is no hook in our plugin for changing passwords. But considering the Plexcel extension has a plexcel_set_password function it would be trivial to implement. Actually there are many features offered by the Plexcel extension that could be used in a variety of interesting ways. Here's an overview: http://www.ioplex.com/plexcel.html I'm still learning about how Joomla! so if anything sounds like it would fit well let me know how. Mike |
|
| Author: | miallen [ Thu Mar 29, 2007 2:28 am ] |
| Post subject: | Re: Implementing Single Sign-On |
Jinx wrote: Great to hear things are working, means we designed it properly :) Unfortunately I think I've found a problem that I can't get around. If you recall, I mentioned that an HTTP Kerberos SSO authentication is actually two requests. The first request is rejected with a 401 and a special header. That triggers the client to re-submit the request with a base 64 encoded token. Ideally, the first request should be intercepted before anything else. It should literally be the first thing executed by the script. But as you know that's not possible with the current code. The earliest opportunity for a plugin to act is in the onAfterInitialise event. Currently when the onAfterInitialise handler of our SSO plugin needs to reject with a 401, we call die() because we do not want to send any unnecessary content in the body and we want the script to perform as little processing as possible. Unfortunately calling die() in onAfterInitialise causes a problem with sessions. This is reproducible when someone clicks 'Logout'. In this case the session is destroyed and a redirect is sent to the client. The subsequent request is received, the session initialization code runs, a default JUser is placed into the session and, as usual, the onAfterInitialise handler rejects the request with 401 and calls die(). This is where the problem occurs. The client re-submits the request with the auth token but when onAfterInitialise is called JFactory:getUser returns NULL because $session->get('user') is not defined. I'm not sure exactly why it's not set but apparently after the default JUser is set the die() call screws it up. I guess the die() call prevented the session from being saved properly. If I remove the die() call the SSO filter works. However, it sends the client the entire page even though we're just sending a 401 Unauthorized. Meaning for each request the page is rendered twice. That's not ok. There really needs to be a hook at line 0 of index.php. That was true before I ran into this problem. Obviously you can't use your existing plugins and events infrastructure to do that but you could create a special plugins directory called 'start' something like that that simply calls a function of a predefined name found in files in that directory. For example I would have a file: plugins/start/plexcel.php This would be loaded and the function named 'plexcel_start' would be called. In my case this function would check apache_request_headers for the authentication token. If present it set's the status to 401 and calls die(). If it is present it does nothing and the real work is performed in onAfterInitialise as usual. If this solution would be acceptable I would be more than happy to provide the implementation. It would all be very simple I think. Otherwise, doing Kerberos or NTLM or any other mutli-request HTTP authentication protocol in J! is going to be so slow I would have no other choice but to require users to patch J! before they can use our plugin. That would not go over well. Mike |
|
| Author: | ianmac [ Thu Mar 29, 2007 2:53 am ] |
| Post subject: | Re: Implementing Single Sign-On |
While I agree that having to render twice is quite inefficient, I wonder how often this occurs? It may be a lot, but the impression I got from reading your posts is that this would happen once per user per 10 hours? Or is this not the case? EDIT: Sorry, just reread your above post, nix that. While still not ideal, I suppose, have you tried using the $mainframe->close() method? http://api.joomla.org/Joomla-Framework/Application/JApplication.html#close This method will close the session and will then invoke the exit call http://ca3.php.net/manual/en/function.exit.php. I don't know if this helps at all, but just thoughts that came that you might experiment with. Ian |
|
| Author: | ianmac [ Thu Mar 29, 2007 3:05 am ] |
| Post subject: | Re: Implementing Single Sign-On |
As another alternative, to get a little bit closer, you could create a system plugin (if that is possible - presumably it is) and include your check code in the system plugin. This would move your call up to before the application is initialized (which may not be that much sooner, but still sooner). Ian |
|
| Author: | miallen [ Thu Mar 29, 2007 3:35 am ] |
| Post subject: | Re: Implementing Single Sign-On |
ianmac wrote: While still not ideal, I suppose, have you tried using the $mainframe->close() method? This method will close the session and will then invoke the exit call. I don't know if this helps at all, but just thoughts that came that you might experiment with. Hi Ian, Yes, that does help. With $mainframe->close() in place of die() the plugin now behaves correctly when Logout is invoked. The session appears to be saved properly. I can live with this solution. Sorry I didn't check the documentation more closely. However, I still would really like to see something like the following at the top of the index.php's: Code: foreach (glob('plugins/init/*.php') as $path) { include($path); $fname = substr($path, 13, strpos($path, '.') - 13) . '_init'; $fname(); } This really is not very intrusive and it would be a very significant performance improvement for multi-request SSO plugins (consider that a mysql query occurs before the onAfterInitialise handler is called). Mike PS: Yes, we are using a system plugin. My understanding is onAfterInitialise is the first event fired. |
|
| Author: | ianmac [ Thu Mar 29, 2007 3:52 am ] |
| Post subject: | Re: Implementing Single Sign-On |
miallen wrote: Yes, that does help. With $mainframe->close() in place of die() the plugin now behaves correctly when Logout is invoked. The session appears to be saved properly. I can live with this solution. Sorry I didn't check the documentation more closely. Documentation is one thing, but it helps sometimes to know where to look. I had come across it before, and that is what triggered this. Quote: However, I still would really like to see something like the following at the top of the index.php's: Code: foreach (glob('plugins/init/*.php') as $path) { include($path); $fname = substr($path, 13, strpos($path, '.') - 13) . '_init'; $fname(); } This really is not very intrusive and it would be a very significant performance improvement for multi-request SSO plugins (consider that a mysql query occurs before the onAfterInitialise handler is called). Mike Can't say too much about that... Quote: PS: Yes, we are using a system plugin. My understanding is onAfterInitialise is the first event fired. What I meant by that is that to import the system plugin, all that really happens is that the file is included. So this is probably bad form, and not recommended, but you could include code in that file outside of your classes and functions that would perform the check that needs to be done. This would not trigger onto an actual event, and so this part wouldn't work as a plugin per se, but only take advantage of where the plugins are being included from (i.e. the file is included using require_once, and then the class or function is called later when the plugin is actually invoked). If you look at the index.php, you will see where the system plugins are imported. Hope this is clearer than what I said before. Ian |
|
| Author: | miallen [ Thu Mar 29, 2007 7:02 am ] |
| Post subject: | Re: Implementing Single Sign-On |
ianmac wrote: What I meant by that is that to import the system plugin, all that really happens is that the file is included. So this is probably bad form, and not recommended, but you could include code in that file outside of your classes and functions that would perform the check that needs to be done. I see. Well that does not bypass the database query so it will have little impact on performance (I tried to move the importPlugin('system') statement just above $mainframe->loadConfiguration() but it choked). Mike |
|
| Author: | ianmac [ Thu Mar 29, 2007 1:43 pm ] |
| Post subject: | Re: Implementing Single Sign-On |
Yeah... I wasn't sure if it would save you much or not, but it was just a suggestion. Ian |
|
| Author: | miallen [ Fri Mar 30, 2007 1:20 am ] |
| Post subject: | Re: Implementing Single Sign-On |
Ok, I have a new problem that someone fluent in J! might be able to help with. I have two plugins. One is an onAfterInitialise handler for Kerberos SSO and the other an onAuthenticate handler for Kerberos login via the Login Form. The SSO plugin works fine by itself. The Login Form plugin works fine by itself. But it would be nice if the user could elect to use the login form even though SSO is being successfully used. Contrarily, they should be able to transition back from using the Login Form to doing SSO. I'm having problems with this. At first I tried to add an onLogout handler that set a flag in the user's session to disable SSO reasoning that when they clicked 'Logout', SSO would be disabled and they would get the Login Form. That didn't work because the session is destroyed on logout in the default J! user onLogout handler. Therefore, an alternative place must be used to store the state that indicates the user's preference for SSO or the Login Form. I currently have a GET parameter that may be used to signal that SSO or the login form is desired (?plexcel=1 to disable sso and ?plexcel=2 to enable it) but, to the best of my knowledge, it seems there is no way to modify the URIs emitted throughout the templates such that this parameter would be submitted with subsequent requests by the client (please correct me if I'm wrong). If the parameter used to disable/enable SSO was used to only change the said flag in the user's session, that would help because the parameter would only need to be added to selected links. But then we run into the session being destroyed on logout problem again. So there's no way to call $mainframe->logout or click the Logout button without destroying any state that could be used to disable SSO. The effect is that when you click 'Logout' the subsequent request starts fresh and uses SSO because that is the default behavior. So once you negotiate SSO, there's no way to disable it to get to the Login Form. Does anyone have a suggestion as to how I might get around this problem? Can I use cookies? Is there flag in the user's table I can use? Is there a separate session that persists across logout? Mike |
|
| Author: | Jinx [ Fri Mar 30, 2007 10:58 am ] |
| Post subject: | Re: Implementing Single Sign-On |
Hi Mike, The best solution to this particular problem would be by using a cookie, you can set the cookie in the onLogout event and then make the SSO system bot check it. This is a bit the same solution as the 'remember me' option, also works through a cookie. We don't allow the session to exist after an explicit logout for security reasons. We also don't want people to rely on the session because it could get garbage collected. Hope this helps ? Johan |
|
| Author: | CoolAcid [ Fri Mar 30, 2007 6:29 pm ] |
| Post subject: | Re: Implementing Single Sign-On |
Hey guys, just want to jump in here. from a AAA point - I'd like to include this in the joomlaAAA on the joomlacode site so we have all the AAA stuff in one place. Would you guys mind? I can add you to the project so you can maintain your code online. -- I'll at least be making a reference to this in the project wiki -- Thanks. Jay |
|
| Author: | miallen [ Sat Mar 31, 2007 4:16 am ] |
| Post subject: | Re: Implementing Single Sign-On |
CoolAcid wrote: Hey guys, just want to jump in here. from a AAA point - I'd like to include this in the joomlaAAA on the joomlacode site so we have all the AAA stuff in one place. Would you guys mind? I can add you to the project so you can maintain your code online. Hi Jay et al, First, I want to make sure I've made something clear. I hope I haven't mislead anyone. If I have, my apologies. The real heavy lifting in our plugin(s) is performed by a commercial PHP extension called Plexcel provided by my company IOPLEX Software. I personally have a significant trail of OSS projects to my name (JCIFS is quite popular) but Plexcel is my first commercial project and suffice it to say, it is not Open Source or free (although it is "free as in beer" for up to 25 users). So the plugin code available by itself is not functional without our non-OSS extension. However, we would be happy to contribute any plugin code we come up with. There are a lot of pedantic J! specific details that we've worked out (with much help from this forum - thanks). For example, if the client does not support SSO (e.g. because they are not logged into AD), we set a JavaScript redirect in the error page that causes the request to be re-submit with a parameter that disables SSO so that the Login Form comes up. Similarly, it can be rather tricky to let the user jump between the Login Form and SSO. We used a persistent cookie (yes, that's what we did Johan, thanks) to enable and disable SSO so that the user could get to the Login Form. We will also be looking at contact lists, ACLs and so on. So that knowledge might help you and others integrate and customize J! on large intrAnet type environments where people are using AD, Krb5, OpenLDAP, ...etc. Maybe we can create an abstraction that factors out the implementation of the code that actually does the said "heavy lifting". Anyway, hopefully we can find a middle ground. I have recently updated the plugin on our site. The authentication part is working pretty well at this point. You can read all about it here: http://www.ioplex.com/joomla_plugin.html I have added an entry in the Extensions Directory. From the hits our site has been getting it looks like people are in fact quite interested in all of this. Mike |
|
| Author: | Jinx [ Sat Mar 31, 2007 2:38 pm ] |
| Post subject: | Re: Implementing Single Sign-On |
Hi Mike, No worries. Nice to hear you have been able to sort everything out. I'm sure you can find other ways to contribute to the project, just being here on the forums and answering questions would very much appreciated. Cheers, Johan |
|
| Author: | redndahead [ Thu May 24, 2007 4:55 pm ] |
| Post subject: | Re: Implementing Single Sign-On |
Trying not to hijack this thread with my questions is there a chance, Mike, that you might be able to create an example.php file like the one in the plugins folder for SSO? I'm looking into creating a plugin for CAS and I'm new to Joomla design. So an example or outline if you have the time would help. Thanks Adam |
|
| Author: | miallen [ Thu May 24, 2007 5:57 pm ] |
| Post subject: | Re: Implementing Single Sign-On |
redndahead wrote: I'm looking into creating a plugin for CAS and I'm new to Joomla design. So an example or outline if you have the time would help. Hi Adam, Our plugin code is not terribly complicated I don't think. You're welcome to use it however you like to derive your own plugins. Otherwise I'm not sure what you mean exactly by example.php. The plugins themselves should demontrate everything you should need to know (e.g. intercepting the request after the user management code has been initialized but before the request is authenticated). Mike |
|
| Page 1 of 2 | All times are UTC |
| Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |
|