How to create a Shared Login Service across Multiple Domains?

Corey Ballou picture Corey Ballou · Nov 29, 2010 · Viewed 29.3k times · Source

I'm interested in how to implement a shared cross-domain login system as well as best practices and security precautions to take. If you are familiar with 37Signals, you are probably accustomed to their usage of having a shared universal authentication mechanism whereby you do not have to subsequently login if you use the top level navigation to a different product. I would like to implement something in a similar fashion.

The closest thing I've found online is the Wikipedia entry on a Central Authentication Service and the response to Cross Domain Login - How to login a user automatically when transfered from one domain to another, which may be slightly different in this case.

I have inspected their session cookies to get a grasp on what they're doing in the process. Initially, each product link has a "goto" uristub, i.e.:

https://MY_COMPANY.campfirenow.com/id/users/[int_identifier]/goto

Using FireCookie and the NET tab in Firebug, I'm able to see the cookies that are set and the redirects that occur in the process. The goto url fires a 302 redirect to:

https://MY_COMPANY.basecamphq.com/login/authenticate?sig=[BASE64_ENCODED_AND_ENCRYPTED_DATA]

The session identifier is recreated, most likely for CSRF purposes. Some of the data in the cookies as well as the GET parameter sig were partially decrypted using base64_decode to the following:

// sig GET param
array(2) {
  [0]=>
���ף�:@marshal_with_utc_coercionT7�z��<k��kW"
  [1]=>
  string(18) "���k�<kn8�f���to��"
}

// _basecamp_session cookie session param
string(247) {
:_csrf_token"1Sj5D6jCwJKIxkZ6oroy7o/mYUqr4R5Ca34cOPNigqkw=:session_id"%060c0804a5d06dafd1c5b3349815d863"
flashIC:'ActionController::Flash::FlashHash{:
@used{: auth{"
              MY_COMPANY{:
                       user_idi�3
:identity_idi�W����������s�]��:�N[��:

߾�����"

The encoding is breaking the code block. Thanks for your help!

Answer

epeleg picture epeleg · Jan 1, 2011

I think the key in your Q is where you write "whereby you do not have to subsequently login if you use the top level navigation to a different product".

I will explain: You want to be able to move from site1.com to site2.com and let site2.com know that you are some user that logged in via site1.com.

because cookies are not shareable between different domains (other than subdomains but I guess you are not talking about sub-domains) you have to pass some information on the link to site2.com that will let it talk with its back-end and know what user you are.

lets start with a naive solution and then work our way to make resolve some of the problems that it poses: suppose you have a users table on some back-end DB and your user has some ID. now assume that the user authenticated on site1.com and he is user 123 from your DB. The most naive solution would be to call site2.com/url/whatever?myRealID=123 and have site2 check for this ID and "belive" the user that this is indeed him.

Problem: Anyone (even a valid user of your site) that will see your link can create a link with myRealID=123 or try other values for it. and site2.com will accept him as that user.

Solution: lets not use guessable ID's suppose you add to your users table a unique GUID and if user 123 has a guid of 8dc70780-15e5-11e0-ac64-0800200c9a66 then call site2.com/url/whatever?myGuid=8dc70780-15e5-11e0-ac64-0800200c9a66.

New Problem: well even though It would be very unlikely that someone will guess your user's GUID his GUID can still be hijacked by some middle man that sees this link and it someone will get the guid he can use it for ever.

Solution: Use a private key saved on your server to sign a string that contains the following data items, current time-stamp, destination site (i.e "site2.com") the said GUID, this signature can be translated into saying "This is a proof that this link was created by the site at the said time for the user that has this GUID to authenticated by this domain" and send it to site2.com along with the time-stamp and GUID. Now when site2.com gets the link he can make sure that it is signed properly and if some middleman or originally user would try to change it in any way (either my modifying the time or GUID) then the signature would not match and site2.com will refuse authenticating the user.

One last problem: if the link is intercepted by a middleman he can still use if from some other machine.

Solution: add a nonce to the passed parameters. The nonce is just a random number that your authentication system should make sure that it does not allow you to authenticate with this number more then once (hence then name N(umber)-ONCE). Note that this means that each link you create on site1.com that leads to site2.com should have a distinct nonce which needs to be saved on the back-end for later validation.

because you don't want to keep adding records to this nonce table forever it is custom to decide that such a link is only valid for some given time after its creation. and so you can create nonce records that are older than this time limit.

I hope that this is the outline you where looking for. It is possible that I missed something but those are the basic guidelines, use a signature to prove the authenticity of the data in the link and use a nonce to prevent middleman attacks. I would also recommend using HTTPS for those links if possible.

Eyal