passport-facebook: FacebookTokenError

I have been using passport-facebook in my app for many months now, and just now I have run into this issue. I sort of understand what this issue means, but I am confused as to why its just now happening and how to fix it. I noticed someone else mention a similar issue and they just forgot to call done() in their auth handler. I double checked my auth handler to make sure I was calling it, and sure enough, I am calling it. I did recently upgrade to using Express4, and I am using the latest version of passport 0.2.0. Any help or direction on where to look would be appreciated. Thanks!

Passport-Facebook Error:

FacebookTokenError: This authorization code has been used.
    at Strategy.parseErrorResponse (/mnt/data/1/node_modules/passport-facebook/lib/strategy.js:198:12)
    at Strategy.OAuth2Strategy._createOAuthError (/mnt/data/1/node_modules/passport-facebook/node_modules/passport-oauth2/lib/strategy.js:345:16)
    at /mnt/data/1/node_modules/passport-facebook/node_modules/passport-oauth2/lib/strategy.js:171:43
    at /mnt/data/1/node_modules/passport-facebook/node_modules/passport-oauth2/node_modules/oauth/lib/oauth2.js:177:18
    at passBackControl (/mnt/data/1/node_modules/passport-facebook/node_modules/passport-oauth2/node_modules/oauth/lib/oauth2.js:124:9)
    at IncomingMessage.<anonymous> (/mnt/data/1/node_modules/passport-facebook/node_modules/passport-oauth2/node_modules/oauth/lib/oauth2.js:143:7)
    at IncomingMessage.emit (events.js:117:20)
    at _stream_readable.js:929:16
    at /mnt/data/1/node_modules/newrelic/node_modules/continuation-local-storage/node_modules/async-listener/glue.js:177:31
    at /mnt/data/1/node_modules/newrelic/node_modules/continuation-local-storage/node_modules/async-listener/glue.js:177:31
  Metadata:
    stack: 
      - FacebookTokenError: This authorization code has been used.
      -     at Strategy.parseErrorResponse (/mnt/data/1/node_modules/passport-facebook/lib/strategy.js:198:12)
      -     at Strategy.OAuth2Strategy._createOAuthError (/mnt/data/1/node_modules/passport-facebook/node_modules/passport-oauth2/lib/strategy.js:345:16)
      -     at /mnt/data/1/node_modules/passport-facebook/node_modules/passport-oauth2/lib/strategy.js:171:43
      -     at /mnt/data/1/node_modules/passport-facebook/node_modules/passport-oauth2/node_modules/oauth/lib/oauth2.js:177:18
      -     at passBackControl (/mnt/data/1/node_modules/passport-facebook/node_modules/passport-oauth2/node_modules/oauth/lib/oauth2.js:124:9)
      -     at IncomingMessage.<anonymous> (/mnt/data/1/node_modules/passport-facebook/node_modules/passport-oauth2/node_modules/oauth/lib/oauth2.js:143:7)
      -     at IncomingMessage.emit (events.js:117:20)
      -     at _stream_readable.js:929:16
      -     at /mnt/data/1/node_modules/newrelic/node_modules/continuation-local-storage/node_modules/async-listener/glue.js:177:31
      -     at /mnt/data/1/node_modules/newrelic/node_modules/continuation-local-storage/node_modules/async-listener/glue.js:177:31
    name:    FacebookTokenError
    message: This authorization code has been used.
    type:    OAuthException
    code:    100
    status:  500
FacebookTokenError: This authorization code has been used.
    at Strategy.parseErrorResponse (/mnt/data/1/node_modules/passport-facebook/lib/strategy.js:198:12)
    at Strategy.OAuth2Strategy._createOAuthError (/mnt/data/1/node_modules/passport-facebook/node_modules/passport-oauth2/lib/strategy.js:345:16)
    at /mnt/data/1/node_modules/passport-facebook/node_modules/passport-oauth2/lib/strategy.js:171:43
    at /mnt/data/1/node_modules/passport-facebook/node_modules/passport-oauth2/node_modules/oauth/lib/oauth2.js:177:18
    at passBackControl (/mnt/data/1/node_modules/passport-facebook/node_modules/passport-oauth2/node_modules/oauth/lib/oauth2.js:124:9)
    at IncomingMessage.<anonymous> (/mnt/data/1/node_modules/passport-facebook/node_modules/passport-oauth2/node_modules/oauth/lib/oauth2.js:143:7)
    at IncomingMessage.emit (events.js:117:20)
    at _stream_readable.js:929:16
    at /mnt/data/1/node_modules/newrelic/node_modules/continuation-local-storage/node_modules/async-listener/glue.js:177:31
    at /mnt/data/1/node_modules/newrelic/node_modules/continuation-local-storage/node_modules/async-listener/glue.js:177:31

My auth handler:

exports.setupFacebookAuth = function setupFacebookAuth() {
    this.passport.use(new FacebookStrategy({
        clientID: config.FACEBOOK_APP_ID,
        clientSecret: config.FACEBOOK_APP_SECRET,
        callbackURL: config.FACEBOOK_callbackURL,
        passReqToCallback: true,
    }, function(req, accessToken, refreshToken, profile, done) {
        process.nextTick(function() {
            if (profile.hasOwnProperty('emails') && profile.emails.length > 0) {
                var userData = {
                    email: profile.emails[0].value,
                    method: 'facebook',
                    id: profile.id,
                    mixpanelID: null
                };
                //check to see if we have a mixpanel cookie
                if (req.cookies.hasOwnProperty('mp_userID') && req.cookies.mp_userID) {
                    userData.mixpanelID = JSON.parse(req.cookies.mp_userID).distinct_id;
                }

                var query = Account.findOne()
                    .where('email').equals(userData.email);

                if (!req.session.signup) {
                    query.where('facebookID').equals(userData.id);
                }

                query.execQ()
                    .then(function(account) {
                        if (account && !req.session.signup) {
                            userData.mixpanelID = account.mixpanelID || userData.mixpanelID;
                            if (config.EnableMixpanel) {
                                mixpanel.track("login", {
                                    distinct_id: account.mixpanelID,
                                    method: 'facebook',
                                    location: config.mixPanelLocation
                                });
                                mixpanel.people.increment(account.mixpanelID, "facebook");
                                mixpanel.people.set(account.mixpanelID, "last_login", new Date());
                            }
                            var resaveAccount = false;
                            if (!account.facebookID) {
                                account.facebookID = userData.id;
                                resaveAccount = true;
                            }
                            if (!account.mixpanelID) {
                                account.mixpanelID = userData.mixpanelID;
                                resaveAccount = true;
                            }
                            if (resaveAccount) {
                                account.saveQ();
                            }
                            userData.accountID = account._id;
                            return done(null, userData);
                        } else {
                            return done(null, userData, {
                                message: 'No Account Exists'
                            });
                        }
                    })
                    .fail(function(err) {
                        return done(err, null);
                    })
                    .done();
            } else {
                done(null, false, {
                    message: 'No Email Associated'
                });
            }
        });
    }));
};

My routes:

router.route('/facebook/return*')
.get(function(req, res, next) {
    passport.authenticate('facebook', function(err, user, info) {
        return LogUserIn(req, res, next, err, user, info);
    })(req, res, next);
});

//facebook login url
router.route('/facebook*')
.get(passport.authenticate('facebook', {
    scope: 'email'
}));

About this issue

  • Original URL
  • State: closed
  • Created 10 years ago
  • Reactions: 3
  • Comments: 31

Commits related to this issue

Most upvoted comments

I’m late to the party but was pulling my hair out for a good while over this. I eventually figured out what was happening though.

Basically, both route handlers (the primary one and the callback one) were being hit on the Facebook callback.

I had configured them as:

router.get(/(facebook|twitter)/, ...);

router.get(/(facebook|twitter)\/callback/, ...);

Unfortunately, I didn’t realize that both route handlers were being triggered when the call to /facebook/callback was made. This basically passed the request through passport.authenticate twice.

Reworking the route definitions fixed this issue for me (basically reordering them since I end the response in the callback route handler).

Hopefully this helps someone who runs into this 😄

What solved it for me was enabling enableProof: true in the strategy.

passport.use(new FacebookStrategy({ clientID: —, clientSecret: —, callbackURL: “http://—/auth/facebook/callback”, enableProof: true }

hope it helps.

Hello all { @toddbluhm, @maxmert, @daxsorbito @lefnire et al }

I have a REST api in Express that uses a Sessionless Facebook Login using the info from Jeroen Pelgrims. What it does is:

  1. Login with Facebook Strategy
  2. Save token and info in user database
  3. Use that token for Bearer login

What passport-facebook authenticate uses in the callback-page is this: passport.authenticate(‘strategy’, options, user, error)

The behavior is correctly throwing that error! As @jsilveira said in the comments, the error happens if a user logs in twice…

My answer is very simple, I catch the error… Read on there is a but…

// route for facebook authentication and login
router.get('/auth/facebook',
    passport.authenticate('facebook', {session: false, scope : ['email'] })
);
// handle the callback after facebook has authenticated the user
router.get('/auth/facebook/callback',
    passport.authenticate('facebook',  { session: false, failureRedirect : '/'}),
    
    // on succes
    function(req,res) {
        // return the token or you would wish otherwise give eg. a succes message
        res.render('json', {data: JSON.stringify(req.user.access_token)});
    },
    
    // on error; likely to be something FacebookTokenError token invalid or already used token,
    // these errors occur when the user logs in twice with the same token
    function(err,req,res,next) {
        // You could put your own behavior in here, fx: you could force auth again...
        // res.redirect('/auth/facebook/');
        if(err) {
            res.status(400);
            res.render('error', {message: err.message});
        }
    }
);

But, if you guys want it to just log in/authorize again you could insert a callback or redirect in the error part.

Hope this clarifies some issues you guys are having. If you by any chance have questions or requests, dont be shy. Info is on my profile.

PS: stackoverflow.com has an answer for re-auth it follows like this:

app.post('/login', function(req, res, next) {
  passport.authenticate('local', function(err, user, info) {
      if (err) {
      return next(err); // will generate a 500 error
      }
      // Generate a JSON response reflecting authentication status
      if (! user) {
      return res.send({ success : false, message : 'authentication failed' });
  }
  return res.send({ success : true, message : 'authentication succeeded' });
  })(req, res, next);
});

I’m having the same issue. From several tests I’ve made, I realized this is happening when some users click twice on the facebook login button. What I don’t understand is why passport-facebook is throwing the error instead of forwarding it to the callback handler. Any ideas?

Hello Friends, I faced the same issue in my app. It happens because of errors in facebook callback function. When i faced this issue I invoked done(null,profile) directly. it worked and it redirect to the appropriate URL after successfully logging in. But when invoked the done function inside the query function then it gave this error. So try invoking the done(null,profile); directly without querying though the db.If it works for you then it clearly indicates that there is an error in the code before invoking the done function. Then try to fix the error and then invoke the done function.

`passport.use({ clientId:" -----“,clientSecret:”-----",callbackURL:“http://localhost:PORT/auth/facebook/callback”}, (accessToken,refreshToken,profile,done)=>{

//check your console you will find the accessToken and refresh Tokens console.log(“Your accessToken is :”+accessToken); console.log(“Your refreshToken is :”+refreshToken ); done(null,profile);

});`

Same issue… How can i solve this?

@rocketspacer I’m using v4

If you happen to do app.use('auth/facebook', passport.authenticate(...) instead of app.get, then you would end up with the routes passing through.

I just wanted to point out what was going on in my particular implementation since it was not immediately clear and was resolved by reordering my routes so that they were hit in the correct order.

@flaviolivolsi and @Connoropolous Seen it here. https://developers.facebook.com/docs/graph-api/securing-requests#appsecret_proof

Require proof on all calls In the Advanced section of your app’s settings, you can require use of appsecret_proof. When this is enabled, we will only allow API calls that either include appsecret_proof or are made from the same device the token was issued to.

I have it activated in my app. So it assumed the second call on that auth code was a “bad guy”.

(…) require proof on all calls (…) prevents bad guys from making API calls with your access tokens from their servers - Facebook Graph API

Well I am sorry to say that I have made no progress on this issue. Partially due to time, but also because we have such a small amount of users that use facebook on our site, I have not run into this issue for a while. I still believe it exists, but no solution yet. It might be best for others to just go the same route as https://github.com/HabitRPG/habitrpg/issues/4221 and just integrate directly with the facebook js sdk.

Same issue. Didn’t figure out yet.