Facebook-passport with JWT

Bar Kedem picture Bar Kedem · Feb 28, 2017 · Viewed 7.3k times · Source

I've been using Passport on my server for user authentication. When a user is signing in locally (using a username and password), the server sends them a JWT which is stored in localstorage, and is sent back to server for every api call that requires user authentication.

Now I want to support Facebook and Google login as well. Since I began with Passport I thought it would be best to continue with Passport strategies, using passport-facebook and passport-google-oauth.

I'll refer to Facebook, but both strategies behave the same. They both require redirection to a server route ('/auth/facebook' and '/auth/facebook/callback' for that matter). The process is successful to the point of saving users including their facebook\google ids and tokens on the DB.

When the user is created on the server, a JWT is created (without any reliance on the token received from facebook\google).

     ... // Passport facebook startegy
     var newUser = new User();
     newUser.facebook = {};
     newUser.facebook.id = profile.id; 
     newUser.facebook.token = token; // token received from facebook
     newUser.facebook.name  = profile.displayName;   
     newUser.save(function(err) {
          if (err)
               throw err;
          // if successful, return the new user
          newUser.jwtoken = newUser.generateJwt(); // JWT CREATION!
          return done(null, newUser);
     });

The problem is that after its creation, I don't find a proper way to send the JWT to the client, since I should also redirect to my app.

app.get('/auth/facebook/callback',
    passport.authenticate('facebook', {
        session: false,
        successRedirect : '/',
        failureRedirect : '/'
    }), (req, res) => {
        var token = req.user.jwtoken;
        res.json({token: token});
    });

The code above redirects me to my app main page, but I don't get the token. If I remove the successRedirect, I do get the token, but I'm not redirected to my app.

Any solution for that? Is my approach wrong? Any suggestions will do.

Answer

Bar Kedem picture Bar Kedem · Feb 18, 2018

The best solution I found for that problem would be to redirect to the expected page with a cookie which holds the JWT.

Using res.json would only send a json response and would not redirect. That's why the other suggested answer here would not solve the problem I encountered.

So my solution would be:

app.get('/auth/facebook/callback',
passport.authenticate('facebook', {
    session: false,
    successRedirect : '/',
    failureRedirect : '/'
}), (req, res) => {
    var token = req.user.jwtoken;
    res.cookie('auth', token); // Choose whatever name you'd like for that cookie, 
    res.redirect('http://localhost:3000'); // OR whatever page you want to redirect to with that cookie
});

After redirection, you can read the cookie safely and use that JWT as expected. (you can actually read the cookie on every page load, to check if a user is logged in)

As I mentioned before, it is possible to redirect with the JWT as a query param, but it's very unsafe. Using a cookie is safer, and there are still security solutions you can use to make it even safer, unlike a query param which is plainly unsecure.