Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cross-domain login using JSONP and cookies

How can I allow users to log into one domain and automatically be logged into my other domains without them having to submit a form on each domain?

like image 605
andrewtweber Avatar asked Jun 08 '12 21:06

andrewtweber


People also ask

Can cookies be used cross domain?

You cannot share cookies across domains. You can however allow all subdomains to have access.

Does JSONP still work?

JSONP is still useful for older browser support, but given the security implications, unless you have no choice CORS is the better choice.

What is the one reason to avoid using JSONP in a web application?

JSONP has some other limitations, too: It can only be used for GET requests, and there's no general way to prevent cross-site request forgeries*. It's bad for private data, since any site on the web could hijack a JSONP response if the URL is known.

Can you share cookies between domains?

To share a cookie between domains, you will need two domains, for example myserver.com and slave.com . One of the domains will issue the cookies and the other domain will ask the first domain what cookie should be issued to the client.


1 Answers

We all know that cookies are not accessible cross-domain as this presents a security risk. However, using some trickery, there are ways around this. Basically we are setting a cookie for the user on a central domain, checking for that cookie's existence using a script, then using a JSON-P callback to copy that cookie onto the other domains. In more detail:

Logging In

Step 1

The <form> displayed on mydomain.com (or myotherdomain.com, etc) should POST to central.com/login

Step 2

On central.com/login, the username and password are verified and a cookie is set on the central.com domain containing a unique value for that user. The user is then redirected back to mydomain.com

SELECT unique_value FROM users WHERE username = $username
set cookie on central.com containing unique_value

Step 3

Back on mydomain.com we embed a javascript call to central.com/check.

<script type="text/javascript" src="http://central.com/check"></script>

Step 4

On central.com/check we check if the unique cookie is set for the user. Then we embed a JavaScript callback (JSON-P) that informs mydomain.com that the user is logged in. No sensitive user data is included, otherwise hacker.com could embed this script and get the user's information. (Setting appropriate Access-Control headers to only allow verified domains can alleviate this risk.) Instead, we create a disposable hash based on the timestamp, so that mydomain.com can verify the authentication.

if cookie on central.com is valid
    user_data = array(
       'success' => true,
       'uid'     => $uid,
       'time'    => time_stamp,
       'hash'    => disposable_salted_hash( $uid, time_stamp )
    )
    echo 'setDomainCookie(' . json_encode(user_data) . ')'

Step 5

The callback function is then executed, setting the cookie on mydomain.com. Finally, we can either refresh the page or just alert the user using JavaScript that they are logged in (preferably both).

function setDomainCookie( user_data ) {
    if( user_data.success ) {
        $.post('/setcookie', user_data, function() {
            location.reload(true);
        }
    }
}

mydomain.com/setcookie is similar to Step 2. Of course this assumes both sites have access to the same database (and code)

if hash = disposable_salted_hash( $uid, time_stamp )
    SELECT unique_value FROM users WHERE uid = $uid
    set cookie on mydomain.com containing unique_value

Step 6

The next time the user refreshes the page, we can bypass the JSON-P callback

if cookie on mydomain.com is valid
    loggedin = true
else
    delete cookie on mydomain.com
    proceed to Step 3

Logging Out

Step 7

The link on mydomain.com should go to central.com/logout

Step 8

On central.com/logout, not only is the cookie deleted, but the unique value for that user is reset. The user is redirected back to mydomain.com

delete cookie on central.com
UPDATE users SET unique_value = new_random_value() WHERE username = $username

Step 9

Now that the unique value is reset, Step 6 from above fails, the cookie is also deleted from mydomain.com, and the user is effectively logged out.

Notes

  1. It is critical that central.com/check from Step 4 has the correct headers set so that it is not cached.

  2. Steps 3-5 when the user is logging in may cause a slight delay. It's wise to both refresh and show some kind of JavaScript alert that they are logged in. It's also important for the script from Step 3 to be as close to the top of the page as possible.

  3. In Step 5, you can optionally store a unique cookie value on each domain.

  4. The separate central.com domain is not really necessary; you can just use one of the other domains as the central domain if you wish. The logic for that domain would obviously be different.

  5. For this to work on Internet Explorer you will need a P3P policy attached to your cookies.

  6. As IvanGusev points out in the comments, one flaw of this approach is that if the user logs out of device A, it will also log them out of every other device.

  7. Hope this is helpful to people. I'd be very interested to receive feedback, especially if there are any security flaws from this method. I think the worst a hacker could do is replicate Steps 3-5 and log you in to mydomain.com without you knowing, but that would be harmless.

like image 79
andrewtweber Avatar answered Oct 29 '22 15:10

andrewtweber