skip to Main Content

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 facebookgoogle 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 facebookgoogle).

     ... // 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.

3

Answers


  1. Chosen as BEST ANSWER

    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.


  2. A proper solution would be to implement the redirection on the client side.

    Simply use:

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

    If you’re client side receives the token, then redirect from there to home page, and in the case the login wasn’t successful, it would be redirected by the server directly.

    Or you can go for the full client side management as I would:

    app.get('/auth/facebook/callback',
      passport.authenticate('facebook', {
        session: false
      }), (req, res) => {
        if (req.user.jwtoken) {
          res.json({
            success: true,
            token: req.user.jwtoken
          })
        } else {
          res.json({
            success: false
          })
        }
      }
    )
    

    If success === true, store JWT in LocalStorage, else redirect to login page.

    Login or Signup to reply.
  3. Adding to Bar’s answer.

    I prepared a landing component to extract the cookie, save it to local storage, delete the cookie, then redirect to an authorized page.

    class SocialAuthRedirect extends Component {
      componentWillMount() {
        this.props.dispatch(
          fbAuthUser(getCookie("auth"), () => {
            document.cookie =
              "auth=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
            this.props.history.push("/profile");
          })
        );
      }
    
      render() {
        return <div />;
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search