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.
- You create a link on your web UI (generally labelled “Login With Facebook” or something like that) to the Facebook.getLoginRedirectURL() URL.
- Facebook will authorise the user with the permissions you requested, and redirect the user to your “redirect_uri” as specified above.
- 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.
- 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 –
- User clicks a link on your site to Facebook.getLoginRedirectURL()
- Facebook asks them for their username/password to log in to your application
- Assuming they authenticate with Facebook, Facebook then redirects the user to your “redirect_uri” with a parameter “code” passed along.
- You use the “code” parameter to query the Facebook authentication service – Facebook.getAuthURL(request.getParameter(“code”))
- 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!
Related posts:
Hi, first of all: thanks for this post.
I try it, but i get an error in the readURL method: the error is
java.lang.IllegalArgumentException: Invalid uri …
I think that there is some not valid char in the uri (something like : or /)
Didn’t you got the same error?
thanks again
Bye
No didn’t get anything like that. I’m guessing your error comes from the line “url.openStream();”?
In which case the URL can not be connected.
What’s the URL that get’s passed to readURL()?
(remove your secret key before posting it)
Hi Richard, Thanks for writing this up!
Quick Question: what does IOUtil.urlToString do?
Never mind — I see. It must be executing the HTTP get request, returning the result as a string, then parsing the JSON.
Correct, it’s part of the code.google.com/p/visural-common library, and just opens a connection to the URL, reads the result and returns it as a string.
Hi
Thank you for this blog to shed a light about java implementation with Facebook OAuth2.0.. I’d like to test and try this approach. Can you specify which library JSONObject is from?
The JSONObject is from the json.org Java implementation –
http://www.json.org/java/index.html
Hi,
Thanks for a great post, I implemented it for an iFrame application, but for some reason the oauth page comes at first grayed, only after I press allow it returns and looks ok, Can you elaborate on this issue?
Thanks,
Dan
Correct, Facebook doesn’t allow you to implement this in an iframe afaik, I believe as a security precaution. There are several posts about in on Facebook’s developer forum.
Thanks, Richard!
According to Graph API, the publishing to FB is needed to post a form targeting https://graph.facebook.com/profile_id/feed. My situation is a servlet or filter would formulate a message and then post it to FB. Whthin a servlet or a filter, I would like to know how to mimic posting a form with HttpServletResponse back to the url above. Beside using Apache HttpClient or out.write( a html form), is there a bettter way to handle it?
HttpClient is a fully featured solution, but might be overkill. I’m considering adding a simple solution to visural-common for this and covering in a future blog post. Something along the lines of – http://www.devx.com/Java/Article/17679/1954
I used GAE library HTTPRequest and URLFetchService to solve the problem. But, I am looking forward to seeing your solution.
Thanks again.
I just wanted to say- thanks so so so much.
Your post is a ray of sunshine in the sea of crappy misinformation that is programming with java and facebook.
Keep ‘em coming!
Thanks, this was a really good article and helped a bunch, thanks!
Hi richard,
Thanks for the api.
I’ve implemented a the graph authentication mechanism and i’m able to get an access token. however facebook does not redirect me to the url i specified in the ‘redirect_url’ but instead displays the token on the ‘https://graph.facebook.com/oauth/access_token?.*‘ page. where could the error be.
regards.
Correct, the client doesn’t get redirected to the redirect_uri when receiving the access token – it’s just to prove to facebook that you are the originator of the original request.
The user (client browser) would have been redirected to the redirect_uri (at which you place the servlet filter) in the previous step (3). In step 4, your servlet filter on the server side accesses the access_token URL to retrieve it to make subsequent requests to the graph API.
hi, im working with a facebook apps with netbeans an i cant get the authentification values of code, and acces token, i read your code and i have a problem with this line:
if (StringUtil.isNotBlankStr(code))
because i dont know where you define that object
i need you help please.
Regards
@Kike
StringUtil is part of the visural-common library, you can download it at http://code.google.com/p/visural-common/
Hi Richard,
Thanks all for your great article.
I need some helps about which mecanism is necessary between my client side (in Flex – AS3) and my server side (in JAVA – with Spring) to make a Facebook authentication ? I do a Facebook application, so, when Facebook plateform launch my application, which authentication mecanism should be done ? My Flex app client, must just call a service (on server side), and in this service can I do the same way like in this article ?
Thank you very much,
Best regards
Anthony
@Anthony
I don’t think the set up described in this article would work for you. I don’t know much about Flex, but the above authentication relies on redirecting the client through authentication pages on Facebook’s site, so it only really works for traditional websites.
I’m not able to advise on the best option for Flex, so I’d suggest the Facebook developer portal is probably the best place to start.
HTH
Hi Richard,
Just a question : why not used the OAuth Java Library instead your implementation ? What is the best approach ?
Thanks Richard,
Anthony
@Anthony
OAuth 2.0 and OAuth 1.0 are very different in how they operate.
OAuth 2.0 (at least Facebook’s implementation) is quite simple (as you can see with the amount of code above needed to implement it). Unlike with the original OAuth, I don’t think a fully fledged library is needed.
hey richard, thanks a lot of the effort!
I’m trying to implement the same functionality but for a facebook application which is using Iframe.
when I redirect to https://graph.facebook.com/oauth/authorize from the application within the facebook it shows a blank page. do you know of anyways how to achieve this for an iframe application?
thanks again
@ali
No, as far as I know the OAuth 2.0 authentication method doesn’t allow this. It was possible previously with the old Facebook API, but they seem to have made it mandatory that you redirect the user to Facebook in the main browser page for the new API.
Does facebook offer the possibility to get a access-token that can be stored to db and used
for the authenticated user ? e.g like twitter provides a access-token and access-token-secret once
the user oAuths the external app..
Hey, Could you post the web.xml details for your filter. i’m having trouble with multiple-redirects and i think my url mapping may be incomplete.
@Joe Mansori
Hi Joe, I believe that the access token that you get from Facebook can be expired by Facebook at any time. You could store it for later use, but you should check that it’s still active and the log the user out of your site if the token is no longer good.
@paul
You just need it mounted at a single URL – /fbauth
e.g.
<filter>
<filter-name>FBOAuth</filter-name>
<filter-class>com.visural.servlet.FBOAuth</filter-class>
</filter>
<filter-mapping>
<filter-name>FBOAuth</filter-name>
<url-pattern>/fbauth</url-pattern>
</filter-mapping>
Thanks for this lovely example.. really saved a lot of trouble in implementing the OAuth protocol for Facebook.. Kudos for sharing..
Hey, is it possble that facebook have changed the reply format of the Facebook.getAuthURL(code) request. The string splitting decribed above does not seem to work, and i’m wondering should the request string be treated as a ‘signed_request’? Thanks again. P
@paul Hmm, still works ok for me. What sort of data are you seeing coming back?
It’s seems to me whenever we requesting for user to grant us “offline” permission, the auth response does not return with “expires” parameters, thus the string splitting function will not work..
Commnet here in case some one facing similar situation
Anyway, thanks for sharing.
@Khoo Chen – thanks for the info – that makes a lot of sense.
Hi Richard,
Can I view the Facebook login page in a widget or iframe?
Thanks.
@pol – no as mentioned before it needs to render in a dedicated browser window for security.
Is it possible to make a replicate of facebook login in java and view it on widget?
Thanks a lot.
@pol if it were possible I would guess it would be against the Facebook API’s terms of use. The user is redirected to Facebook so that they know they’re giving their login details securely to Facebook, not to your site.
hi,
I’m developing a desktop java IM client for facebook(its my CSE year-2 term final project,so thats obvious how much i am intended to do it!)..i am trying to use facebook-java-api…but i am in deep water to find a way about how to log in using my app!? is there an api for login that i need to use in my java code?i have api key & secret key for my app..what i need is very simple..a gui that requests user to enter his facebook mail id & password,then my app will allow him to chat in facebook through itself…i am running out of time..& really in a fix….is there anyone kind enough to show me the perfect way to do so…i will be greatfull.i’ve gone through a lots of examples but all of them are on servlets or needs to access through website.I need to build something like pidGin or eBuddy mobile messenger…all i need is just a perfect example to login as i stated…looking forward to having a great response….
thanks in advance-
dibosh
Hi Richard,
You mentioned using Apache HttpClient for http requests. I am bugged with serialisation exceptions for days now when trying to use Apache Solr CommonsHttpSolrServer within a IDataProvider implementation. Do you have an idea how to detach HttpClient in Wicket to avoid serialisation on it?
GReg
Hi Richard,
Is it possible to autofill the login page of facebook with your email and password?
Thanks a lot!
@pol that’s up to facebook + user’s browser i.e. whether the browser is set to remember / auto-fill fields
@Greg you could detach it to a HttpSession parameter – technically putting non-serializable stuff in HttpSession is against servlet spec, but unless you’re clustering w/ session replication it shouldn’t cause any issues
Hi,
I’m trying to build an app that login with my username&password to facebook, and just get the HTTP src of the home page (or a user object if it simpler to get the info from there).
do i need to use the app key / secret key / whatever?
can i use the code above?
there shouldn’t be a place for putting my password?
i will appreciate if you could provide some simple main just to run things out…. cause those classes doesn’t tell me a lot…
thanks in advance!
@Itay the above code is for the Facebook Graph API for applications to authenticate via Facebook.
You may want to do some background reading before diving in – I’d suggest starting at http://developers.facebook.com/docs/authentication/
Hello Richard,
This tutorial is superb! @Allessandro and maybe one small improvement. It turns out that the accessToken and the authCode can sometimes cause trouble if they are not URL encoded. I believe that this was why he was getting this invalid uri error. So I would throw in some utf8 encoding and encode the authCode when building the AuthURL. Something like this:
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="+encode(authCode);
}
public static String encode(String authCode) {
String encodedAuthCode = null;
try {
encodedAuthCode = URLEncoder.encode(authCode, "UTF-8");
} catch (UnsupportedEncodingException e) {
// This should never happen, we have specified UTF-8 correctly, Log error
}
return encodedAuthCode;
}
The same needs to be done when building the uri in the authFacebookLogin in the ‘UserService’ for the access token.
@Darren Brown – good pick up! My bad.
Weirdly, I am getting this exception
HTTP ERROR 500
Problem accessing /register.jsp. Reason:
sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Caused by:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path
———–
At line
InputStream is = url.openStream();
where url is https://graph.facebook.com/oauth/access_token?client_id=xxxx&redirect_uri=http://localhost:9092/register.jsp&client_secret=xxxxxx&code=xxxxx8U
Not sure what’s going wrong.
@Nishant if you hit the http://localhost:9092/register.jsp&client_secret=xxxxxx&code=xxxxx8U URL directly do you get the same error?
I guess you meant, http://localhost:9092/register.jsp?code=2.4xxxCA — yeah It throws the same exception. I basically have one HTML that contains the link same as you provide by getLoginRedirectURL() and the redirect_uri points to the http://localhost:9092/register.jsp, which is same as FBOAuth filter. The error is thrown in FBOAuth#readUrl at InputStream is = url.openStream();
However when I try, https://graph.facebook.com/oauth/access_token?client_id=xxx&client_secret=xxx&code=xxx&redirect_uri=http://localhost:9092/register.jsp in browser, it shows access token etc, correctly. Weirdly, it seem to be machine issue — so I tried this No more ‘unable to find valid certification path to requested target’ It does not help either.
Finally, as last try, I tried on Windows. The code seem to be working. But I am unsure why it does not work on Ubuntu Karmic Koala (9.10)
Great great post sir! Thanks a lot!
Hi,
Thanks for the great post. i am getting “java.security.AccessControlException: access denied (java.net.SocketPermission graph.facebook.com:443 connect,resolve)” exception. Do you have any solution for this problem? Thanks again.
Greate post. Keek up the good work. I was looking for simple OAuth solution and here it is!
really nice. I started researching about fbconnect and have a question: how will you know that the user logged out from facebook and now he requests a page from your site?
thank you very much
@remus you can store the access-token against your users session and periodically make a facebook graph API call with the token. If Facebook response that the token is no longer valid then you can log them out of your site, if you wish.
hey,
I got the same error as Alessandro, i.e.
Invalid uri ‘https://graph.facebook.com/oauth/access_token?client_id=MY_APP_ID&redirect_uri=http://localhost:8888/MY_APP&client_secret=MY_APP_SECRET&code=CODE_RETURNED_BY_FB': Invalid query
but when i enter the same url in the address bar, it returns the correct output i.e. access token and expire time in the body. Could you guess what’s wrong?
If its related somehow, i’m trying to run this locally on the google app engine’s plugin for eclipse.
Thanks in advance.
@Ankit see Darren Brown’s comment re: URL Encoding the strings correctly
That works! this never crossed my mind. Thanks a ton.
Hello Nichols,
Great post… I am making a Liferay portlet, for fetching facebook data…
I’m getting some problem in authentication…
Everything seems to be working fine on my machine, but when I am using it over LAN(throught my IP followed by my server port), it is going in a infinite loop and then message appears – connection to the server was reset. In the mean time, in status bar, it shows repeatedly connecting/connected to facebook. I’ve given the same IP in my facebook app for callback. I am using the following url:
https://graph.facebook.com/oauth/authorize?client_id=&redirect_uri=/oauth_redirect&scope=
(I’ve tried by removing /oauth_redirect too, but didn’t worked)
Hope to see some positive response…
Thanks and Regards…
@Apporva
I bleieve your redirect URI must be a fully qualified URL, e.g. “http://www.onmydoorstep.com.au/fbauth” and be the same URL has you registered with Facebook for your app.
Great post! Life saver
Although I’m having the same issue as Nishant, i.e.,
HTTP ERROR 500
Problem accessing /auth. Reason:
javax.net.ssl.SSLHandshakeException: Could not verify SSL certificate for: https://graph.facebook.com/oauth/access_token?...(etc)
It works fine on the address bar and it appeared only after encoding the URL as suggested by Darren Brown (before, I was getting the invalid URI exception). Working on FF 3.6.13 on ubuntu Maverick Meerkat (10.10). I do all my development on linux so I would really appreciate any suggestion to overcome this.
Thanks in advance,
aleadam
Well, it seems that it’s a known issue in appengine:
https://groups.google.com/group/google-appengine-java/browse_thread/thread/c19d8407128e3eae/de7ec403d542e11f?#de7ec403d542e11f
So, to fix the issue, I replaced the readURL() function with the following code. I hope someone will find it useful.
private String readURL(URL url) throws IOException {
FetchOptions opt = FetchOptions.Builder.doNotValidateCertificate();
HTTPRequest request = new HTTPRequest (url, HTTPMethod.GET, opt);
URLFetchService service = URLFetchServiceFactory.getURLFetchService();
HTTPResponse response = service.fetch(request);
if (response.getResponseCode() == HttpURLConnection.HTTP_OK) {
byte[] content = response.getContent();
ByteArrayInputStream bais = new ByteArrayInputStream (content);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int r;
while ((r = bais.read()) != -1) {
baos.write(r);
}
return new String(baos.toByteArray());
} else {
return null;
}
}
Thanks a ton Man!!! I was trying to write my first FB application but faced lot of problems due to lack of documentation over internet. Your article helped immensely. Thanks for writing such an article. Keep up the good work
Thank You, Thank you very much. This is my first approach to Java. I started at 09:50 am and ended at 01:55 am. I LEARNED A LOT FROM YOU. Thank you very much!
Nice information
Hi guys, this example shows using facebook api on servlets but could any one help me to create a similar on a desktop application????
Thank you,
-harish
Hi Richard…Thanks for your greaaaaat example… It works c0ooo00ll… but I have a strange problem and I can’t figure it out!!!
Whenever I run the sample code I have to sign in with the “Owner Account” of my application to get the access_token , otherwise it throws exception “java.lang.RuntimeException: Access token and expires not found” . Do you have any idea what the problem might be?
thaaaaaaaaaaannxxxx
Ok the problem is solved but I don t know how
just a small improvement to what Khoo Chen Shiang said… Yes if you use offline_access permission for the application , the Try block will be changed. My teammate wrote the code for the offline mode.
try{
String code = request.getParameter(“code”);
String accessToken = null;
if (StringUtil.isNotBlankStr(code)) {
String authURL = Facebook.getAuthURL(code);
URL url = new URL(authURL);
String result = Facebook.readURL(url);
String[] pairs = result.split(“=”);
accessToken = pairs[1];
}
Nice article.
Thanks for the post.
Helped me a lot. It explains a lot of things that i couldn’t find on other websites.
I could be able port your logic to #nodejs.
Thank You!
Thank you very much for the tutorial, This works fine
hi Richard,
thanks for the great post.
My question is , with the above peace of code we are able to get the access_token for the face book user to act on behalf of him from our application,,,,,,,
But is there a way to have a common token for multiple social networks like face book and twitter … (i am able to get token for both of them separately).
thank you.
Thanks for your code, let me how I can get this into my website how i can make page only for users login through facebook.
Thanks for above example.
But FB will give access_token & expires after authentication.
After expires time can we access FB using same access token?
Hi richard, congratulations for this excellent tutorial. I’d like to ask you what libraries or Binaries you did use for reading the “inputStream” from the JSONObject response (resp), i saw you used “IOUtil” (IOUtil.urlToString(new URL(“https://graph.facebook.com/me?access_token=” + accessToken)))) , BUT i can’t find it. I’ll appreciate you rhelp, thanks.
@Pablo
http://code.google.com/p/visural-common/
Thank you again, it works perfectly