how to create session using node.js? - node.js

I want to create session for my pages, when give a url like http://localhost:3000/pages/profile it will goes to that page without logged in. What should i do now to fix this problem.
node.js
module.exports = function(app, express, passport){
var router = express.Router();
passport.use(new LocalStrategy({
usernameField: 'username',
passwordField: 'password'},
function(username, password, done) {
User.findOne({ name : username}, function(err, user) {
if (!user){
return done(null, false,{message: 'Incorrect username' });
}
if(user){
var validPassword = user.comparePassword(password);
if(!validPassword){
return done(null, false,{message: 'Incorrect password' });
}
}
return done(null, user);
});
}
));
router.post('/pages/auth/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (user === false) {
console.log("login error ");
return res.json({
success:false,
message: info.message,
});
} else {
console.log("login success");
return res.json({
success:true,
//message: 'Login Success',
});
}
})(req, res, next);
});
}
controller
function LoginController($http, $location, Auth, $rootScope)
{
var vm = this;
vm.submitPost = function(userData){
$http({
url: 'http://localhost:7200/api/pages/auth/login',
method: 'POST',
data: userData
}).then(function(res) {
if(res.data.success){
$location.path('/pages/profile');
} else {
vm.message=res.data.message;
$location.path('/pages/auth/login');
}
}, function(error) {
console.log(error);
alert(error.data);
});
};
}
login.html
<form name="loginForm">
<div class="alertmessage" >{{vm.message}}</div>
<md-input-container flex md-no-float>
<input ng-model="vm.form.username" placeholder="Username" translate
translate-attr-placeholder="LOGIN.USERNAME" name="username" required="true">
<div ng-messages="loginForm.username.$error" ng-show="loginForm.username.$touched">
<div ng-message="required">This field is required</div>
</div>
</md-input-container>
<md-input-container flex md-no-float>
<input ng-model="vm.form.password" type="password" placeholder="Password" translate
translate-attr-placeholder="LOGIN.PASSWORD" name="password" required="true">
<div ng-messages="loginForm.password.$error" ng-show="loginForm.password.$touched">
<div ng-message="required">This field is required</div>
</div>
</md-input-container>
<div class="remember-forgot-password" layout="row" layout-sm="column"
layout-align="space-between center">
<md-checkbox class="remember-me" ng-model="data.cb1" aria-label="Remember Me">
<span translate="LOGIN.REMEMBER_ME">Remember Me</span>
</md-checkbox>
<a ui-sref="app.pages_auth_forgot-password" class="forgot-password md-accent-color"
translate="LOGIN.FORGOT_PASSWORD">Forgot Password?</a>
</div>
<md-button class="md-raised md-accent" aria-label="LOG IN" translate="LOGIN.LOG_IN"
translate-attr-aria-label="LOGIN.LOG_IN"
ng-click="vm.submitPost(vm.form);">
LOG IN
</md-button>
</form>

I have a Node.js project with sessions and in my index.js I have the following:
var session = require('express-session');
var MongoStore = require('connect-mongo')(session);
app.use(session({
secret: config('session_secret'),
store: new MongoStore({ mongooseConnection: mongoose.connection }),
resave: true,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
If you don't use MongoDB you can drop the "store" element. Then it uses the default store which is MemoryStore.
To prevent non-authenticated users accessing a page you can do this:
router.get('/secure-page', isLoggedIn, function(req, res) {
res.json({secure: "page"});
});
function isLoggedIn(req, res, next) {
// if user is authenticated in the session, carry on
if (req.isAuthenticated()) {
return next();
}
else {
// redirect to login page.
res.redirect('/login');
}
}

One way is storage values the session with express session, and then interceptade each route with middleware as verify if user is logged or not, somelike this...
Middleware autentic:
module.exports = function(req, res, next) {
if(!req.session.user) {
return res.redirect('/');
}
return next();
};
req.session.user is a variable create in session in login controller for storage username.
And intercept the route, verifying with user is logged:
...
app.get('pages/profile', autentic, controller.function);
...
If user is not logged will redirect to home page.
But, I suggest you to use passport.js:
Passport is authentication middleware for Node.js. Extremely flexible
and modular, Passport can be unobtrusively dropped in to any
Express-based web application. A comprehensive set of strategies
support authentication using a username and password, Facebook,
Twitter, and more.
See the docs for learn how to use and search here in the stack overflow too.

Related

NodeJS csrf protection gives ForbiddenError invalid csrf token only on one POST request form

I have been unsuccessfully trying to find a solution on Google for the past several hours for the following issue:
I have csurf set up and working well. It works for POST requests related to signing up/in users. I am trying to create a form in the user profile, that updates the user's data, but when I hit submit, I get ForbiddenError: invalid csrf token.
On further testing, the csrt token is created on the profile page, but for some reason, it is invalid.
(I have only recently picked up programming, I'm willing to provide additional information if I have missed something.)
Tools used:
Node.js v10.16.3
Express v4.17.1
ejs v2.7.1
csurf v1.10.0
Express session v1.16.2
Passport v^0.4.0
cookie-parser 1.4.4
body-parser v1.19.0
My app.js
let express = require("express"),
app = express(),
bodyParser = require("body-parser"),
cookieParser = require('cookie-parser'),
session = require("express-session"),
mongoose = require("mongoose"),
passport = require("passport"),
flash = require('connect-flash'),
validator = require('express-validator'),
LocalStrategy = require("passport-local"),
csrf = require('csurf'),
csrfProtection = csrf({ cookie: true }),
MongoStore = require('connect-mongo')(session);
let indexRoutes = require('./routes/index');
let userRoutes = require('./routes/user');
let User = require("./models/user");
// APP CONFIGURATION
mongoose.connect("mongodb://localhost:27017/azax", { useNewUrlParser: true, useUnifiedTopology: true, }).then(() => {
console.log("Connected to MongoDB");
}).catch((error) => {
console.log("Something is wrong...");
});
require('./config/passport');
// View engine setup
app.set("view engine", "ejs");
app.use(express.static(__dirname + "/public"));
// Initial setup
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(validator());
// Session setup
app.use(cookieParser());
app.use(session({
secret: 'somesecretforbytox',
resave: false,
saveUninitialized: false
}));
app.use(flash());
// Initialize passport
app.use(passport.initialize());
app.use(passport.session());
app.use(csrf());
// CSRF
app.use(function (req, res, next) {
var token = req.csrfToken();
res.cookie('XSRF-TOKEN', token);
res.locals.csrfToken = token;
next();
});
app.use(function (req, res, next) {
res.locals.currentUser = req.user;
res.locals.session = req.session;
next();
});
// ======================
// Connect to route files
// ======================
app.use('/user', userRoutes);
app.use(indexRoutes);
app.listen(3033, function () {
console.log("Listening at port 3033...");
});
My passport.js:
let passport = require('passport');
let User = require('../models/user');
let LocalStrategy = require('passport-local').Strategy;
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
User.findById(id, function (err, user) {
done(err, user);
});
});
passport.use('local-signup', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
}, function (req, email, password, done) {
req.checkBody('username', 'Invalid username').notEmpty();
req.checkBody('email', 'Invalid email').notEmpty().isEmail();
req.checkBody('password', 'Invalid password').notEmpty().isLength({ min: 4 });
let errors = req.validationErrors();
if (errors) {
let messages = [];
errors.forEach(function (error) {
messages.push(error.msg);
});
return done(null, false, req.flash('error', messages));
}
User.findOne({ 'email': email }, function (err, user) {
if (err) {
return done(err);
}
if (user) {
return done(null, false, { message: 'Вече има акаунт с този имейл.' })
}
let username = req.body.username;
let newUser = new User();
newUser.username = username;
newUser.email = email;
newUser.password = newUser.encryptPassword(password);
newUser.country = 'България';
newUser.save(function (err, result) {
if (err) {
return done(err);
}
return done(null, newUser);
});
});
}));
passport.use('local-signin', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
}, function (req, email, password, done) {
// req.checkBody('username', 'Invalid username').notEmpty();
req.checkBody('email', 'Invalid email').notEmpty();
req.checkBody('password', 'Invalid password').notEmpty();
let errors = req.validationErrors();
if (errors) {
let messages = [];
errors.forEach(function (error) {
messages.push(error.msg);
});
return done(null, false, req.flash('error', messages));
}
User.findOne({ 'email': email }, function (err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, { message: 'Акаунтът не е намерен.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Грешна парола.' });
}
return done(null, user);
});
}));
HTML...:
<form action="/user/profile" method="POST" class="form-validate form-horizontal" enctype="multipart/form-data">
<fieldset>
<legend>Edit address</legend>
<!-- email -->
<div class="control-group">
<div class="control-label">
<label id="jform_email1-lbl" for="jform_email" class="hasPopover required" title="" data-content="Enter new email address." data-original-title="Email Address">
Email<span class="star"> *</span></label>
</div>
<div class="controls">
<input type="email" name="email" class="validate-email required" id="jform_email" value="<%= (typeof currentUser.email != 'undefined' ? currentUser.email : '') %>" size="30" autocomplete="email" required aria-required="true">
</div>
</div>
<!-- name -->
<div class="control-group">
<div class="control-label">
<label id="jform_fname-lbl" for="jform_fname" class="hasPopover required" title="" data-content="Enter new name." data-original-title="Name">
Name<span class="star"> *</span></label>
</div>
<div class="controls">
<input type="text" name="firstName" id="jform_fname" value="<%= (typeof currentUser.firstName != 'undefined' ? currentUser.firstName : '') %>" class="required" size="30" required aria-required="true">
</div>
</div>
</fieldset>
<div class="form-actions">
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
<button type="submit" class="btn btn-primary validate">
<span>Save</span>
</button>
<a class="btn" href="/" title="Cancel">Cancel</a>
</div>
</form>
user.js:
let express = require('express'),
router = express.Router(),
csrf = require('csurf'),
csrfProtection = csrf(),
passport = require('passport');
router.use(csrfProtection);
let User = require("../models/user");
// user profile
router.get("/profile", isLoggedIn, csrfProtection, function (req, res) {
res.render("user/profile", { csrfToken: req.csrfToken(), currentUser: req.user });
});
router.post('/profile', (req, res) => {
updateRecord(req, res);
res.redirect('/profile');
});
// update user data
function updateRecord(req, res) {
User.findOne({ _id: req.user.id }, (err, doc) => {
doc.name = req.body.name;
doc.save(function (err, doc) {
});
});
}
router.get("/profile/edit", isLoggedIn, csrfProtection, function (req, res) {
res.render("user/edit", { csrfToken: req.csrfToken(), currentUser: req.user });
});
// sign up form works with csrf
// signup form
router.get("/signup", csrfProtection, function (req, res) {
let messages = req.flash('error');
res.render("user/signup", { csrfToken: req.csrfToken(), messages: messages, hasErrors: messages.length > 0 });
});
// ... more routes
module.exports = router;
// middleware
function isLoggedIn(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect('/user/login');
}
What worked for me was adding the below hidden field to every form:
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
and sending the token to every get request to the template page displaying the form.
{csrfToken: req.csrfToken()}
like
router.get('/user/login',function(req,res){
res.render('pages/login', {csrfToken: req.csrfToken()});
})
this fixed the error in whole app.

Cant login and no error messages using node/express & passport.js

I have no problems registering but when trying to log in all the that happens is the page refreshes with no error messages.
Here is the login form (I'm using ejs)
<form class="" action="/login" method="post">
<h1>Login</h1>
<div class="EmailAddress">
<input type="text" name="email" placeholder="Email">
</div>
<div class="Password">
<input type="password" name="password" placeholder="Password">
</div>
<div class="Login">
<input type="submit" value="submit">
</div>
</form>
I have created two models for two different types of user one is a Carrier and one is a Customer and they are both practically identical and working perfectly fine for registering. For the moment I'm only trying to log in a Customer so here is here my passport.js file:
Edit: I've updated it according to what zerosand1s said below and still not working unfortunately.
module.exports = function(passport) {
//LocalStrategy
passport.use(
new LocalStrategy(
{ passReqToCallback: true },
{
usernameField: "email",
passwordField: "password"
},
function(req, email, password, done) {
let query = { email: email };
// checking the name in the database against the one submitted on the form
Customer.findOne(query, function(err, customer) {
if (err) throw err;
if (!customer) {
return done(null, false, {
message: "No such password",
type: "error"
});
}
//Match password
bcrypt.compare(password, customer.password, function(err, isMatch) {
if (err) throw err;
if (isMatch) {
return done(null, customer);
} else {
return done(null, false, {
message: "No such password",
type: "error"
});
// if the password isnt an matc return wrong password
}
});
});
}
)
);
};
passport.serializeUser(function(customer, done) {
done(null, customer.id);
});
passport.deserializeUser(function(id, done) {
Customer.findById(id, function(err, customer) {
done(err, customer);
});
});
Heres my routes file for logging in
index.js
// login route
router.get("/login", function(req, res) {
res.render("login", {message: req.flash('error')});
});
// login Process
router.post("/login", function(req, res, next) {
passport.authenticate('local', {
// were using the LocalStrategy used in config/passport file
successRedirect:'../customer/dashboard',
failureRedirect:'/login',
failureFlash: true
})(req, res, next);
});
router.get("/customer/dashboard", function(req, res) {
res.render("customer/dashboard");
});
And the only place I can think there might be a problem is my app.js file:
// Express session
app.use(session({
secret: 'keyboard cat',
resave: true,
saveUninitialized: true,
cookie: { secure: true }
}));
// Express messages middleware
app.use(require('connect-flash')());
app.use(function (req, res, next) {
res.locals.messages = require('express-messages')(req, res);
// setting a global variable called express-messages
next();
});
// Express validator middleware
app.use(expressValidator({
errorFormatter: function(param, msg, value) {
var namespace = param.split('.')
, root = namespace.shift()
, formParam = root;
while(namespace.length) {
formParam += '[' + namespace.shift() + ']';
}
return {
param : formParam,
msg : msg,
value : value
};
}
}));
app.use(function (req, res, next) {
res.locals.messages = require('express-messages')(req, res);
next();
});
// Passport Config
require('./config/passport')(passport);
// Passport Middleware
app.use(passport.initialize());
app.use(passport.session());
app.get('*', function(req, res, next) {
res.locals.customer = req.customer || null;
next();
});
app.use(cookieparser());
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())
app.listen(3000, function() {
console.log('server is running');
});
app.use(flash());
app.use(routes);
I've spent the entire day ripping my hair other this so any help is greatly appreciated
Since you are using email for login (instead of default username), you need to specify as such in your Passport local strategy.
From the Passport documentation,
By default, LocalStrategy expects to find credentials in parameters
named username and password. If your site prefers to name these fields
differently, options are available to change the defaults.
passport.use(new LocalStrategy({
passReqToCallback: true
usernameField: 'email',
passwordField: 'passwd'
},
function(username, password, done) {
// ...
}
));

PassportJS and local strategy

I get an issue using PassportJS and my local strategy. I'm using ExpressJS 4.
My app.js (reduced, without express init) :
var express = require('express'),
ejs = require('ejs'),
path = require('path'),
expressLayouts = require('express-ejs-layouts'),
session = require('express-session'),
compression = require('compression'),
_ = require('lodash'),
fs = require('fs-extra'),
colors = require('colors'),
passport = require('passport'),
flash = require('connect-flash'),
localStrategyMiddleware = require('./middlewares/localStrategy'),
logMiddleware = require('./middlewares/log'),
localsMiddleware = require('./middlewares/locals'),
securityMiddleware = require('./middlewares/security');
this.app.use(session({
secret : 'd4qs45sdq6',
// name : 'sessionId',
// proxy: true,
resave: true,
saveUninitialized: true
}));
this.app.use(flash());
this.app.use(function(request, response, next) {
response.locals.flash = {
info: request.flash('info'),
success: request.flash('success'),
warning: request.flash('warning'),
danger: request.flash('danger')
};
next();
});
this.app.use(passport.initialize());
this.app.use(passport.session());
this.app.post('/test', passport.authenticate('local', {
successRedirect: '/user/success',
failureRedirect: '/user/failure',
failureFlash: true,
successFlash: true
}), function(request, response) {
console.log('test');
});
My strategy (./middlewares/localStrategy) :
var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
sequelize = require('../databases/Database').connexion,
UserModel = require('../app/models/UserModel')(sequelize);
passport.use(new LocalStrategy(
function(username, password, done) {
console.log('test');
UserModel.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
passport.serializeUser(function(user, done) {
console.log(user);
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
console.log(id);
UserModel.findById(id, function(err, user) {
done(err, user);
});
});
module.exports = passport;
Issue : I get no logs and I'm every time redirected to user/failure.
Edit 1 :
<form action="/test" method="post">
<input type="hidden" name="_csrf" value="<%=csrfToken%>">
<input type="text" name="user[username]" placeholder="username">
<input type="text" name="user[password]" placeholder="password">
<input type="submit">
<input type="reset">
</form>
If you are getting no logs then that tells you that your app.js file doesn't have access to the code in ./middlewares/localStrategy
Try changing
localStrategyMiddleware = require('./middlewares/localStrategy')
to simply
require('./middlewares/localStrategy')
Also you need to change your name attributes on your form to match with those that passport uses, I think the default are username and password, so unless you want to specify alternatives in your localStrategy you should use the following.
<input type="text" name="username" placeholder="username">
<input type="text" name="password" placeholder="password">

Node.js app.post for passport-local signup form not working

I'm trying to setup passport local sign-in but the form doesn't do anything when submitted.
My app.js code:
var express = require('express');
var app = express();
app.use(express.static(__dirname + '/public'));
var credentials = require('./credentials.js');
app.use(require('cookie-parser')(credentials.cookieSecret));
app.use(require('body-parser').urlencoded({extended: false}));
var session = require('express-session');
app.use(session({
resave: false,
saveUninitialized: true,
secret: 'keyboard cat'
}));
var passport = require('passport');
app.use(passport.initialize());
app.use(passport.session());
app.disable('x-powered-by');
var handlebars = require('express-handlebars').create({defaultLayout:'main'});
app.engine('handlebars', handlebars.engine);
app.set('view engine', 'handlebars');
app.set('port', process.env.PORT || 3000);
app.post('/login', function(req, res){console.log("body parsing", req.body)});
app.get('/login', function(req, res) {
res.render('login');
});
The form html at /login for sign-ins:
<form action="/login" method="post">
<div>
<label>Username:</label>
<input type="text" name="username"/>
</div>
<div>
<label>Password:</label>
<input type="password" name="password"/>
</div>
<div>
<button type="submit">Submit</button>
</div>
</form>
When I fill in username and password nothing prints out into the console from the post request. What do I have wrong?
You have to serialize and deserialize the user details to make the user authenticated.
Here's and example.
services.js
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const User = require('../models/user');
//serialize and deserialize
passport.serializeUser(function(user, done) {
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
//middleware
passport.use('local-login', new LocalStrategy({
usernameField: 'email',
password: 'password',
passReqToCallback: true
}, (req, email, password, done) => {
User.findOne({ email: email.toLowerCase() }, function(err, user) {
if (err) return done(err);
if (!user) {
return done(null, false, req.flash('loginMessage', 'No user has been found'));
}
if (!user.comparePassword(password)) {
return done(null, false, req.flash('loginMessage', 'Oops! Wrong Password.'));
}
return done(null, user);
});
}));
//custom function to validate
exports.isAuthenticated = function(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect('/login');
}
and once you do the serializing, you have to actually apply the process to the router. i.e, make the user details enter passport.
In your router file:
const passportConfig = require('services.js');
Then you can simply access the data via req.user
I found that removing this AMP js script solves the issue. For some reason it breaks the post requests.
<script async src="https://cdn.ampproject.org/v0.js"></script>

Nodejs Passport authenticate callback not being called with no errors

I've seen similar questions to mine, but I cannot find the right answer.
I'm trying to implement a very simple login form using nodejs+express+passport (local strategy). The thing is, it seems that my authenticate callback always fails. I've removed every connection to the test DB (every tutorial has an example MongoDB).
This is my login form
<html>
<body>
<form action="/login" method="post">
<div>
<label>Username:</label>
<input type="text" name="username" />
<br/>
</div>
<div>
<label>Password:</label>
<input type="password" name="password" />
</div>
<div>
<input type="submit" value="Submit" />
</div>
</form>
</body>
</html>
This is my server.js (i run it with npm start)
var express = require('express');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var app = express();
var port = process.env.PORT || 8080;
app.use(passport.initialize());
app.use(passport.session());
passport.use(new LocalStrategy(function(username, password, done) {
// no authentication logic here... just return done with an object with 2 fields
return (done, {username : username, password : password});
}));
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
// ROUTES
// ==============================================
// sample route with a route the way we're used to seeing it
app.post('/login',
passport.authenticate('local', {
successRedirect: '/loginSuccess',
failureRedirect: '/loginFailure'
})
);
app.get('/login', function(req, res) {
res.sendfile('views/login.html');
});
app.get('/loginFailure', function(req, res, next) {
res.send('Failed to authenticate');
});
app.get('/loginSuccess', function(req, res, next) {
res.send('Successfully authenticated');
});
// START THE SERVER
// ==============================================
app.listen(port);
console.log('Magic happens on port ' + port);
I always get the Failed to authenticate message. Like I said, I've stripped every code relating to a schema/model with mongoose to read data from a sample MongoDB. The passport.authenticate callback just returns the done function with an object with 2 fields, called username and password like the ones expected by passport-local
Can you help me?
I don't believe you're invoking the callback correctly:
First param expects an error or null and second expects false, or user object.
passport.use(new LocalStrategy(function(username, password, done) {
// no authentication logic here... just return done with an object with 2 fields
done(null, {username : username, password : password});
}));
or, using a db:
passport.use(new LocalStrategy(function(username, password, done) {
db.users.findOne({ username : username}, function(err, user){
if(err) return done(err);
if(!user || user.password !== password) return done(null, false);
done(null, user);
});
});
Here you can see we compare passwords before responding with the user object, which then gets attached by the passport lib to req.user. Ofcourse you wouldn't compare the two passwords but instead their hashes using bcrypt or similar.

Resources