Facebook OAuth Login - access_token API returning "This authorization code has been used"

Srivatsan picture Srivatsan · Sep 27, 2013 · Viewed 13.5k times · Source

This question has been asked a few times on Stack, but there have been no real answers. Let me try to explain my situation anyways.

We use an application that uses Facebook OAuth2 login. This login used to work fine till last week, and all of a sudden it is troubling us now.

Application Flow:

Step 1: User presses login with Facebook button on our website

Step 2: Redirected to Facebook login/authorization page

Step 3: On authorizing the app, the callback comes to our application, with a short lived "code" param.

Step 4: This "code" param would be exchanged for a 60 day Access token using "https://graph.facebook.com/oauth/access_token" URL.

Error in Step 4:

When we try to exchange the short living "code" for the access token, we get this error from Facebook.

{"error":{"message":"This authorization code has been used.","type":"OAuthException","code":100}}

Observation:

  1. For users who are newly coming to the application, the above-said error does not occur.
  2. For a returning user this call fails with the above-said error.
  3. Our application is live for more than 9 months now, and this error has come only in the past 7-10 days. We have had thousands of users using it successfully prior to that.

What I already got from Forums:

Here is my interpretation of what I read. May be inaccurate. Facebook has some weird policy that necessitates the app developer to maintain the temporary 10 minute code until the 60 day code that was obtained during the first login expires. So we should create a cookie with the Access token on the user's browser. I was even able to see people modifying their code in order to create the cookies.

What's really bothering me?

  1. The suggested solutions assumes that the cookie that they create would be present in the user's browser always. This is a bad assumption to make, as the cookie may be erased at any time.
  2. I have another app Id/app secret that I use for my development (i.e localhost), and that works perfectly. The login happens fine out there, But its only the product machine that has the problem.
  3. This problem didn't happen on the production machine for nearly 10 months since we launched the app, and it has come all of a sudden. Worst of all, I am unable to get any record of recent changes that breaks this flow.

Edit:

Platform: Python, Google Appengine. We do not use any Facebook SDKs, we make direct HTTP Calls to all the login URLs. Call that fails : https://graph.facebook.com/oauth/access_token - we are passing the appId, secret and code (obtained from facebook) within 20 seconds of the first call happening.

Hope there is enough information here to show that our code is not totally incorrect. Any tips/pointers from people who have encountered and solved this problem is Welcome. If its a Facebook bug, and the Facebook dev comes to notice, I would be even happier.

Answer

royse41 picture royse41 · Sep 29, 2013

I got round this issue by using a random GUID which is appended to each callback url i pass into facebook. It seems the code that facebook returns is made up of a few parts including the redirect_uri parameter you have to specify. By using this GUID trick, your app continues to work but facebook thinks it's a different URL hence generating a new code.

If you store that GUID in a temporary session, it's always the same. Here's a very cut down version of what I mean. I'm using C# but the solution will be the same:

Before i start the oauth process:

Session["facebook_buster"] = System.Guid.NewGuid().ToString();

Then to kick off the login:

var facebook = new FacebookClient();

var loginUrl = facebook.GetLoginUrl(new
{
    client_id = ...,
    redirect_uri = ..."/facebook/oauthcallback?buster=" + Session["facebook_buster"].ToString(),
    display = "popup",
    scope = "publish_stream,user_photos"
});

And then in my callback method, when I want to exchange that code for a new access_token:

var facebook = new FacebookClient();

dynamic result = facebook.Post("oauth/access_token", new
{
    client_id = ...,
    client_secret = ...,
    redirect_uri = ..."/facebook/oauthcallback?buster=" + Session["facebook_buster"].ToString(),
    code = Request["code"] // this is the returned code from the first method
});

Note in that second method i'm using the same session key so that the authorization code is successful.

Been testing this all morning by revoking permissions / manually changing my stored access_token (in my db) / removing my stored access_token completely and it works every time.

Hope this helps!