开发者

Validate and get data from Facebook cookie using OAUTH 2.0

I have a web page made in GWT开发者_如何学Go. There I use all the login facebook stuff with a manipulated gwtfb library, all works fine. After migrating to oauth 2.0 now the cookie sent to the server has changed to a encrypted one.

I want to get a java example code that implements in the server the same than the old one:

  • I need to validate the call like I did before using the cookie md5 trick to know if the call has been made by my client page.
  • Get data from that cookie: I need the facebook user.

If possible not calling FB, just using the cookie data.

Thanks in advance.


Well, although I have a few good answers I answer myself with what I have written in my blog: http://pablocastilla.wordpress.com/2011/09/25/how-to-implement-oauth-f/

Now the cookie has changed a lot: it is encrypted, doesn't have the accesstoken and its content format has changed a lot. Here you have a few links talking about it:

http://developers.facebook.com/docs/authentication/signed_request/

http://developers.facebook.com/docs/authentication/

http://blog.sociablelabs.com/2011/09/19/server-side-changes-facebook-oauth-2-0-upgrade/

So to validate the cookie, get the user from it and get the access token you could use this code:

public class FaceBookSecurity {

// return the fb user in the cookie.
public static String getFBUserFromCookie(HttpServletRequest request)
        throws Exception {
    Cookie fbCookie = getFBCookie(request);

    if (fbCookie == null)
        return null;

    // gets cookie value
    String fbCookieValue = fbCookie.getValue();

    // splits it.
    String[] stringArgs = fbCookieValue.split("\\.");
    String encodedPayload = stringArgs[1];

    String payload = base64UrlDecode(encodedPayload);

    // gets the js object from the cookie
    JsonObject data = new JsonObject(payload);

    return data.getString("user_id");

}

public static boolean ValidateFBCookie(HttpServletRequest request)
        throws Exception {

    Cookie fbCookie = getFBCookie(request);

    if (fbCookie == null)
        throw new NotLoggedInFacebookException();

    // gets cookie information
    String fbCookieValue = fbCookie.getValue();

    String[] stringArgs = fbCookieValue.split("\\.");
    String encodedSignature = stringArgs[0];
    String encodedPayload = stringArgs[1];

    //decode
    String sig = base64UrlDecode(encodedSignature);
    String payload = base64UrlDecode(encodedPayload);

    // gets the js object from the cookie
    JsonObject data = new JsonObject(payload);

    if (!data.getString("algorithm").Equals("HMAC-SHA256")) {
        return false;
    }

    SecretKey key = new SecretKeySpec(
            ApplicationServerConstants.FacebookSecretKey.getBytes(),
            "hmacSHA256");

    Mac hmacSha256 = Mac.getInstance("hmacSHA256");
    hmacSha256.init(key);
    // decode the info.
    byte[] mac = hmacSha256.doFinal(encodedPayload.getBytes());

    String expectedSig = new String(mac);

    // compare if the spected sig is the same than in the cookie.
    return expectedSig.equals(sig);

}

public static String getFBAccessToken(HttpServletRequest request)
        throws Exception {
    Cookie fbCookie = getFBCookie(request);

    String fbCookieValue = fbCookie.getValue();

    String[] stringArgs = fbCookieValue.split("\\.");
    String encodedPayload = stringArgs[1];

    String payload = base64UrlDecode(encodedPayload);

    // gets the js object from the cookie
    JsonObject data = new JsonObject(payload);

    String authUrl = getAuthURL(data.getString("code"));
    URL url = new URL(authUrl);
    URI uri = new URI(url.getProtocol(), url.getHost(), url.getPath(),
            url.getQuery(), null);
    String result = readURL(uri.toURL());

    String[] resultSplited = result.split("&");

    return resultSplited[0].split("=")[1];

}

// creates the url for calling to oauth.
public static String getAuthURL(String authCode) {
    String url = "https://graph.facebook.com/oauth/access_token?client_id="
            + ApplicationConstants.FacebookApiKey
            + "&redirect_uri=&client_secret="
            + ApplicationServerConstants.FacebookSecretKey + "&code="
            + authCode;

    return url;
}

// reads the url.
private static String readURL(URL url) throws IOException {

    InputStream is = url.openStream();

    InputStreamReader inStreamReader = new InputStreamReader(is);
    BufferedReader reader = new BufferedReader(inStreamReader);

    String s = "";

    int r;
    while ((r = is.read()) != -1) {
        s = reader.readLine();
    }

    reader.close();
    return s;
}

private static String base64UrlDecode(String input) {
    String result = null;
    Base64 decoder = new Base64(true);
    byte[] decodedBytes = decoder.decode(input);
    result = new String(decodedBytes);
    return result;
}

    private static Cookie getFBCookie(HttpServletRequest request) 
    {
        Cookie[] cookies = request.getCookies();

        if (cookies == null)
            return null;

        Cookie fbCookie = null;

        for (Cookie c : cookies) {
            if (c.getName().equals(
                "fbsr_" + ApplicationServerConstants.FacebookApiKey)) {
                fbCookie = c;
            }
        }
        return fbCookie;
    }
}


I just added this to a new release (2.1.1) of BatchFB: http://code.google.com/p/batchfb/

To get the user id:

FacebookCookie data = FacebookCookie.decode(cookie, YOURAPPSECRET);
System.out.println("Facebook user id is " + data.getFbId());

You can see the code here, which uses Jackson to parse the JSON and javax.crypto.Mac to verify the signature:

http://code.google.com/p/batchfb/source/browse/trunk/src/com/googlecode/batchfb/FacebookCookie.java

Unfortunately getting the access token is considerably more complicated. The data that comes in the fbsr_ cookie includes a "code" which you can then use to make a graph api fetch to get a real access token... which you would have to store in a cookie or session somewhere. This is incredibly lame.

An better solution is to set your own access token cookie from javascript. Every time you call FB.getLoginStatus(), have it set (or remove) your own cookie. You don't need to worry about signing the access token since it's unguessable.


I think the cookie data is in an arbitary format - because you shouldn't be interpreting it yourself? Surely the SDK should be validating the cookie for you?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜