Associated websites often share user information, so a visitor only has to register once and can use that username and password for all sites. A good example for this is Google. You can use you google account for GMail, Blogger, iGoogle, google code, etc. This is nice, but it would be even nicer if logging in for GMail would mean I’m also logged in for the other websites. For that you need to implement single sign-on (SSO).

There are many single sign-on applications and protocols. Most of these are fairly complex. Applications often come with full user management solutions. This makes them difficult to integrate. Most solutions also don’t work well with AJAX, because redirection is used to let the visitor log in at the SSO server.

I’ve written a simple single sign-on solution (400 lines of code), which works by linking sessions. This solutions works for normal websites as well as AJAX sites.

Without SSO

Let’s start with a website that doesn’t have SSO.
No SSO
The client requests the index page. The page requires that the visitor is logged in. The server creates a new session and sends redirect to the login page. After the visitor has logged in, it displays the index page.

How it works

When using SSO, when can distinguish 3 parties:

  • Client – This is the browser of the visitor
  • Broker – The website which is visited
  • Server – The place that holds the user information

The broker will talk to the server in name of the client. For that we want the broker to use the same session as the client. However the client won’t pass the session id which it has at the server, since it’s at another domain. Instead the broker will ask the client to pass a token to the server. The server uses the token, in combination with a secret word, to create a session key which is linked session of the client. The broker also know the token and the secret word and can therefore generate the same session key, which it uses to proxy login/logout commands and request info from the server.

First visit

-> Try it! (jan/jan1)<-

When you visit a broker website, it will check to see if a token cookie already exists. It it doesn’t it, the broker sends a redirect to the server, giving the command to attach sessions and specifying the broker identity, a random token and the originally requested URL. It saves the token in a cookie.

The server will generate a session key based on the broker identity, the secret word of the broker and the token and link this to the session of the client. The session key contains a checksum, so hackers can go out and use random session keys to grab session info. The server redirects the client back to the original URL. After this, the client can talk to the broker, the same way it does when not using SSO.

The client requests the index page at the broker. The page requires that the visitor is logged in. The broker generates the session key, using the token and the secret word, and request the user information at the server. The server responds to the broker that the visitor is not logged. The broker redirects the client to the login page.

The client logs in, sending the username and password to the broker. The broker sends the username and password to the server, again passing the session key. The server returns that login is successful to the broker. The broker redirects the client to the index page. For the index page, the broker will request the user information from the server.

Visiting another affiliate

-> Try it! <-

You visit another broker. It also checks for a token cookie. Since each broker is on their own domain, they have different cookies, so no token cookie will be found. The broker will redirect to the server attach to the user session.

The server attaches a session key generated for this broker, which differs from the one for the first broker. It attaches it to the user session. This is the same session the first broker used. The server will redirect back to the original URL.

The client requests the index page at the broker. The broker will request user information from the server. Since the visitor is already logged in, the server returns this information. The index page is shown to the visitor.

Using AJAX / Javascript

-> Try it! <-
SSO and AJAX / RIA applications often don’t go well together. With this type of application, you do not want to leave the page. The application is static and you get the data and do all actions through AJAX. Redirecting an AJAX call to a different website won’t because of cross-site scripting protection within the browser.

With this solution the client only needs to attach the session by providing the server with a token generated by the broker. That attach request doesn’t return any information. After attaching the client doesn’t talk at all to the server any more. Authentication can be done as normal.


The client check for the token cookie. It it doesn’t exists, he requests the attach URL from the broker. This attach url includes the broker name and the token, but not a original request URL. The client will open the received url in an <img> and wait until the image is loaded.

The server attaches the browser session key to the user session. When it’s done it outputs a PNG image. When this image is received by the client, it knows the server has connected the sessions and the broker can be used for authentication. The broker will work as a proxy, passing commands and requests to the sso server and return results to the client.

To conclude

By connecting sessions, you give the broker the power to act as the client. This method can only be used if you trust all brokers involved. The login information is send through the broker, which he can easily store if the broker has bad intentions.

Don’t be square, please share!

Demo
Broker ‘Alex’
Broker ‘Binck’
AJAX broker – created by Lukasz ‘Uzza’ Lipinski using Ajax.org PlatForm.

Play around, logging in and out at different brokers. Refresh the other after. Available users:
jan / jan1
peter / peter1
bart / bart1
henk / henk1

Download
Download the code @ github
2009-11-16: Updated the software with bugfixes mentioned in comments + alternative for using symlinks.

If I overlooked security issues with this SSO implementation, please leave a comment below.

This is a simple implementation of SSO. If you want enterprise stuff have a look at Novell Single Sign-On.

  197 Responses to “Simple Single Sign-On for PHP (Ajax compatible)”

  1. I found another problem. This solutions works if each website has own domain. When you try to set up 2 applications in the same domain, i.e. example.com/ and example.com/app2/ this will not work becouse the website user enters firstly creates cookie (session_token) and second application will use it while asking SSO for authentication.

    The solution is to set diffrent names for session_token cookie for each broker, for example session_token_brokername.

    In this case it works for the same and for diffrent domains.

    A.P.

    ReplyReply
  2. Adrian Pawlik The whole reason of using this SSO solution is so you can share session information (like user credentials) across multiple domains. There is no good reason to use this on a single domain (other than testing purposes).

    ReplyReply
  3. Finally !
    Something that works !

    Nice tutorial, you saved many peoples time
    Not even one tutorial, except this one, on the subject of shareing cookies, sessions across multiple domains.

    Good work ^_^

    ReplyReply
  4. Hi Arnold,

    Firstly, congratulations on some great coding – I’ve implemented your solution on a range of my blogs and it’s working wonderfully. I’ve done a lot of research into SSO and your solution is by far the most efficient and easy to implement.

    The only downside that I’ve encountered so far is problems with persistence when the browser is closed, reopened, and a new session is started. Logins persist for domains that have been visited with the previous session, but not for domains that haven’t been visited in the previous (logged in) session.

    For example:
    exampleA.com, exampleB.com and exampleC.com are all using your SingleSignOn_Broker class (php, not ajax) to check whether a client is logged in. Assume the sso server is sso.example.com (although I don’t think it’s relevant).

    A client logs in via exampleA.com, then goes to exampleB.com….. the client will still be logged in.

    However, if the client then closes the browser (killing the current session) and reopens it, the client will still be logged in to exampleB.com, but if they go to exampleC.com, they will no longer be logged in. Bear in mind that if the client doesn’t close the browser, then they appear as logged in across A B and C.

    I’ve tried to extend your code to fix this problem but without success. Have you got any ideas? I’d really appreciate it.

    Thanks – Paul.

    ReplyReply
  5. Hi Arnold,

    its really great code!!

    i want to try this with CodeIgniter, wish me luck :D

    regards

    ReplyReply
  6. Is this code meant to crash when using PHP’s session_start() function. I use a PHP session to maintain persistence of data within my site but when I have the function running it seems to stop the SSO code working, holds it in an infinate lock where no redirects are occuring, it just refuses to load the page, it acts like a super slow connection, but when I remove the session_start() function it works fine. I have tried figuring out why it iss doing this but cant find where in the classes the session_start() function is clashing with other code. Do you have any ideas Arnold?

    ReplyReply
  7. Arnold, I wanted to extend script to keep user logged in for longer period of time, by setting cookie with expire time for one year.
    Are there any drawbacks I could encounter?
    How would you do that?

    Thanks

    ReplyReply
  8. ob1 That won’t work because the session on the SSO server will already be expired.

    You should solve this, as you would solve it without SSO. Let the SSO server write a cookie with the username + auth hash and check that to automatically log in.

    ReplyReply
  9. Will Do.
    Tnx

    ReplyReply
  10. Hi Arnold,

    great code.

    One question: Is there any simple possibility to secure the login? The username and the password is hand over in plain text.
    I think one possibility is to secure each broker. But we have arround 50 brokers so that would be a very expensive solution.
    Do you have any idea or advise?

    Thanks in advance,
    chris

    ReplyReply
  11. chris You could send over an authorization hash instead of the password. This can still be snatched through a man in the middle attack, but by using a timestamp it’s only valid for a few seconds.

    http://www.jasny.net/articles/authentication-without-sessions/

    ReplyReply
  12. Thanks Arnold Daniels. I have wasted my three important days, but not get any Script. Finally I have used your script for “Single Sign On”. Its really quite simple & easy to use.

    Thanks again.

    ReplyReply
  13. Hi, Arnold

    What’s this error mean “String could not be parsed as XML”. What should i do?

    Thanx,

    ReplyReply
  14. Great..!

    Thanks for the codes :)

    ReplyReply
  15. I want single sign-on is using the mysql database.
    is there any script.. help me
    thanks you :)

    ReplyReply
  16. Warning: SimpleXMLElement::__construct() [function.SimpleXMLElement---construct]: Entity: line 2: parser error : Extra content at the end of the document in C:\xampp\htdocs\ssoprovider\sso.php on line 143

    Warning: SimpleXMLElement::__construct() [function.SimpleXMLElement---construct]: Parse error: syntax error, unexpected ‘”‘ in C:\xampp\htdocs\server\s in C:\xampp\htdocs\ssoprovider\sso.php on line 143

    Warning: SimpleXMLElement::__construct() [function.SimpleXMLElement---construct]: ^ in C:\xampp\htdocs\ssoprovider\sso.php on line 143

    Fatal error: Uncaught exception ‘Exception’ with message ‘String could not be parsed as XML’ in C:\xampp\htdocs\ssoprovider\sso.php:143 Stack trace: #0 C:\xampp\htdocs\ssoprovider\sso.php(143): SimpleXMLElement->__construct(‘?Parse…’) #1 C:\xampp\htdocs\ssoprovider\sso.php(158): SingleSignOn_Broker->parseInfo(‘?Parse…’) #2 C:\xampp\htdocs\ssoprovider\login.php(8): SingleSignOn_Broker->getInfo() #3 {main} thrown in C:\xampp\htdocs\ssoprovider\sso.php on line 143

    ReplyReply

  17. Dear friend,

    good article but i have prolem

    When i use session in php.ini with memcache

    How to change code support memcahce session in php.ini

    Thank

    ReplyReply
  18. guntorThe result from the SSO server isn’t valid XML. Probably there is a server error. Output the result from curl and see what is wrong.

    Lang Remove line 168 to 172. Now look at the part where I’m attaching using a file. Replace that with code using memcache.

    On line 78 remove the isset() and replace the file_exists and file_get_contents with memcache.

    ReplyReply
  19. I want to change database
    protected static $users = array(
    ‘jan’ => array(‘password’=>”jan1″, ‘fullname’=>”Jan Smit”, ‘email’=>”jan@smit.nl”),
    ‘peter’ => array(‘password’=>”peter1″, ‘fullname’=>”Peter de Vries”, ‘email’=>”peter.r.de-vries@sbs.nl”),
    ‘bart’ => array(‘password’=>”bart1″, ‘fullname’=>”Bart de Graaf”, ‘email’=>”graaf@bnn.info”)
    );

    with database mysql.. is there any script.. help me

    ReplyReply
  20. Hello Arnold,

    First of all, well coded script! I have read all the comments about the ‘Not attached’ error, but I still cannot seem to figure it out. My client is running Windows Server 2003 and my server is running Linux.

    Symlink is working and also seems to attach. When using fiddler I can see a sequence of requests (sso.php?cmd=attach) from the client to the server, all with a different broker and checksum and all being redirected to the client index.php.

    Error reporting (error_reporting(E_ALL)) is enabled in the server script. The only response I get from the client index.php is:

    Fatal error: Uncaught exception ‘Exception’ with message ‘SSO failure: The server responded with a 406 status: “Not attached”.’ in D:\…\sso\sso.php:162 Stack trace: #0 D:\…\sso\index.php(6): SingleSignOn_Broker->getInfo() #1 {main} thrown in D:\…\sso\sso.php on line 162

    I did have some problems with mod_security in the beginning, but after adding some exceptions, I do not recieve errors from this anymore.

    I have tried pretty much all the suggetions in the above comments. Do you have any idea what I can try to do?

    ReplyReply
  21. Guntur There is no script I know of Please read comment 87.

    Erik Kloeze Check the symlinks. If they are being created correctly, output the contents of the session files. Is there a variable ‘client_addr’. Also make sure the server writes the error to the (Apache) error log.

    ReplyReply
  22. The symlink seems to be created, as there is no “Failed to attach; Symlink wasn’t created.” error.

    In sessionStart, ‘User session’ there is a client_addr mentioned, in the ‘Broker session’ after the session_start there isn’t. After the symlink is created, there also is a client_addr in the session file.

    I think that altough there is a symlink, the ‘old’ session simply isn’t picked up? Adding session_name($sid) before the session_start in the ‘Broker session’ also did not work out.

    For the errors, as far as I know, all errors show up in the Plesk Log Files.

    Any ideas left?

    ReplyReply
  23. Hello Arnold,

    I did some additional debugging, outputting the session_id in the attach function and the sessionstart function.

    When I use the readlink function in the sessionstart function, I get the same session_id as in the attach function. Futhermore, the link variable in the attach function is equal to the session_id in the session_start function (besides the path).

    But it still is not working?

    ReplyReply
  24. Erik Kloeze Login using SSH and check what is happening.

    ReplyReply
  25. Dude you got a lot of patience answering these questions eh

    ReplyReply
  26. Greetings,

    i’ve try ur work but it seems doesn’t work in multiple sub domain?
    what do i have to change if the applicationt are in multiple sub domains?

    Tks

    ReplyReply
  27. Wawam If it doesn’t work for sub domain, but does work for completely different domains, your session cookie is valid for the whole domain. This is set with the cookie-domain ini directive.

    If you only need to share a session over sub domains and not over different domains, don’t use this SSO solution at all. Simply share the PHP session for the different sites.

    ReplyReply
  28. Thanks, for your answer. Very appreciate your works, and I learned a lot from it.

    ReplyReply
  29. Have you ever tried this with PHP and .NET. I am looking for a SSO between a PHP website and a .NET website. Can you think of any reasons why this wouldn’t work with both?

    ReplyReply
  30. Goose No, that should work fine. Set up an SSO server using PHP. You would need to write a broker in .NET. It needs to redirect to the SSO server and than request the data using HTTP, just like the PHP broker.

    ReplyReply
  31. Dear Sir,

    Pls help me change code support session store on memcache
    When i change php.ini file use session memcache code error

    I look and research but i dont can change code support memcache
    i read all comment of vistit here but i can not change my code support my memcache

    Pls help me

    Thank verry much

    Ana

    ReplyReply
  32. For those worrying about SSL certificates for your brokers, perhaps you may consider redirecting the client to the server for authentication, initiating the session on the server then redirecting the client back to the broker?

    ReplyReply
  33. Hello everyone,

    Thanks to the developers for this script; I think it’s the answer to my SSO implementation troubles, but I can’t get my head round the installation. Apologies for asking what may appear to others to be quite “basic” questions, I’m no web guru but can get things done once I see the instructions in human-readable language :-)

    I have 2 web sites, already set up and running, both LAMP based with separate but synchronized user databases (same usernames and passwords on both). They are on two separate domains.

    What I have done so far:

    1. Created subdomain for each site: sso.domain1.com and sso.domain2.com.

    2. Uploaded the SSO files to both subdomains, but putting the brokers in the root of each subdomain, along with the index and login files.

    3. Edited each broker sso.php to reference its own server/sso.php e.g. broker sso.php on sso.domain1.com has the entry

    public $url = “http://sso.domain1.com/server/sso.php”;

    4. Fill in the other details.

    5. Go to http://sso.domain1.com in my browser. See a login page, which I fill with credentials in the server sso.php on domain1.com.

    6. Script returns identity, fullname and email, with a logout link.

    7. Now for the really important bit: I go to http://sso.domain2.com, and because I have the same “protected static $brokers” and “protected static $users” data on both server sso.php files, I was not expecting to be asked to log in again.

    8. Script takes me to login page on sso.domain2.com. I don’t log in but when I refresh the browser, I get this fatal error:

    Fatal error: Uncaught exception ‘Exception’ with message ‘SSO failure: The server responded with a 406 status: “Not attached”.’ in /home/bizemail/.weh/sso.php:161 Stack trace: #0 /home/bizemail/.weh/index.php(5): SingleSignOn_Broker->getInfo() #1 {main} thrown in /home/bizemail/.weh/sso.php on line 161

    I know I’m doing something (maybe many things) wrong, and I’d grateful for pointers to properly install the script.

    Regards,

    Charles.

    ReplyReply
  34. Hi Charles,

    The developers is me :p. So thanks for your praise. Just doing my bit to make the internet world a better place :)

    I looked at what you’re doing. Your creating a multi-master set-up. Where all sites are both server and broker. Though interesting, I think this approach is making the problem complexer than it needs to be.

    Ask yourself the question: Why do I need to have multiple masters?

    Whatever you come up with, you can probably also do with 1 server, even without losing the autonomy. This will make the whole thing much easier ;)

    Let’s take the following: domain1.com is a fully working website. You’ve got your database with your users, an authentication system, etc. Cool…

    Now you’ve got domain2.com which is also a fully working website including database with users, authentication system, etc.

    To share the users across the sites your simply copying them between databases. No magic there… but it works.

    Does this sound about right?

    Now the hard part (which isn’t really that hard). If users login to domain1.com and go to domain2.com you want them automatically to be logged in there as well. That’s what SSO does for you.

    However you want domain1.com as well as domain2.com both handle the authentication by them self and notify the other one. What a pickle :p

    Lets take a slightly different approach. The site on domain1.com stays how it is, but gets the SSO server software as well. The site on domain2.com (and all further domains) get the broker software.

    But you want domain2.com to handle authentication himself you tell me. Ahh, but that’s the easy part. Instead of domain2.com having to set the username and password, just let him send the userid (or username) by modifying function login() on server/sso.php.

    Calling this function will instantly login the user on domain1.com as well.

    Just make sure that only a valid broker can call that method and not some evil hacker.

    Now you only need domain2.com to check at domain1.com is the user is logged in when first visited. Lucky for you, that the broker can already do that :D .

    Told you it was easy.

    ReplyReply
  35. Dear Sir,

    first of all, i would like to thank you for sharing your code with us.
    I would like to ask you if it is possible to test this at localhost.
    For example, i have a server (folder jasny-SSO/server) and two brokers (Alex: jasny2-SSO/broker and Binck: jasny3-SSO/broker).
    I have connected as jan (username: jan, password: jan1) in the url: http://localhost/jasny2-SSO/broker/index.php and then i tried to connected to the url: http://localhost/jasny3-SSO/broker/index.php when i encountered with the login page.
    What i should do in order to make this work.

    Thank you in advance,

    Marios

    P.S: I comment the line ;session.auto_start = 0 in my php.ini file.

    ReplyReply
  36. [...] Canada goose business just as '57 udsalg canada goose . canada goose vest canada goose store canada goose chilliwack bomber canada goose expedition parka canada goose store canada goose montebello parka canada [...]

  37. Dear Sir,

    once again thank you for your code.
    I am writing again because i do not hear from you since today.
    I would like to ask you if it is possible to test your code at localhost.
    I tried to use two brokers and one server at different folders but it does not work.
    I would appreciate if you help me.

    Thank you in advance,

    Marios

    ReplyReply
  38. After studying the model here, i created a slightly different version – using the model to run my site – http://www.dhtmlextreme.net
    I just made a blog-post about 2 models – one that runs across subdomains on a website and another version that runs across multiple domains and subdomains or even within the same website.
    I made a plugin for drupal so that the sso can integrate with drupal –
    my server is http://www.accounts.dhtmlextreme.net
    my brokers are http://www.blogs.dhtmlextreme.net, http://www.classrooms.dhtmlextreme.net and others.

    ReplyReply
  39. Hi Arnold

    I have tried the script, i have to say “wow! its damn great!”
    It work pretty fine in my local and my testing server.
    However, im facing the issue with “406″ stuff, when i place my broker on my local (win7) and sso server on test server (linux). I read every comments above and tried with several fix. yet i cant get it right.

    understand that, this sso suppose to centralise the login auth across domains. im confusing about the _server["REMOTE_ADDR"] as it matching the IP add from local and server IP and final it goes to invalid checksum

    please help and appreciate alots…

    ReplyReply
  40. Yes! I got the issue solved.

    ReplyReply
  41. Hi Arnold…
    I want to implement your code using LDAP, which part should I make a change?
    2nd question, I still confuse with ajax-broker implementation, plus your demo site is not working right now, would you active back the demo site so I can learn from it?
    I really appreciate your kindly help

    Thanks for your help…

    ReplyReply
  42. @Arnold Daniels: Hi Arnold I have the Same problem as Charles. Can you please Explain me wat to modify in server/sso.php as mentioned below

    “But you want domain2.com to handle authentication himself you tell me. Ahh, but that’s the easy part. Instead of domain2.com having to set the username and password, just let him send the userid (or username) by modifying function login() on server/sso.php .”

    ReplyReply
  43. Hi all, the demo’s are currently broken indeed. I need to look at that soon. Also I’ve decided to create a full featured lib from this example, since I’m still getting a lot of response years after writing this article.

    Sorry @Gowtham I didn’t reply to you earlier. Since the broker will do the authentication, it can simply send the userid to the SSO server instead of the username/password.

    ReplyReply
  44. Thank you so much for this toturial ^^

    ReplyReply
  45. Hi! Nice code!
    I have used it to create an SSO-based application using the Yii Framework. It’s cool!
    I would like to ask you one thing. What is the reason of “attaching” to the SSO-Server?
    Why not direclty sending the un-logged client to the SSO-Server for logging?

    Anyway,thanks for sharing the code!

    ReplyReply

 Leave a Reply

(required)

(required)

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code lang=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" extra="">

   

Questions? Just ask!

About the author

Hi, I'm Arnold Daniels. How nice that you like to know a bit more about little old me :).

I've spend a big part of my life behind a computer (and not playing games). I've learned a lot about databases, programming and system administration especially on. the LAMP stack (Linux, Apache, MySQL & PHP).

Have a look at what I'm working on now!
© 2012 Jasny · web development Suffusion theme by Sayontan Sinha