Blog About Contact

Implementing Facebook OAuth 2.0 Authentication in Java

Published Wed, 30 Jun 2010 • 95 comments

I recently switched onmydoorstep.com.au's Facebook login feature from the old "Facebook Connect" API implemented with facebook-java-api over to the new Facebook Graph API / OAuth 2.0 authentication.

This was far easier to implement than the original authentication, particular under Apache Wicket, but it should be easier regardless of your Java framework of choice.

Here's how I did it.

First I developed a basic "magic" class for the Facebook API -

public class Facebook {
    // get these from your FB Dev App
    private static final String api_key = "MYAPIKEY";     
    private static final String secret = "MYSECRETKEY";
    private static final String client_id = "MYCLIENTID";  

    // set this to your servlet URL for the authentication servlet/filter
    private static final String redirect_uri = "http://www.onmydoorstep.com.au/fbauth"; 
    /// set this to the list of extended permissions you want
    private static final String[] perms = new String[] {"publish_stream", "email"};

    public static String getAPIKey() {
        return api_key;
    }

    public static String getSecret() {
        return secret;
    }

    public static String getLoginRedirectURL() {
        return "https://graph.facebook.com/oauth/authorize?client_id=" + 
            client_id + "&display=page&redirect_uri=" + 
            redirect_uri+"&scope="+StringUtil.delimitObjectsToString(",", perms);
    }

    public static String getAuthURL(String authCode) {
        return "https://graph.facebook.com/oauth/access_token?client_id=" + 
            client_id+"&redirect_uri=" + 
            redirect_uri+"&client_secret="+secret+"&code="+authCode;
    }
}

You'll need the visural-common library for some of the code above.

I want the "email" and "publish_stream" extended permissions, so that I can get the user's email address and post updates back to their stream in Facebook. You can customise this list with the permissions that you need.

The process of authentication is simple.

  1. You create a link on your web UI (generally labelled "Login With Facebook" or something like that) to the Facebook.getLoginRedirectURL() URL.
  2. Facebook will authorise the user with the permissions you requested, and redirect the user to your "redirect_uri" as specified above.
  3. In a servlet or filter at your "redirect_uri" you need to
    • Retrieve the request parameter "code"
    • Make another request to the URL - Facebook.getAuthURL(request.getParameter("code"))
    • Parse the response for the "access_token" and "expires", assuming that it was a valid response that contained them.
  4. You use your access token to retrieve data about the user and/or make other calls to the Facebook Graph API

Due to the way Apache Wicket works, I implemented a Servlet Filter for the "redirect_uri" (/fbauth) -

public class FBOAuth implements Filter {

    public void init(FilterConfig fc) throws ServletException {
    }

    public void doFilter(ServletRequest sr, ServletResponse sr1, FilterChain fc) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest)sr;
        HttpServletResponse res = (HttpServletResponse)sr1;
        String code = sr.getParameter("code");
        if (StringUtil.isNotBlankStr(code)) {
            String authURL = Facebook.getAuthURL(code);
            URL url = new URL(authURL);
            try {
                String result = readURL(url);
                String accessToken = null;
                Integer expires = null;
                String[] pairs = result.split("&");
                for (String pair : pairs) {
                    String[] kv = pair.split("=");
                    if (kv.length != 2) {
                        throw new RuntimeException("Unexpected auth response");
                    } else {
                        if (kv[0].equals("access_token")) {
                            accessToken = kv[1];
                        }
                        if (kv[0].equals("expires")) {
                            expires = Integer.valueOf(kv[1]);
                        }
                    }
                }
                if (accessToken != null && expires != null) {
                    UserService us = UserService.get();
                    us.authFacebookLogin(accessToken, expires);
                    res.sendRedirect("http://www.onmydoorstep.com.au/");
                } else {
                    throw new RuntimeException("Access token and expires not found");
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }       
    }

    private String readURL(URL url) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        InputStream is = url.openStream();
        int r;
        while ((r = is.read()) != -1) {
            baos.write(r);
        }
        return new String(baos.toByteArray());
    }

    public void destroy() {
    }
}

This is a cut-down version of my actual code as I do a bunch of other things on onmydoorstep.com.au which is more related to internal house-keeping and UX.

Your "UserService" might look something like -

class UserService {

    // ....

    public void authFacebookLogin(String accessToken, int expires) {
        try {
            JSONObject resp = new JSONObject(
                IOUtil.urlToString(new URL("https://graph.facebook.com/me?access_token=" + accessToken)));
            String id = resp.getString("id");
            String firstName = resp.getString("first_name");
            String lastName = resp.getString("last_name");
            String email = resp.getString("email");

            // ...
            // create and authorise the user in your current system w/ data above
            // ...

        } catch (Throwable ex) {
            throw new RuntimeException("failed login", ex);
        }
    }
}

So, just to recap, the sequence of authentication is as follows -

  1. User clicks a link on your site to Facebook.getLoginRedirectURL()
  2. Facebook asks them for their username/password to log in to your application
  3. Assuming they authenticate with Facebook, Facebook then redirects the user to your "redirect_uri" with a parameter "code" passed along.
  4. You use the "code" parameter to query the Facebook authentication service - Facebook.getAuthURL(request.getParameter("code"))
  5. Assuming it was a valid authentication code, Facebook will pass you back an "access_token" that you can use to access the Facebook Graph API for the given user.

This could certainly be formalised into a reusable chunk of code. I may get around to adding it to visural-common, but right now there's a lot of onmydoorstep-specific stuff in the real code.

Anyhow, hope it helps someone out!


About the Author

Richard Nichols is an Australian software engineer with a passion for making things.

Follow him on twitter or subscribe by RSS or email.

You might also enjoy reading -


Discuss / Comment

There are 95 comments.

Add a comment

  • {{e.error}}

Thanks for your comment!/

Required.
Valid email address required.
Required.
Posting message, please wait...