Is this express-session authentication method secure - node.js

Im not sure if this express-session auth method is secure. I encrypt the password using bcryptjs and after encrypting I set the req.session.isLoggedIn = true in admin.js and later check it in events.js by using an if statement. Is the if statement method in events.js secure or somehow breachable? Is there an better option?
I'm using handlebars to render the web pages.
admin.js
bcrypt.compare(pass, user.password).then((doMatch) => {
console.log(doMatch);
//Check if password match
if (doMatch) {
//setting the isLoggedIn value
req.session.isLoggedIn = true;
//Events is the route that requires authentication
return res.redirect('/events');
} else {
res.redirect('/');
}
}).catch((err) => {
console.log(err);
});
events.js
Router.get('/', (req, res) => {
//Checking the if the loggedIn value is true
if (req.session.isLoggedIn) {
Event.find({}, (err, events) => {
res.render('events', {
prods: events,
pageTitle: 'Events',
path: '/events',
hasProducts: events.length > 0
});
}).catch((err) => {
console.log(err);
});
} else {
console.log('User not authenticated');
res.status(404).send({error: 'not authorized!'});
}
});

I don't think this is the best method you should take a look at : http://www.passportjs.org/
They have a good documentation about authentication strategies and registration also many other methods(Facebook login, Twitter...) also there is many tutorials on how you can implement Passport.js
Hope it helps you !

Related

Catching and handling a return code 403 from node API on client-side

How is it going?
Currently making my first experiences in developing with node, express, JWT and Login mechanism. I am also using EJS, but this is not part of this discussion.
There are hundreds of tutorials out there on the internet, all of them are good and have helped me so far.
But there is something missing crucially in all of these tutorials. I mean the error handling on the client side.
Maybe I am on the wrong way. But as of now, I could not find a way out.
JWT and login does work so far. Also does the verification of the JWT token.
What is not working is how to catch the 403 error that comes from the VerifyToken middleware. In that case, I would like to redirect the user to the login form again.
Better I give you some code (condensed to the most interesting parts):
server.js
const auth = require("./auth/AuthController.js");
server.use('/auth', auth);
server.use("/", VerifyToken, (req,res,next) => {
res.render("index", { page: "drivers", username: req.userName });
});
AuthController.js
const VerifyToken = require('./VerifyToken.js');
app.post("/login", (req,res,next) => {
conn.query("call sp_GetUserByName(?)", [req.body.username], function (err, row) {
if (err) {
res.status(500).send("DB Error: "+err)
} else {
if(row[0].length == 0){ res.status(200).send("nouser") }
else{
var dbpass = row[0][0].password;
bcrypt.compare(req.body.userpassword, dbpass, function(err, r){
if(err){
console.log("error: "+err);
res.status(500).send("pwcomperr")
}
else{
if(r){
const token = jwt.sign(
{ user_id: row[0][0].ID,
user_name: row[0][0].name },
process.env.jwtpass,
{
expiresIn: "6h",
}
);
return res
.cookie("access_token", token, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
})
.status(200)
.send("pwmatch");
}
else{
res.status(200).send("pwconflict")
}
}
});
}
}
});
});
VerifyToken.js
const jwtpass = process.env.jwtpass;
const VerifyToken = (req, res, next) => {
const token = req.cookies.access_token;
if (!token) {
return res.sendStatus(403);
}
try {
const data = jwt.verify(token, jwtpass);
req.userId = data.user_id;
req.userName = data.user_name;
return next();
} catch {
return res.sendStatus(403);
}
}
module.exports = VerifyToken
As you can see, I return a 403 if a user is not logged in (=no token present) or the token is timed out.
But with this code implementation, I get a 403 returned to the user's browser, saying "Forbidden" (which is correct, of course). Even if I want to access the root of the website.
I do not pass this request via Ajax or such. I only type the URL in the Browser: http://app-server.local:3000.
How is it possible to catch that error and to redirect to /auth/loginForm ?
I mean, I am surely not the first one coding that stuff - how do the others do it?

How to get data from passportjs validator in node js?

I'm using passport js for validate users. Here is my strategy.
passport.use(
new Strategy(opts, async ( payload , done) => {
try {
let user = await User.findById(payload.id);
if (!user) throw new Error("User not found");
if(user.role !== payload.role) throw new Error("Hacker");
return done(null, user.getUserInfo());
} catch (e) {
console.log(e);
done(null, false);
}
})
);
And if all OK, I'm returning a user.getUserInfo(). Now, here is my route:
router.get("url", passport.authenticate("jwt", { session: false }), async (req, res) => {
try {
} catch (e) {
console.log(e);
}
});
And now, when user pass the validator, how can I get this data from return done() statement, or it is unrealistic to take data from there. I'm a new in node js, so I don't know how to do that, or Is real to take data from passport.authenticate()?
In routes, Do:
{session: true}
and inside req.user, you will get the user data.
Otherwise, a work around can be to set user data in request just before done callback function.
req.userData = user.getUserInfo();

How to initiate session on signup with cookie-session and passport.js?

I have a /register router for signing up a user. I am using cookie-session (which is similar) instead of express-session for simplicity for now.
I am stuck on the part where I need to authenticate a user on sign up. I am confused about the functionality of req.sessions.save() and req.login(). I know req.login() is provided by passport.js, but I don't understand which one provides the req.session object.
I am new to passport.js and have read numerous articles, videos, and StackOverflow questions extensively to build up my knowledge. Honestly, the passport documentation has been quite a pain so far. I am still confused about how session initiation on signup should work. Many articles skipped the signup part. I thus request help on how to do it.
router.post('/register', (req, res, next) => {
console.log(req.body)
User.findOne({email: req.body.email}).then((currentUser) => {
if(currentUser){ // already exists
res.render('login')
} else { // if not, create user in our db
new User({
email: req.body.email
}).save().then((newUser) => {
passport.authenticate('local')(req, res, () => {
//>>>> //**This is where I don't know what to do**
req.session.save((err) => {
if (err) {
return next(err)
}
res.redirect('http://localhost:3000')
})
})
});
}
});
})
const express = require("express");
const router = express.Router();
const passport = require("passport");
router.post("/register", (req, res, next) => {
User.findOne({ email: req.body.email }).then((currentUser) => {
if (currentUser) { // already exists
res.render('login')
} else { // if not, create user in our db
new User({
email: req.body.email
}).save();
}
});
passport.authenticate("local", function (err, user, info) {
if (err) {
return res.status(400).json({ errors: err });
}
if (!user) {
return res.status(400).json({errors:"No user found."});
// or save User : new User({email: req.body.email}).save();
}
req.login(user, function (err) {
if (err) {
return res.status(400).json({ errors: err });
}
req.session.save((err) => {
if (err) {
return next(err)
}
res.redirect('http://localhost:3000')
});
return res.status(400).json({ success: `logged in ${user.id}` });
});
})(req, res, next);
});
module.exports = router;
passport.authenticate('local')(request, response, () => {
req.session.save((err) => {
if (err) {
return next(err)
}
res.redirect('http://localhost:3000')
})
}

Express redirect after login authentication

I'm working on an app which has node.js and express on the server, mongodb for the db and Backbone.js on the front-end. I'm working on enabling user logins, so I've used the passport.js library. I have a problem with my login 'post' method: It is not redirecting to another page (well it's an SPA, so I mean rendering a Backbone view). Here's the code:
//standard express setup...
app.post('/api/auth', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err) }
if (!user) {
req.session.messages = [info.message];
return res.redirect('/')
}
req.logIn(user, function(err) {
if (err) {
return next(err);
} else {
console.log('yup, working'); //Can see the response in the console
return res.redirect('/api');
}
});
})(req, res, next);
});
app.get('/api', function (request, response) {
response.send( 'Login successful!' );
});
So I can see the console.log message fine, and a GET request for the route IS triggered...but nothing actually happens. So I'm thinking that I've misunderstood how 'res.redirect' works - I want to navigate to that route upon the login success. I've thought about using window.location, but is this a good long-term solution? I'm not using any html templates on the server, so I can't (I don't think) do something as simple as 'res.render('index')'
What would be the best way to approach this? Thanks in advance.
I had a face-palm moment a few days after asking this question where I realized I had failed to grasp a core concept correctly. Let me answer my own question:
Rather than trying to serve the page from the server, I just need to send a response to the client and have Backbone do the rendering, depending on the response received. So my server does something more like this:
controller.post('/user', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err) }
user.save(function(err) {
if(err){
console.log(err);
return res.json(401);
} else {
return res.json(200); //SEND RESPONSE - do not try to redirect
}
});
Then I do the page loading in the Backbone View, like this:
login: function (e) {
e.preventDefault();
console.log('submitting login request');
var formValues = {
username: $('#inputEmail').val(),
password: $('#inputPassword').val()
};
var user = new User(); //user model
user.set(formValues);
function saveUserfunction (){
if(user) {
user.save(this.formValues, {
success: function(model, response, options) {
if (response == 200) {
console.log('success :' + response);
window.location.href = 'http://localhost:4711/#/api/menu_auth'; //Here's my redirect - the router is listening for this route and will render accordingly
} else {
console.log('error: '+response);
}
}, error: //Error handling etc.
As Pheonix pointed out in the comments, the best way to do this would be to listen to the the user model.

Restricting Login Access - Passport.js, Google Authentication

Okay, so using passport.js works, and works well, from what I've seen. However, I'm not sure how to properly exclude certain users. If the application is intended to have restricted access, rather than just providing the user a method for logging in, how can I restrict the login through passport.js? As it stands, users can just visit /login and log in with their Google account, thereby getting access to the internals.
Here is one way to do this, with comments throughout. The main thing is understanding this page from the author: http://passportjs.org/guide/authenticate/, which I explain a little more in this example ...
It might be easier to read bottom to top:
var authenticate = function(req, success, failure) {
// Use the Google strategy with passport.js, but with a custom callback.
// passport.authenticate returns Connect middleware that we will use below.
//
// For reference: http://passportjs.org/guide/authenticate/
return passport.authenticate('google',
// This is the 'custom callback' part
function (err, user, info) {
if (err) {
failure(err);
}
else if (!user) {
failure("Invalid login data");
}
else {
// Here, you can do what you want to control
// access. For example, you asked to deny users
// with a specific email address:
if (user.emails[0].value === "no#emails.com") {
failure("User not allowed");
}
else {
// req.login is added by the passport.initialize()
// middleware to manage login state. We need
// to call it directly, as we're overriding
// the default passport behavior.
req.login(user, function(err) {
if (err) {
failure(err);
}
success();
});
}
}
}
);
};
One idea is to wrap the above code in some more middleware, to make it easier to read:
// This defines what we send back to clients that want to authenticate
// with the system.
var authMiddleware = function(req, res, next) {
var success = function() {
res.send(200, "Login successul");
};
var failure = function(error) {
console.log(error);
res.send(401, "Unauthorized");
};
var middleware = authenticate(req, success, failure);
middleware(req, res, next);
};
// GET /auth/google/return
// Use custom middleware to handle the return from Google.
// The first /auth/google call can remain the same.
app.get('/auth/google/return', authMiddleware);
(This all assumes we're using Express.)
Try this.
googleLogin: function(req, res) {
passport.authenticate('google', { failureRedirect: '/login', scope: ['https://www.googleapis.com/auth/plus.login', 'https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/userinfo.email'] }, function(err, user) {
req.logIn(user, function(err) {
if (err) {
console.log(err);
res.view('500');
return;
}
var usrEmail = user['email'];
if(usrEmail.indexOf("#something.com") !== -1)
{
console.log('successful');
res.redirect('/');
return;
}
else
{
console.log('Invalid access');
req.logout();
res.view('403');
return;
}
});
})(req, res);
}
*

Resources