简体   繁体   中英

Passport.js, Sessions, & React | Accessing req.user from any route other than the /login route is undefined

I have a React client setup at localhost:3000 and a node.js server at localhost:5000

I'm trying a simple auth flow in which the client tries to authenticate with the server using Passport.js Google OAuth2.0 and staying authenticated using express-sessions with a MongoDB store.

I believe the reason I'm finding req.user is undefined is because of my lack of understanding of how the auth flow is supposed to work versus any issues with the actual code.

I'm initiating the auth flow through the following code in the react client:

<Button href="http://localhost:5000/auth/google">
        Login using Google
</Button>

The following is my auth.js file:

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

// @desc    Auth with Google
// @route   GET /auth/google
router.get("/google", passport.authenticate("google", { scope: ["profile"] }));

// @desc    Google auth callback
// @route   GET /auth/google/callback
router.get(
    "/google/callback",
    passport.authenticate("google", {
        failureRedirect: "/",
        successRedirect: "http://localhost:3000/dashboard",
    })
);

// @desc    Logout user
// @route   /auth/logout
router.get("/logout", (req, res) => {
    req.logout();
    res.redirect("/");
});

module.exports = router;

The following is my Google Strategy configuration:

const GoogleStrategy = require("passport-google-oauth20").Strategy;
const mongoose = require("mongoose");
const User = require("../models/User");

module.exports = function (passport) {
    passport.use(
        new GoogleStrategy(
            {
                clientID: process.env.GOOGLE_CLIENT_ID,
                clientSecret: process.env.GOOGLE_CLIENT_SECRET,
                callbackURL: "http://localhost:5000/auth/google/callback",
            },
            async (accessToken, refreshToken, profile, done) => {
                const newUser = {
                    googleId: profile.id,
                    displayName: profile.displayName,
                    firstName: profile.name.givenName,
                    lastName: profile.name.familyName,
                    image: profile.photos[0].value,
                };

                try {
                    let user = await User.findOne({ googleId: profile.id });

                    if (user) {
                        done(null, user);
                    } else {
                        user = await User.create(newUser);
                        done(null, user);
                    }
                } catch (err) {
                    console.error(err);
                }
            }
        )
    );

    passport.serializeUser((user, done) => {
        done(null, user.id);
    });

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

The following code is my index.js which brings everything together:

const express = require("express");
const cors = require("cors");
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
const connectDB = require("./config/db");
const morgan = require("morgan");
const passport = require("passport");
const session = require("express-session");
const MongoStore = require("connect-mongo")(session);

// Dotenv config
const dotenv = require("dotenv").config({
    path: "./config/config.env",
});

// Passport config
require("./config/passport")(passport);

// MongoDB config
connectDB();

const app = express();
const PORT = process.env.PORT;

// Middleware

app.use(cors());
app.use(bodyParser.json());
app.use(morgan("dev"));

// Sessions
app.use(
    session({
        secret: "stackoverflow",
        resave: false,
        saveUninitialized: false,
        store: new MongoStore({ mongooseConnection: mongoose.connection }),
    })
);

// Passport middleware
app.use(passport.initialize());
app.use(passport.session());

app.use("/posts", require("./routes/posts"));
app.use("/auth", require("./routes/auth"));

app.listen(PORT, () => console.log(`Server listening @ port ${PORT}`));

I am fetching posts from the DB through one of the routes after user login:

...
// @desc Get all posts
// @route GET /posts

router.get("/", (req, res) => {
    const posts = Post.find(function (error, posts) {
        if (error) return console.error(error);
        console.log(req.user) // <------ is undefined
        res.json(posts);
    });
});

I'm assuming that after the user is being redirected to the dashboard and then sending a request to the route to get all posts, the user is not authenticated? Although I redirected the user towards this route after authenticating him?

The goal is definitely not to fetch the user at the /posts route but create a separate /user route that returns the user to the client, but that also results in req.user being undefined.

Finally got it to work.

I had to edit two things,

First (Server Side):

I had to setup CORS to allow cookies to pass through as such:

app.use(
    cors({
         origin: "http://localhost:3000", // allow to server to accept request from different origin
         methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
         credentials: true, // allow session cookie from browser to pass through
   })
);

Second (Client Side):

I had to let Axios know that it has to send the cookie alongside the request as such:

axios
     .get("http://localhost:5000/auth/user", { withCredentials: true })
     .then(console.log)
     .catch(console.error);

where the /auth/user route is defined as such:

router.get("/user", (req, res) => {
    if (req.user) {
        res.json(req.user);
    }
});

These mistakes could have been avoided had I had a better understanding of the entire authentication process, of which now I do.

We live and we learn.

At first haidousm's answer did not work for me, what I did was I added { withCredentials: true } to the post request for my login as well.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM