Search code examples
node.jsexpresspassport.js

Why can't I access req.user in passport.use()?


I'm writing a small e-commerce app with third-party login via Google using a PostgreSQL database. This is my Google Strategy configuration:

const GoogleStrategy = require("passport-google-oauth").OAuth2Strategy;
passport.use(new GoogleStrategy({
    clientID: process.env.GOOGLE_CLIENT_ID,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    callbackURL: `${process.env.NODE_ENV === "production" ? [production URL] : "http://localhost:8000"}/api/auth/login/google/callback`,
    passReqToCallback: true,
    scope: ["email", "profile"]
}, db.third.login));

And this is db.third.login below, which is working perfectly with both newly registered and existing users who authenticated themselves via Google. Alongside authenticating the user, it also passes information that ultimately forces a newly registered user to set a phone number and password in the client before they can use the app.

const login = async(req, accessToken, refreshToken, profile, done) => {
    // Get request IP address
    const ip = requestIP.getClientIp(req);

    // Generate login attempt ID
    const attemptId = idGen(15);

    try { // Get federated credentials
        let result = await pool.query("SELECT * FROM federated_credentials WHERE id = $1 AND provider = $2", [profile.id, profile.provider]);

        // Create user account if credentials don't exist
        if (result.rows.length === 0) {
            // Send error if email already exists in database
            result = await pool.query("SELECT email FROM users WHERE email = $1", [profile.emails[0].value]);
            if (result.rows.length > 0) return done({ status: 409, message: "Error: A user with the provided email already exists." });

            // Generate user ID and cart ID
            const userId = idGen(7);
            const cartId = idGen(7);

            // Generate password hash
            const salt = await bcrypt.genSalt(17);
            const passwordHash = await bcrypt.hash(process.env.GENERIC_PASSWORD, salt);

            // Add user to database
            let text = `INSERT INTO users (id, first_name, last_name, phone, email, password, created_at) VALUES ($1, $2, $3, $4, $5, $6, to_timestamp(${Date.now()} / 1000)) RETURNING id`;
            let values = [userId, profile.name.givenName, profile.name.familyName, "254700000000", profile.emails[0].value, passwordHash];
            result = await pool.query(text, values);

            // Add user cart to database
            result = await pool.query("INSERT INTO carts (id, user_id) VALUES ($1, $2)", [cartId, userId]);

            // Add federated credentials to database
            result = await pool.query("INSERT INTO federated_credentials (id, provider, user_id) VALUES ($1, $2, $3)", [profile.id, profile.provider, userId]);

            // Add user details to be confirmed to session
            const federatedCredentials = { id: profile.id, provider: profile.provider, confirm: true };
            return done(null, { id: userId, email: profile.emails[0].value, role: "customer", cartId: cartId, federatedCredentials });
        }

        // Save federated credentials details
        const federatedCredentials = { id: result.rows[0].id, provider: result.rows[0].provider, confirm: !result.rows[0].confirmed };

        // Get user details
        result = await pool.query("SELECT users.id AS id, users.email AS email, users.password AS password, users.role AS role, carts.id AS cart_id FROM users JOIN carts ON carts.user_id = users.id WHERE email = $1", [profile.emails[0].value]);

        // Log login attempt
        await loginAttempt(attemptId, ip, profile.emails[0].value, "google", true);

        // Add user to session
        return done(null, { id: result.rows[0].id, email: result.rows[0].email, role: result.rows[0].role, cartId: result.rows[0].cart_id, federatedCredentials });
    } catch (err) {
        return done({ status: 500, message: "An unknown error occurred. Kindly try again." });
    }
}

I'd now like to add functionality that allows existing users (who registered otherwise) to link their Google accounts, but my problem is that req.user is apparently not accessible in passport.use() (console logs show that it is undefined even after I've logged in). How can I access information on the current user in the session in passport.use() to make this happen? (Or is there another solution altogether?)


Solution

  • Did some digging on the Passport repository on GitHub and found a user who reported setting the sameSite property in the express-session cookie object to "lax" as a working solution, and it worked for me as well. You can read the comment here.

    const session = require("express-session");
    app.use(session({
        ... // other session options
        cookie: {
            ... // other cookie options
            sameSite: "lax" 
        }
    }));
    

    Hopefully Mr. Jared Hanson can provide an update on why this is the case soon (as well as a fix that allows unconditional access to req.user from passport.use()).