req.user is unidentified in session (Node, express, session, passport) Ask Question

For some reason req.user is undefined, and after 4+ hours of trying to figure out why, I’m asking here. I even copy-pasted the server/index.js file of a friend’s server, changed the auth strategy so it worked for mine, and I get the same issue.

Everything else is working. It redirects to auth0, comes back to the correct place, either creates a new user in the DB or finds the user. In passport.serializeUser it has all the data I passed along. But when I hit the ‘/auth/me’ endpoint, req.user is undefined.

server/index.js

require('dotenv').config();
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors')
const session = require("express-session");
const passport = require('passport');
const Auth0Strategy = require('passport-auth0');
const massive = require('massive');
const axios = require('axios');
const process = require("process");
const moment = require('moment');

const app = express();

//app.use(express.static(__dirname + './../build'));
app.use(bodyParser.json());
app.use(cors());

app.use(session({
    secret: process.env.SECRET, 
    cookie: { maxAge: 60000 },
    resave: false,
    saveUninitialized: true
   }));
app.use(passport.initialize());
app.use(passport.session()); 

// Use the session middleware
massive(process.env.CONNECTION_STRING)
.then( (db) => {
    console.log('Connected to Heroku')
    app.set('db', db);
}).catch(err=>console.log(err))
 


passport.use(new Auth0Strategy({
    domain: process.env.AUTH_DOMAIN,
    clientID: process.env.AUTH_CLIENT_ID,
    clientSecret: process.env.AUTH_CLIENT_SECRET,
    callbackURL: process.env.AUTH_CALLBACK
}, (accessToken, refreshToken, extraParams, profile, done) => {
    const db = app.get("db");
    const userData = profile._json;

    db.find_user([userData.identities[0].user_id]).then(user => {
    if (user[0]) {
        return done(null, user[0]);
    } else {
        db.create_user([
            userData.given_name,
            userData.family_name,
            userData.email,
            userData.identities[0].user_id
        ])
        .then(user => {
            return done(null, user);
        });
    }
    });
}))

passport.serializeUser( (user, done) => {
    //console.log('serializeuser', user)
    done(null, user);
}) 

passport.deserializeUser( (id, done) => {
    app.get("db").find_session_user([id])
        .then(user => {
        console.log(user);
        done(null, user[0]);
        });
})

app.get('/auth', passport.authenticate('auth0'));
app.get('/auth/callback', passport.authenticate('auth0', {
    successRedirect: process.env.SUCCESS_REDIRECT
}))

app.get('/auth/me', (req, res) => {
    console.log('auth/me endpoint hit')
    console.log(req.user)
    if(!req.user){
        return res.status(401).send('No user logged in.');
    }
    return res.status(200).send(req.user);
})

app.listen(process.env.PORT, () => console.log(`Listening on port: ${process.env.PORT}`));

Doing Away With Passport – Manually Verifying A JWT Token (node/express/graphQL) Ask Question

I’ve started to feel like Passport is a bit bloated for me. I use JWT tokens for authorizations and was finding myself writing multiple JWT strategies for slightly different token use cases (e.g. what a token was allowed to access in different routes).

This is my new lightweight approach, and I was wondering if it’s sound:

  1. When a user creates an account (or changes their password), the password is hashed with bcrypt.hash(user.password, 10)
  2. When a user tries to sign in, their password is checked with bcrypt.compare(args.password, user.password, (err, isMatch) => {...
  3. If the password is a match, a JWT token is generated with the following (using npm jsonwebtoken):

    jwt.sign({
    username: user.username,
    }, process.env.SECRET_KEY);

  4. Now, when the user accesses any route, they will pass this token in the authorization header.

  5. The route they access may look like this:

    router.route('/stuff')
    .post(checkToken, doStuff);

  6. checkToken() looks in the authorization header for the token, takes the username that is in the token, and puts it in the request object.

    const decoded = jwt.verify(token, process.env.SECRET_KEY);
    req.authorizedUsername = decoded.username

  7. Later in doStuff(), we will check the authorized username before returning certain information.

The key is that they not authorized straight away, but I have the flexibility to check that token’s username is allowed to access given data in later logic, without having to predefine given checks through passport strategies. (I found I was having to make different ones for “edit own profile”, “access own profile”, “access friends profile”, “access public profile” etc when I would rather handle this in the business logic).

Thanks!

How do I not link a twitter/google account to a new user with passportjs if there's already an existing user with the same twitter/google account? Ask Question

Here’s my scenario:

I am working on user authentication with different strategies like google, twitter using passportjs, wherein a user could login with either his google account or his twitter account.

How do I prevent a user who has already been authenticated with both google and twitter from using a second google account this time around and linking the same twitter account which he used for his first account or the other way around?

This is possible because, I configured my google strategy to always prompt for authorization instead of automatically logging the user in.

With this enabled, a user could choose to use a different account and once logged in, he could link his twitter account.

I made sure that when a previously authenticated user decides to use a different google account to log into the app, a new user document is created in my database.

My problem is that when this newly created user decides to link the same twitter account which he used for his first google account, the new user document is getting updated with the same twitter account.

So now I have two different users with the same twitter Id’s linked to each of their accounts.

Ideally, I would want to flash a message to the user telling him something along the lines of ‘This twitter account is already linked to a different account’ and then redirect him to his profile page or something.

This scenario could unfold the other way around too because I also have google OAuth implemented too.

Also, I feel that I have too many if statements and that there is a better way of doing all these checks in my passport.js file.

This is what I have done so far:

This is my passport.js file where all of the different strategies are set up

const passport = require("passport");
const GoogleStrategy = require("passport-google-oauth").OAuth2Strategy;
const TwitterStrategy = require("passport-twitter").Strategy;
const mongoose = require("mongoose");

const keys = require("../config/keys");

const User = mongoose.model("users");

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

passport.deserializeUser(async (id, done) => {
  const user = await User.findById(id);
  done(null, user);
});

passport.use(
  new GoogleStrategy(
    {
      clientID: keys.googleClientID,
      clientSecret: keys.googleClientSecret,
      callbackURL: "/auth/google/callback",
      proxy: true,
      passReqToCallback: true
    },
    async (req, accessToken, refreshToken, profile, done) => {
      console.log("req", req.session);
      console.log("req", req.user);
      console.log("req", profile.id, profile.displayName);
      console.log("*-------------------------------*");

      // Not logged-in currently with any other social account.
      if (!req.user) {
        let existingGoogleUser = await User.findOne({ googleId: profile.id });

        //check if there is an existing user already with that social account Id.
        //like (twitterId, googleId etc)
        if (existingGoogleUser) {
          return done(null, existingGoogleUser);
        }

        //if there is no existing user with that social account Id
        //create a new user with that social account Id
        let user = await new User({ googleId: profile.id }).save();
        return done(null, user);
      }

      // Logged in currently with some other social account

      //Only update the user document if the current authenticated user 
      //has not linked his google account already.
      if (!req.user.googleId) {
        //if there is a google account with the same id, return the authenticated user.
        if(await User.findOne({ googleId: profile.id })) {
          return done(null, req.user);
        }
        let existingUser = await User.findByIdAndUpdate(
          req.user.id,
          { googleId: profile.id },
          { new: true }
        );
        return done(null, existingUser);
      } else {
        // If the user has already linked his google account and tries
        // to login from the same computer with a different account
        // then create a new user document
        if (req.user.googleId !== profile.id) {
          let user = await new User({ googleId: profile.id }).save();
          return done(null, user);
        }

        // If the user logs in with the same google account again
        // Continue with the passport authentication flow and log him in.
        done(null, req.user);
      }
    }
  )
);

passport.use(
  new TwitterStrategy(
    {
      consumerKey: keys.twitterConsumerKey,
      consumerSecret: keys.twitterConsumerSecret,
      callbackURL: "/auth/twitter/callback",
      proxy: true,
      passReqToCallback: true
    },
    async (req, token, tokenSecret, profile, done) => {
      console.log("req", req.session);
      console.log("req", req.user);
      console.log("req", profile.id, profile.displayName);
      console.log("*-------------------------------*");

      // Not logged-in currently with any other social account.
      if (!req.user) {
        let existingTwitterUser = await User.findOne({ twitterId: profile.id });


        if (existingTwitterUser) {
          return done(null, existingTwitterUser);
        }

        //if there is no existing user with that social account Id
        //create a new user with that social account Id
        let user = await new User({ twitterId: profile.id }).save();
        return done(null, user);
      }

      // Logged in currently with some other social account

      //Only update the user document if the current authenticated user 
      //has not linked his twitter account already.
      if (!req.user.twitterId) {
        //if there is a twitter account with the same id, return the authenticated user.
        if(await User.findOne({ twitterId: profile.id })) {
          return done(null, req.user);
        }

        let existingUser = await User.findByIdAndUpdate(
          req.user.id,
          { twitterId: profile.id },
          { new: true }
        );
        return done(null, existingUser);
      } else {
        // If the user has already linked his twitter account and tries
        // to login from the same computer with a different account
        // then create a new user document
        if (req.user.twitterId !== profile.id) {
          let user = await new User({ twitterId: profile.id }).save();
          return done(null, user);
        }

        // If the user logs in with the same twitter account again
        // Continue with the passport authentication flow and log him in.
        return done(null, req.user);
      }
    }
  )
);

This is my authRoutes.js file where all the authentication routes are setup.

const passport = require("passport");

module.exports = app => {
  //google auth
  app.get(
    "/auth/google",
    passport.authenticate("google", {
      scope: ["profile", "email"],
      prompt: "select_account"
    })
  );

  app.get("/auth/google/callback", passport.authenticate("google"));

  //twitter auth
  app.get("/auth/twitter", passport.authenticate("twitter"));
  app.get("/auth/twitter/callback", passport.authenticate('twitter'));

  app.get("/api/logout", (req, res) => {
    req.logout();

    res.send(req.user);
  });

  app.get("/api/current_user", (req, res) => {
    res.send(req.user);
  });
};

This is my User.js file with the mongodB model.

const mongoose = require("mongoose");
const { Schema } = mongoose;

const userSchema = new Schema({
  googleId: {
    type: String,
  },
  twitterId: {
    type: String,
  }
});

mongoose.model("users", userSchema);

This is my main index.js file

const express = require("express");
const mongoose = require("mongoose");
const cookieSession = require("cookie-session");
const passport = require("passport");

require("./models/User");
require("./services/passport");
const keys = require("./config/keys");

mongoose.connect(keys.mongoURI);

const app = express();

app.use(
  cookieSession({
    maxAge: 30 * 24 * 60 * 60 * 1000,
    keys: [keys.cookieKey]
  })
);

app.use(passport.initialize());
app.use(passport.session());

require("./routes/authRoutes")(app);

const PORT = process.env.PORT || 5000;

app.listen(PORT, () => {
  console.log(`Started Server on ${PORT}`);
});

PS: I apologize for the long post. I am a beginner and this is my first question here on stackOverFlow. Any help regarding handling errors in the above code along with any other improvements would be greatly appreciated. Thank you!!

Authentication using Token , passport jwt , Express Ask Question

I’m working on this little project and i’m having difficulties , Everything works fine in my api except that when i want to authenticate a user using a token , it says “unauthorized”. any help would be much appreciated.
this is my passport.js file :

       const jwtStrategy = require('passport-jwt').Strategy;
       const extractJwt = require('passport-jwt').ExtractJwt;
       const User = require('../models/user');
       const config = require('../config/database');

         module.exports = function(passport){
          let opts = {};
       opts.jwtFromRequest = 
       extractJwt.fromAuthHeaderWithScheme('jwt');
       opts.secretOrKey = config.secret;
       passport.use(new jwtStrategy(opts, function(jwt_payload, done) 
       {
       User.getUserById(jwt_payload.data._id, function(err, user) {
        if (err) {
            return done(err, false);
        }
        if (user) {
            return done(null, user);
        } else {
            return done(null, false);
        }
    });
}));

}

And this is my user.js file (i think that the fault is in the getUserById function :

       const mongoose = require('mongoose');
       const bcrypt = require('bcryptjs');
       const config = require('../config/database');
      //User schema
      const userSchema = mongoose.Schema({
      name: {
     type: String
     },
      email: {
         type: String,
         required: true
       },
      username: {
        type: String,
        required: true
         },
         password: {
           type: String,
           required: true,
       }

     });

    const User = module.exports = mongoose.model('User', userSchema);

   module.exports.getUserById = function(id, callback){
     User.findById(id, callback);
    }
   module.exports.getUserByUsername = function(username, callback){
   const query = {username: username}
   User.findOne(query, callback);
   }
    module.exports.addUser = function(newUser, callback){
    bcrypt.genSalt(10, function(err, salt){
    bcrypt.hash(newUser.password,salt,function(err, hash){
        if(err) throw err;
        newUser.password= hash;
        newUser.save(callback);
       });
     });
  }
    module.exports.comparePassword = function (candidatePassword, 
    hash, callback){
     bcrypt.compare(candidatePassword, hash, function(err, isMatch){
    if(err) throw err;
    callback(null, isMatch);
    });
    }

Reloading page with another HTML using Node? Ask Question

I’m trying to load a new HTML page after the user logs in.

My login is currently:

app.post('/login', passport.authenticate(
  'local', {
     session:false,
}), getToken); // callback function

This returns a token to the client, which is used to call /mainPage.

app.get('/mainPage', (req, res) => {
  console.log("/mainPage reached");
  res.sendFile(__dirname + '/mainpage.html');
})

I’m not getting any errors, and I’m getting /mainPage reached in my console, but it’s not reloading the page. I’m assuming it’s because there is already another HTML page present. How do I load a new page?

I’ve tried using successRedirect in passport.authenticate, but I don’t think it works since I have another function that is called after it.

How to combine passport-jwt with ACL(access control list) pattern Ask Question

I am using passport-jwt. And I’m trying to implement an acl pattern in my system . There are some different roles in my system. And I want to control their access to some resource.

I think the common way to do that in token based authentication is to write a middleware for express.js which will validate the token and add a ‘role’ field to ‘req.user’. And then mount another middleware to every route which will specify which roles can access them.
So my question is , How to combine this approach with passport-jwt .

plus:
The common way of using passport-jwt strategy :

  app.get('/myapi', passport.authenticate('jwt', {session: false}),
    function(req, res, next){ ...})

The common way I think in token based authentication:

app.use(function(req, res, next){
  token = extractToken(req)
  jwt.verify(token, key, function(err, decoded) {
    if (err) {
      return res.send(err);
    } else {
      users.findOne({userId: decoded.userId}, function(err, user){
        if(!user) res.send('unknown')
        else{
          req.user = user //in which include a role field.
                      // for example: user.role = 'user' | 'manager'
        }
      })
    }
  });
})

and at every route:

function checkRole(roles){
  return function (req, res, next) {
    if (req.session.user && roles.includes(req.user.role)) {
        next();
    } else {
        res.send(403);
    }
  }
}

app.get('/myapi', checkRole(['user']), function(req, res){
  ....
})

Different session data for multiple passport js strategy Ask Question

I have implemented a JWT based passport strategy to protect my express API to authenticated users only. Also, now I need to give an option to the users to connect with Linkedin and add more details to their profile. I am using passport-linkedin-oauth2 to connect with LinkedIn.

I am getting all the required info from the users’ linkedin profile as mentioned in the above link. But the issue I am facing is that the, /auth/linkedin and the /auth/linkedin/callback routes are to be protected to be available to only authenticated users. I have implemented the same as given below, but the issue is that the session i.e. user data from the JWT strategy is being lost due to the linkedin strategy, even though I have instantiated two passport objects.

user.js route file:

const pass = require('passport').Passport;
const router = express.Router();

const passport = new pass();
const linkedinPassport = new pass();

router.use(passport.initialize({userProperty: "access"}));
router.use(passport.session());
require('../config/passport')(passport);

router.use(linkedinPassport.initialize({userProperty: "linkedin"}));
router.use(linkedinPassport.session());
require('../config/linkedin')(linkedinPassport);

passport.serializeUser((user, done) => { console.log("Serializing ID: " + user._id);  done(null, user); });

linkedinPassport.serializeUser((user, done) => { console.log("L: " + user);  done(null, user)});

router.get('/auth/linkedin', passport.authenticate('jwt', {session: true}), linkedinPassport.authenticate('linkedin', {session: true}));

router.get('/auth/linkedin/callback', passport.authenticate('jwt', {session: true}), linkedinPassport.authenticate('linkedin', {session: true}), (req, res) => {

console.log(req.access);
console.log(req.linkedin);


});

module.exports = router;

getting form data from POST using express.js with passport.js Ask Question

so i built a chatroom and everything worked fine up until i changed the system over to use just uid heres a little rundown of how it works

when the user signs up he gets assigned a uid thats put in the data base along with his credentials so everytime the user logs in it asks the server to get the uid thats associated with the username you typed in on the text box and sets it as a cookie like this

setCookies() gets called on submit

function setCookies() {
    var username = document.getElementById("login-username").value
    console.log(username)
    getLoggedInUserInfo(username)
}
function getLoggedInUserInfo(username){     
console.log(username)
socket.emit('getLoggedInUserInfo',username)
}
socket.on('loggedInUserInfo',function(uid){
    document.cookie = "uid=" + uid;
})

but this is a problem on the signup page because the user doesnt have anything about him on the database yet

so what i was thinking what if i could set the cookie on the server side using express but i cant get any values from the form using express like this

app.post('/apolloLogin', passport.authenticate('local-login', {
    successRedirect: '/apolloMainchat', // redirect to the secure profile section
    failureRedirect: '/apolloLogin', // redirect back to the signup page if there is an error
    failureFlash: true // allow flash messages
},function(req, res){
   var username = res.query.username

    console.log("tettestset")
    //res.cookie('cookieName','randomUid', { maxAge: 900000, httpOnly: true });
}),

Here i would get the uid associated with the username
And then set the uid cookie like this

res.cookie('cookieName','randomUid', { maxAge: 900000, httpOnly: true });

but i always get null for the username value and it says res.cookie is not a function

what am i doing wrong here with express

EDIT

now i am trying this

app.post('/apolloSignup', passport.authenticate('local-signup', {
    successRedirect: '/apolloMainchat', // redirect to the secure profile section
    failureRedirect: '/apolloSignup', // redirect back to the signup page if there is an error
    failureFlash: true // allow flash messages
},function(req, res){
    console.log(req.body.username)
}));

SECOND EDIT

this is what i am using now and i am getting null from the console.log

app.post('/apolloSignup', passport.authenticate('local-signup', {successRedirect: '/apolloMainchat',failureRedirect: '/apolloSignup',failureFlash: true}),function(req, res){
    console.log(req)
});

THIS IS WHAT MY HTML LOOKS LIKET

<html>
    <head>
            <link type="text/css" rel="stylesheet" href="css/regular.css" />
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <script src="/socket.io/socket.io.js"></script>
            <script src="scripts/jquery-1.11.1.js"></script>  
            <script src="scripts/bic_core.js"></script> 
    </head>
    <body>
        <div class="login-register-div" id="login-register-div">
                            <!-- show any messages that come back with authentication -->
                            <% if (message.length > 0) { %>
                                <div class="login-error">
                                    <script>
                                        $("#login-username").addClass("textboxError");
                                        $("#login-password").addClass("textboxError");
                                        </script>
                                    <%= message %>
                                </div>
                                <% } %>
                                    <!-- LOGIN FORM -->
            <div id="login-container" class="login-container">
                <form action="/apolloSignup" method="post">
                    <center><h1>Sign Up</h1></center>
                    <input type="text" placeholder="Username" class="login-username" id="login-username" name="username">
                    <input type="password" placeholder="Password" class="login-password" id="login-password" name="password">
                    <input type="submit" value="Signup" class="login-submit" id="login-submit" onclick="setCookies()">
                    <a class="signup-login-link" href="/apolloLogin">Log In</a>
                </form>
            </div>
        </div>
    </body>
</html>
<script>

passport, mongo, express, shopping cart tutorial lags on click submit after a post request Ask Question

I use passport authentication, after submit, and passing all validations, on successRedirect, the application does not redirect to the specified route and instead just loads and after a while it crashes. I am following this youtube tutorial by Max S. and here, I will load my passport.js my index.js and my app.js that has express imports.

HERES PASSSPORT.JS

var passport = require("passport");
var User = require("../models/user");
var LocalStrategy = require("passport-local").Strategy; //chaining the object `Strategy`

//this function tells passport how to store the user in the session
passport.serializeUser(function(user, done) {
  done(null, user.id); //whenever you want to store user in your session, serialize it by id (hence `user.id`)
});

passport.deserializeUser(function(id, done) {
  //use mongo method to find by id
  User.findById(id, function(err, user) {
    done(err, user); //when using `done`, return err (first) if unsuccessful or the user if successful
  });
});

//----- were not creating a new user yet...

//below we create a new user with a local strategy
//`use` is a passport method that takes the first string argument as the name of the local strategy (below: `local-signup`), and new LocalStrategy takes two argument 1. an object configuration and 2. a call back function
passport.use(
  "local-signup",
  new LocalStrategy(
    {
      //object configuration
      usernameField: "email", //tell passport that usernameField is email
      passwordField: "password", //tell passport that passwordField is password
      passReqToCallback: true //which means that in the callback function below you can access and use the (request, email, password and done)
    },
    function(req, email, password, done) {
      //the call back function
      //check for validations here, before running the query to database.
      req
        .checkBody("email", "invalid email")
        .notEmpty()
        .isEmail();
      req
        .checkBody("password", "invalid password")
        .notEmpty()
        .isLength({
          min: 4
        });
      var errors = req.validationErrors();
      console.log("req.validationErrors() 44 >>>> ", errors);
      if (errors) {
        var messages = [];
        errors.forEach(function(errors) {
          messages.push(errors.value + "" + errors.msg);
          console.log("messages here >>>>> ", messages)
        });
        return done(null, false, req.flash("error", messages));
      }
        //use mongo method to find one (which is email)
        User.findOne({
          'email': email,
          function(err, user) {
            //equal to the second argument passed in the call back function
            if (err) {
              //check1
              console.log("ERROR HERE passport 59 >>>>>>");
              return done(err);
            }
            if (user) {
              console.log("USER HERE >>>>>>>");
              //in the next line of code:
              //null -> means no error but also
              //false -> means the process is unsuccessful
              //message -> tells user email is already taken
              return done(null, false, {
                messages: "Email is already in use!"
              }); //check2
            }
            //after passing both checks above, we can create a NEW USER
            var newUser = new User();
            newUser.email = email;
            newUser.password = newUser.encryptPassword(password); //in user.js (under model folder, we implement bcrypt-nodejs hashing capability)
            newUser.save(function(err, result) {
              //we will save the newUser
              if (err) {
                //check1
                return done(err);
              } else {
                return done(null, newUser);
              }
            });
          }
        });
      
    }
  )
);