Java example of how to log in to Google App Engine with a Facebook account using OAuth
Asked Answered
K

5

39

I searched a lot, read many blogs, articles, tutorials, but until now did not get a working example of using a Facebook account to log in to my application.

I know that I have to use OAuth, get tokens, authorizations, etc...

Can anyone share an example?

Knockknee answered 5/3, 2012 at 12:54 Comment(0)
I
52

Here is how I do it on App Engine:

Step 1) Register an "app" on Facebook (cf. https://developers.facebook.com/ ). You give Facebook a name for the app and a url. The url you register is the url to the page (jsp or servlet) that you want to handle the login. From the registration you get two strings, an "app ID" and an "app secret" (the latter being your password, do not give this out or write it in html).

For this example, let's say the url I register is "http://myappengineappid.appspot.com/signin_fb.do".

2) From a webpage, say with a button, you redirect the user to the following url on Facebook, substituting your app id for "myfacebookappid" in the below example. You also have to choose which permissions (or "scopes") you want the ask the user (cf. https://developers.facebook.com/docs/reference/api/permissions/ ). In the example I ask for access to the user's email only.

(A useful thing to know is that you can also pass along an optional string that will be returned unchanged in the "state" parameter. For instance, I pass the user's datastore key, so I can retrieve the user when Facebook passes the key back to me. I do not do this in the example.)

Here is a jsp snippet:

<%@page import="java.net.URLEncoder" %>
<%
    String fbURL = "http://www.facebook.com/dialog/oauth?client_id=myfacebookappid&redirect_uri=" + URLEncoder.encode("http://myappengineappid.appspot.com/signin_fb.do") + "&scope=email";
%>

<a href="<%= fbURL %>"><img src="/img/facebook.png" border="0" /></a>

3) Your user will be forwarded to Facebook, and asked to approve the permissions you ask for. Then, the user will be redirected back to the url you have registered. In this example, this is "http://myappengineappid.appspot.com/signin_fb.do" which in my web.xml maps to the following servlet:

import org.json.JSONObject;
import org.json.JSONException;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SignInFB extends HttpServlet {

    public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {            
        String code = req.getParameter("code");
        if (code == null || code.equals("")) {
            // an error occurred, handle this
        }

        String token = null;
        try {
            String g = "https://graph.facebook.com/oauth/access_token?client_id=myfacebookappid&redirect_uri=" + URLEncoder.encode("http://myappengineappid.appspot.com/signin_fb.do", "UTF-8") + "&client_secret=myfacebookappsecret&code=" + code;
            URL u = new URL(g);
            URLConnection c = u.openConnection();
            BufferedReader in = new BufferedReader(new InputStreamReader(c.getInputStream()));
            String inputLine;
            StringBuffer b = new StringBuffer();
            while ((inputLine = in.readLine()) != null)
                b.append(inputLine + "\n");            
            in.close();
            token = b.toString();
            if (token.startsWith("{"))
                throw new Exception("error on requesting token: " + token + " with code: " + code);
        } catch (Exception e) {
                // an error occurred, handle this
        }

        String graph = null;
        try {
            String g = "https://graph.facebook.com/me?" + token;
            URL u = new URL(g);
            URLConnection c = u.openConnection();
            BufferedReader in = new BufferedReader(new InputStreamReader(c.getInputStream()));
            String inputLine;
            StringBuffer b = new StringBuffer();
            while ((inputLine = in.readLine()) != null)
                b.append(inputLine + "\n");            
            in.close();
            graph = b.toString();
        } catch (Exception e) {
                // an error occurred, handle this
        }

        String facebookId;
        String firstName;
        String middleNames;
        String lastName;
        String email;
        Gender gender;
        try {
            JSONObject json = new JSONObject(graph);
            facebookId = json.getString("id");
            firstName = json.getString("first_name");
            if (json.has("middle_name"))
               middleNames = json.getString("middle_name");
            else
                middleNames = null;
            if (middleNames != null && middleNames.equals(""))
                middleNames = null;
            lastName = json.getString("last_name");
            email = json.getString("email");
            if (json.has("gender")) {
                String g = json.getString("gender");
                if (g.equalsIgnoreCase("female"))
                    gender = Gender.FEMALE;
                else if (g.equalsIgnoreCase("male"))
                    gender = Gender.MALE;
                else
                    gender = Gender.UNKNOWN;
            } else {
                gender = Gender.UNKNOWN;
            }
        } catch (JSONException e) {
            // an error occurred, handle this
        }

        ...

I have removed error handling code, as you may want to handle it differently than I do. (Also, "Gender" is of course a class that I have defined.) At this point, you can use the data for whatever you want, like registering a new user or look for an existing user to log in. Note that the "myfacebookappsecret" string should of course be your app secret from Facebook.

You will need the "org.json" package to use this code, which you can find at: http://json.org/java/ (just take the .java files and add them to your code in an org/json folder structure).

I hope this helps. If anything is unclear, please do comment, and I will update the answer.

Ex animo, - Alexander.

****UPDATE****

I want to add a few tidbits of information, my apologies if some of this seems a bit excessive.

To be able to log in a user by his/her Facebook account, you need to know which user in the datastore we are talking about. If it's a new user, easy, create a new user object (with a field called "facebookId", or whatever you want to call it, whose value you get from Facebook), persist it in the datastore and log the user in.

If the user exist, you need to have the field with the facebookId. When the user is redirected from Facebook, you can grab the facebookId, and look in the datastore to find the user you want to log in.

If you already have users, you will need to let them log in the way you usually do, so you know who they are, then send them to Facebook, get the facebookId back and update their user object. This way, they can log in using Facebook the next time.

Another small note: The user will be presented with a screen on Facebook asking to allow your app access to whatever scopes you ask for, there is no way around this (the less scopes you ask for, the less intrusive it seems, though). However, this only happens the first time a user is redirected (unless you ask for more scopes later, then it'll ask again).

Isolating answered 6/3, 2012 at 9:6 Comment(23)
I don't see you validating the signed_request property to verify if the user/request is actually valid, but apart from that, those are indeed the steps to be taken...Nostoc
Alexander, with this code, if the user was logged into facebook, and open my GAE, it will auto-check the facebook account? or the user will always need to click somewhere to "Authenticate"?Knockknee
You can do two things with this code. (1) Allow new users to click "sign up with facebook" and (2) allow existing users to log in just by clicking a "log in with your facebook account". They can both click the same link, and when the servlet above is run, you can check the database and see if you have a registered user with this facebookId. If so, you can log that user in. If not, you can create a new user, and store the facebookId. Yes, if the user is logged into facebook and has approved any permissions, this code will log the person in, without having to click anywhere.Isolating
A small additional note: You can of course also allow existing users of your app to log in with Facebook (i.e. updating their user object with their facebookId). This can for instance be done by passing the user's datastore key in the state parameter I briefly mention, then letting the servlet above grab it and update the user with the facebookId (or you could use the session to detect the user instead).Isolating
Alexander, thx for all your ideas, tonight i will try to make it work on my APPKnockknee
Alex thx alot, the code appers to work GREAT!, but can u gimme an idea how i can call the Authentication process without a "click"event?Knockknee
Alex, how i redirect the browser to a new URL. Example, the Main page is 123.html, after logged it must go to 123.html/logged?Knockknee
Do you want to redirect the user without him/her needing to click a button on your website, or without having to click accept on Facebook? If it's the latter, this is not possible, the user will have to accept it the first time (but only the first time). If it's the former, you can redirect the user automatically, by using response.sendRedirect(fbURL);Isolating
The process is as follows: 1) The user is on your website, and is forwarded to Facebook. 2) Facebook may ask the user to approve permissions. 3) Facebook sends the user back to your site, to the URL you have registered with Facebook. In your example, in step (1) the user will be on 123.html, click on something (or automatically redirected), step (2) the user is on Facebook, and in step (3) Facebook will send the user to something like myloginpage.jsp (whatever you have registered on Facebook). Does this answer your question, or am I misunderstanding you? :)Isolating
The first access the user will need to click somewhere to get the Facebook auth.i got this. "response.sendRedirect(fbURL);" will redirect to URL i want, after the Auth (this is what i asked u first, thanks). But lets imagine the next time, in another day he logs into facebook, and reopen my GAE, at this momment, how can i trigger the authentication withouth a click event?Knockknee
i will try to explain better, what i want is, when someone try to access my GAE, before anything i will check if there is a facebook account logged at the browser, if there is, i wanna capture the ID from this facebook session and i will check if i have it stored into my datastore. is this possible?Knockknee
No, you cannot capture Facebook's session. However, you may not need to, what you can do is to forward the user to Facebook automatically. If the user is not logged into Facebook, he/she will be asked to log in (and approve permissions if its the first time), and will be redirected to your site with the facebookId. If the user is logged in and has already approved rights, he will not need to click anything (and it usually only takes a second before he is back on your site). Does this work for you?Isolating
your code works, my only problem now is, how to "check" IF there is a Facebook account logged without the user "Click" at a link. IF there is a Logged account then i will auto login into my app, else the login screen is shown, and at this login screen the user can "click to login with facebook account" if he wishes –Knockknee
What you can do is this... if the user logs in to your app via Facebook, set a cookie. The next time a user goes to your site, you can look for the cookie, and auto-login the user. (Small caveat: If the user has several devices, the user will need to do it for each device though.)Isolating
hmm, its a solution, but a bit unsafe, because if anyother user sit at his computer, he or she will auto-log in my app. btw for the momment until i find how to "get the current facebookid logged without a click", i will force always "Click to login with facebook account"Knockknee
The auto-login will only work if the user is logged into Facebook, so it should not be less safe than if it was possible to check if Facebook had a session with the user :)Isolating
so, how to check if there is a Facebook session actived?Knockknee
You cannot check if Facebook has a session, but if you have a cookie set by your server, and it automatically redirects the person to log in via Facebook, it will be just as secure as it would be IF it was possible to check if a Facebook session existed, as the person will not be logged in after being redirected to Facebook if no such session exist.Isolating
I do not mean that you log the person in if you find a cookie set by your server. I mean that if you find a cookie (saying that this computer has used Facebook to log in in the past), you redirect the person to Facebook to be logged in (which in turn will redirect the user back to you).Isolating
Alex thx for all your help... but i really dont know how to do this =(Knockknee
@nanospeck use URLEncode.encode(urlS, enc) docs.oracle.com/javase/7/docs/api/java/net/…Azpurua
Gender gender; this is giving error ,can u please tell from where we can get the Gender classReject
@KunalSaini Thank you for asking. As mentioned in my post, gender is a class I have created. It is merely a typesafe enum, try this: public class Gender { public static final Gender UNKNOWN = new Gender(); public static final Gender MALE = new Gender(); public static final Gender FEMALE = new Gender(); private Gender() {} }Isolating
H
6

You can try face4j https://github.com/nischal/face4j/wiki . We've used it on our product http://grabinbox.com and have open sourced it for anyone to use. It works well on GAE.

There is an example on the wiki which should help you integrate login with facebook in a few minutes.

face4j makes use of oAuth 2.0 and the facebook graph API.

Hankhanke answered 5/3, 2012 at 15:21 Comment(1)
@Jus12 its called for face4j not twitter4j. So I would guess not.Surpassing
F
3

I had a lot of difficulty when trying to implement the OAuth signing myself. I spent a lot of time trying to debug an issue with my tokens not actually getting authorized - a common problem apparently. Unfortunately, none of the solutions worked for me so I ended up just using Scribe, a nifty Java OAuth library that has the added benefit of supporting other providers besides for Facebook (e.g. Google, Twitter, etc.)

Fowliang answered 24/9, 2012 at 16:2 Comment(0)
F
1

You can take a look at LeanEngine, the server part: https://github.com/leanengine/LeanEngine-Server/tree/master/lean-server-lib/src/main/java/com/leanengine/server/auth

Frederiksberg answered 5/3, 2012 at 15:32 Comment(0)
L
0

Check facebook's java APIs.

Other examples: http://code.google.com/p/facebook-java-api/wiki/Examples

Leilaleilah answered 5/3, 2012 at 14:39 Comment(2)
I don't believe facebook-java-api 3.0.4 is current with the latest documentation for FB authentication. Instead, I followed steps in accepted answer and then used the restfb framework.Ectophyte
@Ectophyte I'm not surprised; FB changes their API more often that models changes clothes ;) Glad to hear you found a solution :)Leilaleilah

© 2022 - 2024 — McMap. All rights reserved.