I want to let users log in on my website using their Facebook ID without reloading the page. This is why I use Facebook Javascript SDK. This scheme describes the authorization flow with this SDK:
At the end of the process I know that the user is logged in and I know their Facebook ID. I can then register them in my database via this ID, and let them use it to log in afterwards.
However, this seems terribly insecure. In order to let my server-side script know the user's ID, I have to send it via AJAX. However, I have no way of knowing whether it's the owner of the ID who's trying to log in. Anyone can send the POST request with an ID (especially of one gets hold of another user's ID).
My current idea is to let the user log in via JS SDK as usual, send the ID and Access Token via AJAX to the server and then use cURL in the PHP script to ensure the user is actually logged in.
Is this the way to go, or am I overlooking better alternatives?
You don't need to push the user's ID via ajax. You should, on the server side, use the fbsr_{app_id} cookie which holds the signed_request. Parse this signed_request using your 'secret' app_secret issued by FB to get the 'user_id'. NOTE: a successful parse also shows that the cookie data provided by FB is not tampered with.
Once you parse the signed_request, you should also get the 'issued_at' time. Check that this time is within the last 10 mins. By doing this, you know that the login request hit your server as the user (with user_id) used client-side SDK. (Refer: http://developers.facebook.com/roadmap/completed-changes/)
You should immediately exchange this code for an access_token. If this fails (FB will give you an error message of type OAuthException), it means that there was an unnatural delay between user signing in to facebook and you getting the login request.
With step #2, you can thwart attempts of an attack using old fbsr_ cookie. If the user (from user_id) already has an account with you, then you may wish to stop here and login the user. However, there may be scenarios where your app_secret may be compromised. To take care of this case, you should follow step #3, as the exchange of code for access_token can happen only once and within 10 mins of it's issue. If the user doesn't have an account with your site, then you anyway need step #3 to use the access_token for retrieving other necessary user data, like name, email, etc from FB.
Therefore, someone else stealing the victim's cookie and trying to attack is possible only within this 10 min security gap. If you are unhappy with this security hole, you should migrate to server-side authentication. The decision depends on the sensitivity of the user information you store. And you don't compromise anything moving to server-side auth, you can simultaneously continue to use client-side methods for other things.
Once you've logged in a user via the JS SDK, a special cookie will be setup containing credential info (encoded with your secret key if I'm correct). This info can then be used via the PHP SDK getUser()
method.
As long as your API (your ajax endpoint) is on the same domain as your app, you should receive this cookie whenever the user request your server.
Of course, you need to make sure the Javascript SDK is setup correctly and that you used the cookie: true
config option, and that you give a valid channel
file. If these requirement ain't met, you may have some trouble with cross-domain communication and 3rd party cookie in IE and Safari.
You can also check this related question: A proper approach to FB auth