2

I'm currently working on my first MERN project, with Passport Local, Google OAuth 2.0 and JWT. All my routes are protected with passport.authenticate("jwt", { session: false }), (req, res) => {}). I know Google Strategy relies on Express session and here's where my issue starts.

I don't know if this is the correct approach, but I want to generate a JWT token from Google OAuth login to handle the user session with it and keep all my routes requests working properly. From the past issues opened here I've found this one to be the most recent and practical solution, but it seemed to me that it would introduce a new problem - since the local login already generates the JWT token, wouldn't it conflict with the request on the step 4 from the linked solution? If I'm thinking right, the token would be generated two times when using the standard login - one right at the authentication and another when reaching the app's home page, not to mention that it looks like the request to /auth/login/success will be made every time the home page is reached, which doesn't seem right to me.

So, what would be the best and cleaner way to approach this? I'd also like to validate everything on the server side and avoid appending the token to the URL, if possible. Anyway, here is my code:

passport.js

const GoogleStrategy = require("passport-google-oauth20").Strategy;
const JwtStrategy = require("passport-jwt").Strategy;
const User = require("../models/user.model");

require("dotenv").config();

module.exports = function (passport) {
  // CONFIGURE STRATEGIES

  // Local
  passport.use(User.createStrategy());

  // Google
  passport.use(
    new GoogleStrategy(
      {
        clientID: process.env.GOOGLE_CLIENT_ID,
        clientSecret: process.env.GOOGLE_CLIENT_SECRET,
        callbackURL: "http://localhost:5000/auth/google/movie-log",
      },
      function (accessToken, refreshToken, profile, cb) {
        User.findOrCreate(
          { googleId: profile.id },
          { first_name: profile.displayName, email: profile._json.email },
          function (err, user) {
            return cb(err, user);
          }
        );
      }
    )
  );

  // JWT
  const cookieExtractor = (req) => {
    let token = null;
    if (req && req.cookies) {
      token = req.cookies["access_token"];
    }

    return token;
  };

  passport.use(
    new JwtStrategy(
      {
        jwtFromRequest: cookieExtractor,
        secretOrKey: process.env.SECRET,
      },
      (payload, done) => {
        User.findById(payload.sub, (err, user) => {
          if (err) {
            return done(err, false);
          }
          if (user) {
            return done(null, user);
          } else {
            return done(null, false);
          }
        });
      }
    )
  );

  // CONFIGURE AUTHENTICATED SESSION PERSISTENCE
  passport.serializeUser(function (user, done) {
    done(null, user.id);
  });

  passport.deserializeUser(function (id, done) {
    User.findById(id, function (err, user) {
      done(err, user);
    });
  });
};

auth.js (for Google authentication)

const express = require("express");
const passport = require("passport");
const router = express.Router();

const login = "http://localhost:3000/login";
const diary = "http://localhost:3000/diary";

router.get(
  "/google",
  passport.authenticate("google", {
    scope: ["email", "profile"],
  })
);

router.get(
  "/google/movie-log",
  passport.authenticate("google", {
    successRedirect: diary,
    failureRedirect: login,
  })
);

module.exports = router;

login.js (for local authentication)

const express = require("express");
const passport = require("passport");
const JWT = require("jsonwebtoken");
const router = express.Router();

const signToken = (userID) => {
  return JWT.sign(
    {
      iss: "Movie.log",
      sub: userID,
    },
    process.env.SECRET,
    { expiresIn: "1h" }
  );
};

router
  .route("/")
  .post(passport.authenticate("local", { session: false }), (req, res) => {
    if (req.isAuthenticated()) {
      const { _id, first_name } = req.user;

      const token = signToken(_id);
      res.cookie("access_token", token, { httpOnly: true, sameSite: true });

      res.status(200).json({
        isAuthenticated: true,
        user: first_name
      });
    }
  });

module.exports = router;
Alex Braga
  • 495
  • 1
  • 6
  • 19

0 Answers0