Passport.js: passport-facebook-token strategy, login through JS SDK and THEN authenticate passport?

matthiasdv picture matthiasdv · Jan 5, 2014 · Viewed 14k times · Source

I was looking for a way to let my client authorize with the facebook JS SDK and then somehow transfer this authorization to my node server (so it can verify requests with the fb graph api)

I stumbled across: https://github.com/jaredhanson/passport-facebook/issues/26

&

https://github.com/drudge/passport-facebook-token

what seems to be an entirely different strategy from passport-facebook.

Am I correct when assuming that:

One logs in with the fb JS SDK, and then the facebook-token strategy somehow extracts the token and fb id from the document or body object?

Or is there any other decent way to achieve this? I'm namely trying to avoid the redirects enforced by the server SDKs

Answer

Orangetronic picture Orangetronic · Jun 29, 2014

I've spent a couple of days this week trying to figure out the best way to use Facebook Authentication for a private API, using passport.js — passport-facebook-token is perfect for this.

You are correct in assuming these are two separate authentication strategies. You don't need passport-facebook installed to use passport-facebook-token.

If you have Facebook authentication implemented in the client-side JS (or iOS etc.), and are looking for a way to then authenticate API requests using your user's Facebook authToken, passport-facebook-token is a really elegant solution.

passport-facebook-token works totally independently of passport-facebook, and basically handles the redirects required by Facebook internally, before passing the request along to your controller.

So to authenticate an API route using passport-facebook-token, you'll need to set up a passport strategy like so:

passport.use('facebook-token', new FacebookTokenStrategy({
    clientID        : "123-your-app-id",
    clientSecret    : "ssshhhhhhhhh"
  },
  function(accessToken, refreshToken, profile, done) {
    // console.log(profile);

    var user = {
        'email': profile.emails[0].value,
        'name' : profile.name.givenName + ' ' + profile.name.familyName,
        'id'   : profile.id,
        'token': accessToken
    }

    // You can perform any necessary actions with your user at this point,
    // e.g. internal verification against a users table,
    // creating new user entries, etc.

    return done(null, user); // the user object we just made gets passed to the route's controller as `req.user`
  }
));

It's worth noting that the User.findOrCreate method used in the passport-facebook-token Readme is not a default mongo/mongoose method, but a plugin that you'll have to install if you want it.

To use this auth strategy as middleware for any of your routes you'll need to pass it an access_token object either as a URL parameter, or as a property of the request body.

app.get('/my/api/:access_token/endpoint', 
        passport.authenticate(['facebook-token','other-strategies']), 
        function (req, res) {

            if (req.user){
                //you're authenticated! return sensitive secret information here.
                res.send(200, {'secrets':['array','of','top','secret','information']});
            } else {
                // not authenticated. go away.
                res.send(401)
            }

        }

NB. the access_token property is case-sensitive and uses an underscore. The documentation for passport-facebook-token isn't extensive, but the source is really well commented and pretty easy to read, so I'd encourage you to take a look under the hood there. It certainly helped me wrap my head around some of the more general ways that passport works.