24

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

Cœur
  • 37,241
  • 25
  • 195
  • 267
matthiasdv
  • 1,136
  • 2
  • 15
  • 26
  • After some digging i'm thinking the req.body.accessToken needs to be set by the client and then it can be picked up by passport-facebook-token? – matthiasdv Jan 05 '14 at 03:09

1 Answers1

41

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.

Orangetronic
  • 300
  • 5
  • 10
  • Do you know if passport-facebook-token supports sessions (or similar strategy)? Or does it send a request to validate the access_token to Facebook servers on every request? – electronix384128 Jul 22 '14 at 12:29
  • It supports sessions. It's been a couple of months now since I set it up, but I believe the way I got everything working, you can set it up to store the validated access_token, checks against that first, then validates with Facebook if that doesn't match. If you *want* to validate with Facebook each time, you can turn sessions off. – Orangetronic Jul 30 '14 at 05:09
  • Hi Orangetronic. Do you know how the refreshToken works - I've looked at the documentation and it doesn't give much info on it. I want to request a new token when the old token has expired...any idea how this is done? Thanks! – jeh Feb 10 '15 at 01:04
  • Off the top of my head, the way I ended up handling expired auth tokens was by having the server prompt the client to request a new auth token ( `FB.login()` ) if the token was expired. It's been a good while since I've had my head in that stuff though… – Orangetronic Feb 11 '15 at 01:44
  • Shouldn't the '/my/api/:auth_token/endpoint' be '/my/api/:access_token/endpoint'? – smukov Apr 21 '15 at 09:08
  • @smukov, you're totally right! — I've edited the answer to reflect that correction, thankyou! – Orangetronic Apr 22 '15 at 10:27
  • @Orangetronic I have been using this middleware and found something strange. Even though I use wrong client id and client secret, I am able to get client details. More information on https://github.com/drudge/passport-facebook-token/issues/65 – rakesh kashyap Dec 10 '16 at 09:06
  • @rakeshkashyap huh — that's definitely not ideal! – Orangetronic Dec 11 '16 at 22:10
  • I am getting unauthorized, when making call from Postman. Link http://stackoverflow.com/q/43557408/1939163 – Luzan Baral Apr 28 '17 at 06:54
  • Isn't the better practice be to pass access token as a header instead of GET parameter? – Abhishek Saini Jan 21 '18 at 11:02
  • @AbhishekSaini probably would be! – Orangetronic Jan 22 '18 at 14:45