Skip to main content

How to Implement Facebook App Authorization in Java


In this article we will go through the Facebook Application Authorization process that uses OAuth 2.0 signature scheme and implement it in Java. We will be creating a very simple iframe app that displays user's name for authorized users and forces unauthorized users to go through the authorization process.

Requirements

Assuming the iframe facebook app is already registered, we would need to have the following ready for the implementation:
  • Facebook App details: App IDApp SecretCanvas Page, and Canvas URL. This information could be found on your app profile page.
  • Base 64 decoder with url-safe mode support. We will use Apache Commons Codeclibrary.
  • JSON decoder. We will use Json-lib library.
  • HMAC SHA256 hashing implementation. Comes with JDK.
  • Facebook Graph API client. We will use RestFB library.

Authorization Workflow

Authoirzation process for facebook apps is described here. Our workflow would be the following:
  • With each request facebook sends signed_request parameter through HTTP POST to our Canvas URL that contains information about current user. If this parameter is not present, do a server side redirect to Canvas Page.
  • If user authorized the app, signed_request parameter contains user id and access token encoded according to the OAuth 2.0 signature scheme.
  • If user hasn't authorized the app, build OAuth Authorization Dialog url and do a client side redirect.

Implementation

Here is a facebook app authorization process implemented in Java using Spring Web MVC framework and Apache Velocity templates:
import java.net.URLEncoder;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.json.JSONObject;
import net.sf.json.JSONSerializer;
import org.apache.commons.codec.binary.Base64;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
import org.springframework.web.servlet.view.RedirectView;
import com.restfb.DefaultFacebookClient;
import com.restfb.FacebookClient;
import com.restfb.types.User;
public class AppController extends AbstractController {

    protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {

        ModelAndView mav = new ModelAndView("appView");

        //Facebook App info
        String fbSecretKey = "abcdefghijklmno";
        String fbAppId = "1234567890";
        String fbCanvasPage = "http://apps.facebook.com/example-app/";
        String fbCanvasUrl = "http://example.com/";

        //parse signed_request
        if(request.getParameter("signed_request") != null) {

            //it is important to enable url-safe mode for Base64 encoder 
            Base64 base64 = new Base64(true);

            //split request into signature and data
            String[] signedRequest = request.getParameter("signed_request").split("\\.", 2);

            //parse signature
            String sig = new String(base64.decode(signedRequest[0].getBytes("UTF-8")));

            //parse data and convert to json object
            JSONObject data = (JSONObject)JSONSerializer.toJSON(new String(base64.decode(signedRequest[1].getBytes("UTF-8"))));

            //check signature algorithm
            if(!data.getString("algorithm").equals("HMAC-SHA256")) {
                //unknown algorithm is used
                return null;
            }

            //check if data is signed correctly
            if(!hmacSHA256(signedRequest[1], fbSecretKey).equals(sig)) {
                //signature is not correct, possibly the data was tampered with
                return null;
            }

            //check if user authorized the app
            if(!data.has("user_id") || !data.has("oauth_token")) {
                //this is guest, create authorization url that will be passed to javascript
                //note that redirect_uri (page the user will be forwarded to after authorization) is set to fbCanvasUrl
                mav.addObject("redirectUrl", "https://www.facebook.com/dialog/oauth?client_id=" + fbAppId + 
                        "&redirect_uri=" + URLEncoder.encode(fbCanvasUrl, "UTF-8") + 
                        "&scope=publish_stream,offline_access,email");
            } else {
                //this is authorized user, get their info from Graph API using received access token
                String accessToken = data.getString("oauth_token");
                FacebookClient facebookClient = new DefaultFacebookClient(accessToken);
                User user = facebookClient.fetchObject("me", User.class);
                mav.addObject("user", user);
            }

        } else {
            //this page was opened not inside facebook iframe,
            //possibly as a post-authorization redirect.
            //do server side forward to facebook app
            return new ModelAndView(new RedirectView(fbCanvasPage, true));
        }

        return mav;

    }

    //HmacSHA256 implementation 
    private String hmacSHA256(String data, String key) throws Exception {
        SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(secretKey);
        byte[] hmacData = mac.doFinal(data.getBytes("UTF-8"));
        return new String(hmacData);
    }
}
Page template:
<html>
    <head>
        #if($redirectUrl)
            <script>
                top.location.href = "${redirectUrl}";   
            </script>
        #end
    </head>
    <body>      
        Welcome, ${user.name}!
    </body>
</html>
Please note that if our page is viewed by an unauthorized user, we cannot simply do a server side redirect to the authorization dialog as our app is running inside an iframe. We would need to redirect the topmost window instead, which could be done with javascript.
Another important moment is that users will be redirected to Canvas Url page after authorization (as set by redirect_uri parameter in OAuth Dialog), so they will not be on facebook anymore. We can detect this redirect by the absence of signed_request request parameter and the presence of code (if a user clicked "Allow" button) or error (if a user clicked "Don't Allow" button) parameters, and forward a user back to our app inside facebook (Canvas Page). In this implementation we simply look at the absence ofsigned_request parameter and don't make a difference between a post-authorization redirect and a page being viewed not inside a facebook iframe.

Comments

Popular posts from this blog

Creating Custom Events in C#

First of all, to read and understand this article you should be familiar with these concepts of C# language: Inheritance Events Delegates Just to remind them very quickly: Inheritance is a feature of OOP. You can create a class and then you can create another class which has the same properties and methods of the class it is inheriting from. This could be useful for example when you need to create a class for objects like cars. You create a basic Car class and then you create some classes that inherits from Car, and in their body you add methods and/or properties. The C# syntax for inheriting a class is this: class BaseClass { /*body of the BaseClass here */ } class DerivedClass : BaseClass { /*body of the DerivedClass here */ } Events and delegates are two concepts that very often work together. When something happens on a windows form (like a click, dblclick, changin some text, selecting an item in a combobox, moving mouse, pressing a key and so on) an event is raise...

How to use Application Bar in Windows Phone 7 Application ?

      If you are working with Windows Phone 7, the first thing that you should have noticed is the very own Application bar. Application Bar is present in most of the applications that you use in your Windows Phone 7. This is basically a standard Toolbar with a menu associated with it which allows you to enumerate the commonly used commands in a standard location. While creating your application, Microsoft strongly recommends you to add an application bar, to ensure the user have common behaviour for every application. You can think Application bar similar to TaskBar of windows. Components of Application Bar An application Bar is made up with two components: 1. ApplicationBar Buttons 2. ApplicationBar Menu The applicationbar buttons are always visible for an application which is used to list only the items that needed to be frequently used while dealing with the application. Lets say you create a Text processing application, you can list the File->Ope...

WP7 Sample–Create a JPG screenshot of Bing maps control

WP7 Sample–Create a JPG screenshot of Bing maps control Today I saw a colleague ask an interesting question, how do you create a screenshot of a map that can be used in a list as a thumbnail? so I did some digging and came up with this sample that can be downloaded from the link below. http://cid-c6b45be43711d8b8.skydrive.live.com/self.aspx/Code%20Samples/wp7Sample.ScreenshotOfMap.zip In summary for those who don’t want to download the sample app, most of the work is done inside of the “Take picture” button, here is the source: // Store the size of the map control int Width = (int)mapTest.RenderSize.Width; int Height = (int)mapTest.RenderSize.Height; // Write the map control to a WriteableBitmap WriteableBitmap screenshot = new WriteableBitmap(mapTest, new TranslateTransform()); using (MemoryStream ms = new MemoryStream()) { // Save it to a memory stream screenshot.SaveJpeg(ms, Width, Height, 0, 100); // Take saved memory stream and put it back into an BitmapImage BitmapImage i...