8

I am developing an Android app. With the impending deprecation of Facebook's offline_access permission I am trying to use the Graph API to extend the Facebook token.

        https://graph.facebook.com/oauth/access_token?             
        client_id=APP_ID&
        client_secret=APP_SECRET&
        grant_type=fb_exchange_token&
        fb_exchange_token=EXISTING_ACCESS_TOKEN

Can anyone provide detailed code that demonstrates how to implement the code above into an android app and obtain the refreshed Facebook token?

Thank you for your time.

Update Two:

Progress (I think)!

Using the full URL with the facebook request method results in the base URL being added to the the beginning of the URL So instead of

String refreshUrl = "https://graph.facebook.com/oauth/access_token?client_id=12345678910&client_secret=abcdefghijklmnopqrstuvwxyz&grant_type=fb_exchange_token&fb_exchange_token="+currentAccessToken;

should use

String refreshUrl = "oauth/access_token?client_id=12345678910&client_secret=abcdefghijklmnopqrstuvwxyz&grant_type=fb_exchange_token&fb_exchange_token="+currentAccessToken;

However I am now getting the response {"error":{"message":"Error validating application. Invalid application ID.","type":"OAuthException","code":190}}

Update One:

Here is what I have I tried. The code completes, that is OnComplete on the listerner is called, BUT the response does not contain a new access token or expiry value.

    void refreshWithGraph() {
        AsyncFacebookRunner extendingAsyncRunner = new AsyncFacebookRunner(facebook);
        Bundle parameters = new Bundle();

        //the variable currentAccessToken is obtained after authorisation is complete using currentAccessToken = facebook.getAccessToken();

        String refreshUrl = "https://graph.facebook.com/oauth/access_token?client_id=12345678910&client_secret=abcdefghijklmnopqrstuvwxyz&grant_type=fb_exchange_token&fb_exchange_token="+currentAccessToken;


    extendingAsyncRunner.request(refreshUrl, parameters, new RefreshListener(), null );
   }

Here is my version of RefreshListener...

  //REFRESH LISTENER
    public class RefreshListener extends BaseRequestListener {

        public void onComplete(final String response, Object state) {


            try {
               final JSONObject json = Util.parseJson(response);

                runOnUiThread(new Runnable() {

                    @Override
                    public void run() {             
                        tvRefreshResponse.setText("IN REFRESH LISTENER ONCOMPLETE\nResponse is " + response);
                        tvRefreshToken.setText("IN REFRESH LISTENER ONCOMPLETE\nToken is " + facebook.getAccessToken());
                        tvRefreshExpiry.setText("IN REFRESH LISTENER ONCOMPLETE\nFacebook expiry is " + millisecToDate(facebook.getAccessExpires()));

                    }

                }); //end runOnUiThread



            } catch (JSONException e) {
                runOnUiThread(new Runnable() {

                            @Override
                            public void run() {
                                tvRefreshResponse.setText("IN REFRESH LISTENER ONCOMPLETE CAUGHT JSON EXCEPTION \nResponse is " + response);

                            }

                         }); //end runOnUiThread

            } catch (FacebookError fe) {
                runOnUiThread(new Runnable() {


                            @Override
                            public void run() {
                                tvRefreshResponse.setText("IN REFRESH LISTENER ONCOMPLETE CAUGHT FACEBOOK ERROR \nResponse is " + response);

                            }

                         }); //end runOnUiThread
            } //end catch Facebook error

        }  //end onComplete


        @Override
        public void onIOException(IOException e, Object state) {
            tvRefreshResponse.setText("IN REFRESH LISTENER IOEXCEPTION \nException is "+ e.getLocalizedMessage());

        }

        @Override
        public void onFileNotFoundException(FileNotFoundException e, Object state) {
            tvRefreshResponse.setText("IN REFRESH LISTENER FILE NOT FOUND EXCEPTION \nException is "+ e.getLocalizedMessage());
        }

        @Override
        public void onMalformedURLException(MalformedURLException e, Object state) {
            tvRefreshResponse.setText("IN REFRESH MALFORMED URL \nException is "+ e.getLocalizedMessage());

        }

        @Override
        public void onFacebookError(FacebookError e, Object state) {
            tvRefreshResponse.setText("IN REFRESH ONFACEBOOK ERROR \nException is "+ e.getLocalizedMessage());

        }
    } //end RefreshListener

The code completes, that is OnComplete on the listerner is called, BUT the response does not contain a new access token or expiry value. The response is...

 {"id":"https:\/\/graph.facebook.com\/oauth\/access_token","shares":78,"comments":1}

When I put the same URL (with a alphanumeric current token value) into a web browser the response DOES include an access token.


Related Info

Facebook's offline_access permission will be deprecated on 1st of May, 2012

Please don't suggest using the extendAccessTokenIfNeeded function in onResume() instead. I am also having trouble with that and it is the reason why I am looking into the Graph API token refreshing :-)

Related Stack Overflow Questions

Is it possible to extend Facebook tokens with extendAccessTokenIfNeeded in an Android app? (my question)

How would offline_access work after deprecation after May 1st?

Facebook access token can not be extended

Protecting app secret for extendAccessToken usage (Java/Android)

Relevant Facebook links

Facebook Android Tutorial

Facebook offline_access permission deprecation

Community
  • 1
  • 1
Mel
  • 6,214
  • 10
  • 54
  • 71
  • With all the links above, why do you need any more detailed information? Looks like you have enough documentation to implement this.. – Shreeni Apr 10 '12 at 04:02
  • You would think I could work it out, but no. :-( All the easy question on Stack Overflow get fully answered very quickly so I guess this isn't as easy as it first seems... – Mel Apr 10 '12 at 04:24

1 Answers1

7

To be honest, I'm a bit confused - looks like you have everything to get it done - and it's simple. But let me try to answer your question. Here is the code from my C# project where I extend app's token with my comments in case you are not familiar with C# languages and classes:

string currentToken = "token from somewhere";

// WebClient is used to send GET request to the given URL
// and receive the response
using (var req = new System.Net.WebClient())
{
    // create URL string and fill it in with data (app Id, secret, and current token)
    var extendTokenUrl = string.Format(
        "https://graph.facebook.com/oauth/access_token?client_id={0}&client_secret={1}&grant_type=fb_exchange_token&fb_exchange_token={2}",
        FB_APP_ID,
        FB_APP_SECRET,
        currentToken);

    // send GET request and download the response
    var response = req.DownloadString(extendTokenUrl);

    // if all is good, response will be a string which looks like this:
    // access_token=<<THE TOKEN GOES HERE>>
    var newToken = response.Substring("access_token=".Length);

    // now save newToken in your DB INSTEAD of currentToken - 
    // so all calls will be made with extended token
    SaveTokenInDB(newToken);
}

hope that helps, and translating this into Java should be straightforward.

avs099
  • 10,937
  • 6
  • 60
  • 110
  • Hi thank you for your response. I am confused myself! What I'm told is an easy task is not getting any response from Android developers - even with the 100 point bounty! My coding problem is that the response from Facebook does not contain an access key. However, When I just put the whole URL in a browser I do get an access key. I have updated my answer to include my previous attempts. – Mel Apr 17 '12 at 10:38
  • I noticed right away that you parse JSON in your RefreshListener - but that's wrong, I believe. You are not getting JSON back but just a raw string. Also - if you paste value of "String refreshUrl" in the browser, will you get proper token? Are you 100% sure you are doing GET request (instead of POST)? Also - do you have sources for AsyncFacebookRunner? can you find out WHERE it actually sends HTTP request - and log exact URL, HTTP method there? Also - if you do NOT use AsyncFacebookRunner but just standard HTTP request component - will it work? – avs099 Apr 17 '12 at 11:55
  • Thank you, I'm making progress...When I alter the code in the SDK's Util.java code (which is the class that actually does the http request) I start getting the same response as from typing an address in browser. But I now realise that the expiry figure is very small (5143168). When I refresh the figure always decreases... Any ideas on this? Response example... access_token=AAAEparERUxABAFSPLEI8VNAwo8FLsxOpR6bZAgyeRxj0JAtGBBqMv5y3gnMkvy0TMCrnKuF0Jp6S1DSpiO8mmiy3JMBV2XZASAimYwZDZD&expires=5143168 – Mel Apr 17 '12 at 21:44
  • expires=5143168 - it's number of seconds for how long token is valid, in seconds. It's 59-something days - which is the duration FB promises for the extended token. So looks like all is good. – avs099 Apr 18 '12 at 12:46
  • Thank you. I realise that the token does not actually refresh. When I attempt to refresh with a few seconds in between refreshes I get the same token and the expiry goes down by just a few seconds. I'm think I read somewhere (can't find where) that tokens can only be refreshed once every 24 hours? Do you think there could be another reason? – Mel Apr 19 '12 at 01:13
  • unfortunately i do not have an official link, i also have a feeling I read somewhere about 1 day before token gets actually refreshed but I do not remember exactly. Anyway, just give it a try tomorrow :) – avs099 Apr 19 '12 at 01:54
  • Will do. Thank you for all your help. I have assigned the bounty (as it was about to expire) and I will mark question correct after I have refreshed token with a 24 hour gap between refreshes. Fingers crossed! – Mel Apr 19 '12 at 02:42