15 November 2011

In this example I show how to validate a user’s facebook session in the server side using the fbsr cookie created by the FB javascript API.

I use the following, basic html to initialize the login button.

<div id="fb-root"></div>
<script>
window.fbAsyncInit = function() {
FB.init({appId : 'APP_ID', status : true, cookie : true, oauth : true, xfbml : true});
};
// Load the SDK Asynchronously
(function(d){
var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {return;}
js = d.createElement('script'); js.id = id; js.async = true;
js.src = "//connect.facebook.net/en_US/all.js";
d.getElementsByTagName('head')[0].appendChild(js);
}(document));
</script>
<div class="fb-login-button">Login with Facebook</div>
view raw index.html hosted with ❤ by GitHub

The facebook login will in this case create a cookie named fbsr_[your facebook application id] which is in the form of signed request consisting of two parts:

[mac sha256 signature].[base64 url encoded json]

Now I can validate the cookie on server side by signing the second part of the cookie with my facebook secret and making sure that it matches to the first part. To get the json data I just have to json decode the second part of the cookie.

Here’s code that I use to do it in Java:

package models;
import com.google.gson.Gson;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.regex.Pattern;
/**
* @author huljas
*/
public class FacebookCookie {
private String value;
private boolean valid = false;
private Data data;
public FacebookCookie(String value) {
this.value = value;
}
/**
* Call before anything else!
*/
public boolean validate(String secret) {
String[] splits = value.split(Pattern.quote("."));
String signature = splits[0];
String encoded = splits[1];
String jsonString = decodeBase64(encoded);
String signature2 = digest(encoded, secret);
if (!signature2.equals(signature)) {
return false;
}
Gson gson = new Gson();
this.data = gson.fromJson(jsonString, Data.class);
return true;
}
public static String digest(String encoded, String secret) {
try {
Mac hmacSha256 = Mac.getInstance("HmacSHA256");
SecretKeySpec key = new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256");
hmacSha256.init(key);
byte[] mac = hmacSha256.doFinal(encoded.getBytes("UTF-8"));
byte[] base64 = Base64.encodeBase64(mac);
String s = new String(base64, "UTF-8");
return s.replace("/", "_").replace("+", "-").replace("=", "");
} catch (NoSuchAlgorithmException e) {
throw new Error("Never thrown", e);
} catch (UnsupportedEncodingException e) {
throw new Error("Never thrown", e);
} catch (InvalidKeyException e) {
throw new RuntimeException("Invalid key", e);
}
}
public static String decodeBase64(String encoded) {
return new String(Base64.decodeBase64(encoded));
}
public Data data() {
return data;
}
public class Data {
public String user_id;
public String code;
}
}

So now we have json object in the format of:

{ 
  "algorithm" : "HMAC-SHA256",
  "code" : "2.AQDC1d-ZE90999-6.3600.1321387200.1-676786217|X1VAtqRDS_3NR6lduKISj50Uozw", 
  "issued_at" : 1321381928, 
  "user_id" : "12345678"
}

In order to get the access token, we need to request from the graph api using the following request:

https://graph.facebook.com/oauth/access_token?
client_id=[facebook application id]
&redirect_uri=
&client_secret=[application secret]
&code=[code from the json]

Note that you need to include an empty redirect_uri parameter in the request for it to work. From the response you get the actual access token, which you can use to get more information about the user.

You can find a simple example application here.



blog comments powered by Disqus