Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Re-validating expired AccessToken (or get new one) when logging into app via Facebook Strategy using Passport

Under a variety of circumstances, Facebook's internal AccessToken (used for various features in the Facebook API and other cases) may become invalid. For example, if a user changes her password between sessions of using an application that relies on Facebook Login. There are a few other cases as well.

What happens, however, is that your application essentially crashes, with an error like this: data={"error":{"message":"Error validating access token: Session does not match current stored session. This may be because the user changed the password since the time the session was created or Facebook has changed the session for security reasons.", "type":"OAuthException", "code":190, "error_subcode":460}}.

Passport's Facebook Strategy uses the AccessToken, and it's available to an app as soon as a user is logged in / authenticated via Passport. The problem, however, is what to do when the above error is encountered. Facebook gives a convoluted re-auth flow as an example in PHP, but the general sense is that you need to re-authorize your app with Facebook.

The question is, even when removing the Facebook app from your Facebook page, and forcing the application relying on Facebook Login to re-authorize itself, it seems that Passport + Facebook Strategy is still picking up the last existing AccessToken from the browser's session storage. (At least that's what I'm seeing with Mozilla/Fx 26). I have re-authorized my app several times, but when debugging and looking at what Passport returns, I always get back the same invalid AccessToken. Seems like if an AccessToken exists in session-storage, Passport picks that up instead of getting a new one from Facebook.

So is there a way - within Passport + Facebook Strategy - to essentially ignore or override any stored AccessToken and always request a new one from Facebook, in the event of this kind of error? Not at all clear how to make that happen. Thanks for the help.

Update: The sample code for invoking the strategy has refreshToken as a parameter; what does this do? Is there a possible solution with this parameter?

passport.use(new FacebookStrategy(
{
...
},
function(accessToken, refreshToken, profile, done) 
{
like image 566
Lew Avatar asked Nov 08 '13 22:11

Lew


1 Answers

I found the answer to my question, and will leave it up in case anyone else encounters the same issue.

Passport, in its VerifyCallback function, passes the AccessToken to the application. The recommendation (tacit or otherwise) is to of course save this AccessToken as part of the user's persisted record (say, a MongoDB document). It'll be needed when making subsequent calls to the Facebook API, and though it can probably be safely retrieved from the request.user object passed around through the Express middleware, having your own local copy probably makes sense.

Now, when you're done authenticating the user via Passport, you still will pull up your user's persisted record to get other user data. Passport documentation features a generic function called Users.findOrCreate() to serve as a model/prototype of this function for you to implement. And here's where the issue lies: If you find a user (one already exists) via this function, you'll be using that record throughout your app. And if that record also holds an AccessToken, and that AccessToken has changed, then of course you'll be in trouble: Passport/Facebook is passing you a new AccessToken, but you're using an outdated one.

So the simple fix is this: If you've implemented a Users.findOrCreate() function to persist a user and rely on this to retrieve a user's set of data - and that record also stores the AccessToken - then make sure you check the current (old) AccessToken to the one Passport/Facebook is passing you in the VerifyCallback when a user has just been authenticated; if the two are different, update the user's persisted record and use this new Token.

like image 53
Lew Avatar answered Jan 01 '23 13:01

Lew