{"id":428,"date":"2008-04-17T19:06:57","date_gmt":"2008-04-17T18:06:57","guid":{"rendered":"http:\/\/wp.devco.net\/?p=428"},"modified":"2012-01-24T10:58:42","modified_gmt":"2012-01-24T09:58:42","slug":"designing_a_single_sign_on_system_-_part_2","status":"publish","type":"post","link":"https:\/\/www.devco.net\/archives\/2008\/04\/17\/designing_a_single_sign_on_system_-_part_2.php","title":{"rendered":"Designing a Single Sign On system – part 2"},"content":{"rendered":"

This is the 2nd part of my ongoing series of posts about designing a simple Single Signon System for PHP, you should read part 1 first<\/a>.<\/p>\n

I am often annoyed about series of blog posts that don’t make it clear what the end goal is early on, so you end up wasting time reading through loads of stuff only to realise at the end its a bad fit.  So below you’ll see some sample bits of code using my Single Sign On system in PHP and Apache after this you can easily decide to just ignore the rest of the posts or to keep paying attention.<\/p>\n

First as I said the authentication should be pluggable, I want to be able to fetch users from LDAP, MySQL, and any number of other things, towards this goal I made the actual code that does the hard work pluggable by using a simple OO module and an interface.  Below is a bit of code to just always allow a user ‘john’ in with the password ‘secret’, the values for name etc is hardcoded and you can’t change the settings, but you’ll get the basic idea!<\/p>\n


\n<?
<\/span>class <\/span>StupidAuth <\/span>implements <\/span>pSSO_Authenticator <\/span>{
    public function <\/span>_authenticate<\/span>(<\/span>$username<\/span>, <\/span>$password<\/span>) {
        if (<\/span>$username <\/span>== <\/span>\"john\" <\/span>&& <\/span>$password <\/span>== <\/span>\"secret\"<\/span>) {
            return(<\/span>1<\/span>);
        }<\/p>\n

        return(<\/span>0<\/span>);
    }<\/p>\n

    public function <\/span>_getEmailAddress<\/span>() {
        return(<\/span>\"john@doe.net\"<\/span>);
    }<\/p>\n

    public function <\/span>_getRealName<\/span>() {
        return(<\/span>\"John Doe\"<\/span>);
    }<\/p>\n

    public function <\/span>_getUsername<\/span>() {
        return(<\/span>\"john\"<\/span>);
    }<\/p>\n

    public function <\/span>_getTimeZone<\/span>() {
        return(<\/span>\"Europe\/London\"<\/span>);
    }<\/p>\n

    public function <\/span>_setEmailAddress<\/span>() {
    }<\/p>\n

    public function <\/span>_setRealName<\/span>() {
    }<\/p>\n

    public function <\/span>_setUsername<\/span>() {
    }<\/p>\n

    public function <\/span>_setTimeZone<\/span>() {
    }
}
<\/span>?><\/p>\n

<\/span>
\n<\/span>
\n<\/code>It doesn’t really come simpler than that, within this framework you really should be able to do almost any form of authentication, if PHP can talk to it and auth then so should the SSO system.  This code will live in the server, I won’t go much into the server here, it’s just a really a system to wrap the code above into a well defined protocol between client and server, more on this some other day.  For now just assume there is a config file on the server and you tell it what Class implements the actual auth – StupidAuth in this case.<\/p>\n

Now for a quick client, remember the client can run anywhere on any domain, and I want my clients to be registered with me before they can use the SSO system.  To this end each client has a Pre Shared Key (PSK) and a unique ID.  The PSK is used to encrypt the communications from the SSO server to the SSO client as the reply will have real names, email addresses and such in it, you don’t want this to show up in proxy logs and such!<\/p>\n

Here’s a quick client:<\/p>\n


\n<?
    <\/span>require(<\/span>\"pSSO_Client.class.php\"<\/span>);<\/p>\n

    <\/span>$psk <\/span>= <\/span>\"goG4mUrJeacE7VyidEfd\"<\/span>;
    <\/span>$siteid <\/span>= <\/span>1<\/span>;
    <\/span>$ssoServer <\/span>= <\/span>\"http:\/\/sso.yourcompany.com\/\"<\/span>;
    <\/span><\/span><\/code>$thisURL <\/span>= <\/span>\"http:\/\/\" <\/span>. <\/span>$_SERVER<\/span>[<\/span>\"HTTP_HOST\"<\/span>] . <\/span>$_SERVER<\/span>[<\/span>'SCRIPT_NAME'<\/span>];<\/p>\n

<\/span><\/span><\/code>    <\/span>session_start<\/span>();<\/p>\n

    <\/span>\/\/ the SSO server sent us back a token, validate it and set cookies
    <\/span>if (isSet(<\/span>$_GET<\/span>[<\/span>'authdata'<\/span>]) && (<\/span>$_GET<\/span>[<\/span>'v'<\/span>])) {
        <\/span>$psso <\/span>= new <\/span>pSSO_Client<\/span>(<\/span>$_GET<\/span>[<\/span>'authdata'<\/span>], <\/span>$_GET<\/span>[<\/span>'v'<\/span>], <\/span>$psk<\/span>, <\/span>
                                     $siteid<\/span>, <\/span>$ssoServer<\/span>);
        
        if (<\/span>$psso<\/span>-><\/span>authenticate<\/span>()) {
            <\/span>\/\/ The user is logged in, send him back to this same url
            \/\/ except without any GET params etc, so he'll be a normal
            \/\/ returning logged in user.
            <\/span><\/span><\/code>header(<\/span><\/span><\/code><\/span>\"Location: \" <\/span>.<\/span><\/span><\/code>$thisURL<\/span><\/span><\/code>);<\/span>
            <\/span><\/code>exit;<\/span><\/span><\/code>
        <\/span>} else {
            <\/span>\/\/ login failed, eventhough it shouldn't have, bail out
            <\/span>Throw new <\/span>Exception <\/span>(<\/span>\"Login failed:\" <\/span>. <\/span>$psso<\/span>-><\/span>getError<\/span>());
        }
    } else {
        <\/span>\/\/ We didn't get a token, either its a guest or he already has cookies
        \/\/ from a previous visit
        <\/span>$psso <\/span>= new <\/span>pSSO_Client<\/span>(<\/span>\"\"<\/span>, <\/span>\"\"<\/span>, <\/span>$psk<\/span>, <\/span>$siteid<\/span>, <\/span>$ssoServer<\/span>);
        if (<\/span>$psso<\/span>-><\/span>isLoggedIn<\/span>()) {
            print(<\/span>\"You are logged in:<br><br>\"<\/span>);
            print(<\/span>\"Your 
\nusername is: \" <\/span>. <\/span>$psso<\/span>-><\/span>getUserName<\/span>() . <\/span>\"<br>\"<\/span>);
            print(<\/span>\"Your real name is: \" <\/span>. <\/span>$psso<\/span>-><\/span>getRealName<\/span>() . <\/span>\"<br>\"<\/span>);
            print(<\/span>\"Your email address is: \" <\/span>. <\/span>$psso<\/span>-><\/span>getEmailAddress<\/span>() . <\/span>\"<br>\"<\/span>);
            print(<\/span>\"Your timezone is: \" <\/span>. <\/span>$psso<\/span>-><\/span>getTimeZone<\/span>() . <\/span>\"<br>\"<\/span>);
            exit;
        } else {<\/span>
            print(<\/span>\"Welcome guest, you can login <a href='\" <\/span>. <\/span>$psso<\/span>-><\/span>getAuthURL<\/span>(<\/span>$thisURL<\/span>)
                . <\/span>\"'>here<\/a>\"<\/span>);
            exit;
        }
    }
<\/span>?><\/p>\n

<\/span><\/span><\/code>A quick run through the code:<\/p>\n