NodeJS - Express - Sessions - Redirect to secure page - javascript

I'm trying to set up a "backend" for a webpage I've created. So some pages can only be accessible if the user is logged in. I've built the basic functionality for this and I can do a simple validation in whether the user is logged-in. But the redirecting to the page is where I get stuck.
Example:
var auth = function(req,res,next){
if (req.session.loggedin){
return next();
} else{
return res.sendStatus(401);
}
};
app.get('/list-video', auth, function (req, res) {
res.redirect('/list-video');
});
So my issue is that '/list-video' is the page I want to protect and only be accessible when the user successfully logged in. But after the validation, I'm redirecting to the same page: '/list-video'. this doesn't seem to work as I'm obviously getting stuck in a loop. I have tried redirecting to a different page like '/list-audio' and of course this works fine.
Can someone advise on how this is usually done? Do I need to create a separate link that I can redirect to? (I do want to prevent users going to that link manually by typing the URL in the browser.)
Any help or advice will be greatly appreciated!
My complete app.js code:
const express = require('express');
const fileUpload = require('express-fileupload');
const bodyParser = require('body-parser');
const mysql = require('mysql');
const path = require('path');
const app = express();
const session = require('express-session');
const { getHomePage } = require('./routes/index');
const { getBackendPage } = require('./routes/backend');
const { getVideoPage, listVideoPage, editVideoPage, editVideo, deleteVideo, addVideoPage, addVideo } = require('./routes/video');
const { getEbookPage } = require('./routes/ebook');
const { getMusicPage } = require('./routes/music');
const { getGamePage } = require('./routes/game');
const { getShopPage } = require('./routes/shop');
const port = 5000;
const db = mysql.createConnection({
host: '127.0.0.1',
user: 'user',
password: 'bla',
database: 'test'
db.connect((err) => {
if (err) {
throw err;
}
console.log('Connected to database');
});
global.db = db;
// configure middleware
app.set('port', process.env.port || port); // set express to use this port
app.set('views', __dirname + '/views'); // set express to look in this folder to render our view
app.set('view engine', 'ejs'); // configure template engine
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json()); // parse form data client
app.use(express.static(path.join(__dirname, 'public'))); // configure express to use public folder
app.use(fileUpload()); // configure fileupload
app.use(session({
secret: 'secret',
resave: true,
saveUninitialized: true
}));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
/ passenger views
app.get('/', getHomePage);
app.get('/backend', getBackendPage);
app.get('/video', getVideoPage);
app.get('/ebook', getEbookPage);
app.get('/music/:id', getMusicPage);
app.get('/game', getGamePage);
app.get('/shop', getShopPage);
// backend views video
app.get('/edit-video/:id', editVideoPage);
app.post('/edit-video/:id', editVideo);
app.get('/add-video', addVideoPage);
app.post('/add-video', addVideo);
app.get('/delete-video/:id', deleteVideo);
// login screen
app.post('/auth', function (request, response) {
var username = request.body.username;
var password = request.body.password;
if (username && password) {
db.query('SELECT * FROM accounts WHERE username = ? AND password = ?', [username, password], function (error, results, fields) {
if (results.length > 0) {
request.session.loggedin = true;
request.session.username = username;
response.redirect('/');
} else {
response.send('Incorrect Username and/or Password!');
}
response.end();
});
} else {
response.send('Please enter Username and Password!');
response.end();
}
});
var auth = function(req,res,next){
if (req.session.loggedin){
return next();
} else{
return res.sendStatus(401);
}
};
app.get('/list-video', auth, function (req, res) {
res.redirect('/list-video');
});
app.listen(port, () => {
console.log(`Server running on port: http://localhost:${port}`);
});

Move your protected pages to a different directory (outside the folder where your public static files are) and serve the express.static after the authentication middleware, something like this:
app.use('/', express.static(path.join(__dirname, 'public'))); //notice I have no auth middleware
app.use('/mysecretpages', auth, express.static(path.join(__dirname, 'secret'))); //notice I DO have auth middleware

change it to:
var auth = function(req,res,next){
if (!req.session.loggedin){
return res.redirect("/login");
} else{
return next();
}
};
app.get('/list-video', auth);
That way you will redirect to the login page if the user isn't authenticated and continue if he is.

Related

node-postgres queries not returning anything in Express routes async routes

I'm trying to run a simple query from an express route:
var router = require('express-promise-router')()
const { Pool } = require('pg')
const pool = new Pool({
user: 'user',
password: 'password',
host: 'host',
port: 1234,
database: 'db'
})
router.get('/', async (req, res) => {
console.log('OK')
try {
const { rows } = await pool.query('Select VERSION()')
console.log(rows)
}
catch(e) {
console.log(e)
}
console.log('DONE')
})
module.exports = router
'OK' Prints after sending the request but rows, e, or 'DONE' never print. I'm following the async/await method directly from https://node-postgres.com/guides/async-express.
I've also came across a thread for koa-router where people were having issues with async await calls because of some middle-ware they added that wasn't synchronous
https://github.com/ZijianHe/koa-router/issues/358.
I'm not sure what middle-ware would cause this but here's my app.js that initializes all middle-ware:
var createError = require('http-errors');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var cors = require("cors");
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var dataRouter = require("./routes/data");
var uploadRouter = require("./routes/upload")
var fundingRouter = require('./routes/chartData/fundingOverview')
var testRouter = require('./routes/test')
var authRouter = require('./routes/auth')
var session = require('express-session')
var MongoStore = require('connect-mongo')(session)
var passport = require('passport')
const config = require('config')
const mongo = config.get('mongo')
const mongoose = require('mongoose')
mongoose.connect(mongo, {
useUnifiedTopology: true,
useNewUrlParser: true,
useFindAndModify: false
}).then(res => {
console.log('connected')
}).catch(err => {
console.log(err)
})
var express = require('express');
const mountRoutes = require('./routes')
var app = express();
const bodyParser = require('body-parser')
app.use(bodyParser.json())
mountRoutes(app)
app.use(cors())
var sessionMiddleWare = session({
secret: 'top session secret',
store: new MongoStore({ mongooseConnection: mongoose.connection }),
resave: true,
saveUninitialized: true,
unset: 'destroy',
cookie: {
httpOnly: false,
maxAge: 1000 * 3600 * 24,
secure: false, // this need to be false if https is not used. Otherwise, cookie will not be sent.
}
})
app.use(sessionMiddleWare)
// Run production React server on Node server
if(process.env.NODE_ENV === 'production') {
app.use(express.static('client/build'))
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'))
})
}
// End Run production React Server on Node Server
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({
extended: false
}));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
// app.use('/upload', uploadRouter)
// app.use('/', indexRouter);
// app.use('/users', usersRouter);
// app.use('/data', dataRouter)
// app.use('/funding', fundingRouter)
// app.use('/login', usersRouter)
// app.use('/auth', authRouter)
// catch 404 and forward to error handler
// app.use(function(req, res, next) {
// next(createError(404));
// });
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
I'm mounting the routes directly after body parser. That's the only middle-ware that's called before the routes and is required in order for me to get data into the back end.
I'm able to execute that simple query by putting it into a script file and running 'node test.js' (I.E without the router) and it works fine so I know it's not a problem with node-postgre.
I know this is a problem with the call stack not being totally synchronous but I'm confused as to what's not at this point. I even made the axios call on the front-end async/await with no luck (I don't think it was necessary though).
Any guidance would be help a lot.
EDIT:
I created a fresh express skeleton and hooked my front-end to make a call to a route on the new express server with the same code, it worked. It led me to discover the call wasn't being completed because I was running the server with Nodemon. When I start the server using 'yarn start' the async calls get processed correctly. The question now is what in nodemon makes async router calls not work?
You need to finish the request/response cycle in your middleware.
So in your code:
router.get('/', async (req, res) => {
console.log('OK')
try {
const { rows } = await pool.query('Select VERSION()')
console.log(rows)
res.status(200).json(rows)
}
catch(e) {
console.log(e)
res.status(500).json(e)
}
console.log('DONE')
})

Login Page Redirect - Make HTML inaccessible if not logged in

I have a basic login page in HTML where username and password fields are handled in javascript and the inputs are sent back to my Node API for a full AD authentication.
I have 4 more HTML sites which should only be accesible if the user has authenticated. The authentication part running in Node app works perfectly fine, however, I am not able to handle a session for the users who have logged in.
I also want to make all the 4 HTML sites inaccessible if the login fails or when the user logs out.
How do I handle the accessibility part by simply using HTML and Vanilla JS?
login.js
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var router = express.Router();
var cors = require('cors');
var ActiveDirectory = require('activedirectory');
const session = require('express-session');
app.use( bodyParser.json() ); // to support JSON-encoded bodies
app.use(bodyParser.urlencoded({ // to support URL-encoded bodies
extended: true
}));
app.use(session({
name: 'session.sid',
secret: 'entersomepasswordhere',
resave: false,
saveUninitialized: false
}));
var port = process.env.PORT || 8080;
app.use('/', router);
router.post('/api/Login1', function(req, res)
{
var username = req.body.username;
var password = req.body.password;
console.log(username);
var config = {
url: 'abc',
baseDN: 'dc=abc,dc=abc'
};
var ad = new ActiveDirectory(config);
// Authenticate
ad.authenticate(username, password, function(err, auth) {
if (err) {
res.send("Login Failed. Incorrect username or password");
console.log('ERROR: '+JSON.stringify(err));
return;
}
if (auth) {
console.log('Authenticated!');
req.session.loggedin = true;
res.redirect('/test.html');
}
else {
console.log('Authentication failed!');
res.send("Login Failed. Incorrect username or password");
}
});
});
router.get('/test.html', function(req, res) {
if (req.session.loggedin == true) //check if user is loggedin, replace with your variables/checks
res.sendFile('/absolutepath/test.html');
else
res.redirect('/Login1');
})
app.use(cors());
app.listen(port);
console.log('Listening at port ' + port);
I think I know what the problem is. The core module just handles every request for you, therefore every http request first looks in the core middleware if it finds a matching html file. You need to place
app.use(cors());
After you've defined your routes and define a custom route to your test.html. See the following example:
var express = require('express');
var app = express();
var router = express.Router();
var cors = require('cors');
var port = process.env.PORT || 8080;
//use routes on the localhost:8080 path
app.use('/', router);
//Relevant part:
router.get('/test.html', function(req, res) {
var loggedin = true; //false if not loggedin
if (loggedin)
res.sendFile(__dirname + '/test.html');
else
res.redirect('https://www.google.com/');
})
//This needs to be after the routes
app.use(cors());
app.listen(port);
console.log('Listening at port ' + port);
You can set loggedin to false to see what happens if a user isn't loggedin and true if a user is loggedin.
UPDATE
In order to save if a user is loggedin, i recommend using the express-session module: https://expressjs.com/en/resources/middleware/cookie-session.html
You can include it like the following:
const session = require('express-session');
app.use(session({
name: 'session.sid',
secret: 'entersomepasswordhere',
resave: false,
saveUninitialized: false
}));
After authenticating you can just set
req.session.loggedin = true;
and then just check if
req.session.loggedin == true
Note: you can replace loggedin with any variable name you like, and you can also set multiple session cookies.
In your case just set req.session.loggedin = true before you redirect to your test.html and then perform the check in there.

setting up passport local strategy with postgresSQL

I'm having issues setting up passport authentication with my server. I've used passportjs before but with mongodb. I'm currently trying to setup my local strategy with postgressql but have had no luck. When going to a login POST route with passport.authenticate(), I'm not getting a cookie sent back to me. I'm not sure if I setup my server correctly with passportJS and with my postgres database hosted via Heroku.
require('dotenv').config(); //In order to gain access to our .env file
//process.env.YELP_API_KEY
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
app.use(bodyParser.json()); //This is going to allow us to access data stored in 'body' when making a post or put request
app.use(bodyParser.urlencoded({extended: true}));
const { Pool } = require('pg');
const fs = require("fs"); //This is needed to convert sql files into a string
let port = process.env.PORT || 5000;
//session-related libraries
const session = require("express-session");
const passport = require("passport"); //This is used for authentication
const LocalStrategy = require("passport-local").Strategy;
const bcrypt = require("bcrypt");
//Setting up our session
app.use(session({
secret: process.env.SECRET,
resave: false,
saveUninitialized: false
}));
//Connection to database
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: true
}); //This is used to connect to our remote postegres database hosted via Heroku
//initializing our session
app.use(passport.initialize());
app.use(passport.session()); //Telling our app to use passport for dealing with our sessions
//setting up our local strategy
passport.use('local', new LocalStrategy({passReqToCallBack: true},( username, password, cb )=> {
console.log("this is being executed");
pool.query("SELECT id, username, password from users where username=$1", [username], (err, result) => {
if(err){
return cb(err);
}
if(result.rows.length > 0){
const first = result.rows[0];
bcrypt.compare(password, first.password, (err, res) => {
if(res){
cb(null, {
id: first.id,
user: first.username
})
}
else {
cb(null, false);
}
})
}
else {
cb(null, false);
}
})
}));
passport.serializeUser(function(user, done){
console.log("serialize user is executing")
done(null, user.id);
})
passport.deserializeUser(function(id, done){
pool.query('SELECT id, username FROM users WHERE id = $1', [parseInt(id, 10)], (err, results) => {
if(err) {
return done(err)
}
done(null, results.rows[0])
});
});
app.post("/api/login", (req, res) => {
passport.authenticate('local', function(err, user, info){
console.log(user);
});
})
app.listen(port, function(){
console.log("Your app is running on port " + port);
});
Expected Results: user should be able to login with the post route "/api/login" but passport.authenticate is not working? Passport local strategy should also be correctly setup.
In your route app.post("/api/login", .... the passport.authenticate needs an access to the req and res.
There's more than one way to make it work.
app.post("/api/login", (req, res) => {
passport.authenticate('local', function(err, user, info){
console.log(user);
// make sure to respond to the request
res.send(user);
})(req, res); // <= pass req and res to the passport
})
// or
// use it as a middleware
app.post("/api/login", passport.authenticate('local'), (req, res) => {
console.log(req.user);
// make sure to respond to the request
res.send(req.user);
})

nodejs expressjs ERR_TOO_MANY_REDIRECTS

I have error with "ERR_TOO_MANY_REDIRECTS" in browser.
On linux server it looks like:
Error: Can't set headers after they are sent.
This is my app.js:
const express = require('express');
const bodyParser = require('body-parser');
const mysql = require('mysql');
const path = require('path');
const app = express();
const session = require('express-session');
const {getHomePage} = require('./routes/index');
const {getmain, addUserPage, addUser, deleteUser, editUser, editUserPage, addTemplates, getHistory} = require('./routes/user');
const port = 5000;
var auth = function(req, res, next) {
if (req.session && req.session.user === "amy" && req.session.admin)
return next();
else
return res.sendStatus(401);
};
// create connection to database
// the mysql.createConnection function takes in a configuration object which contains host, user, password and the database name.
const db = mysql.createConnection ({
host: 'localhost',
user: '*****',
password: '*****',
database: '*****',
charset : 'utf8mb4'
});
// connect to database
db.connect((err) => {
if (err) {
throw err;
}
console.log('Connected to database');
});
global.db = db;
// configure middleware
app.set('port', process.env.port || port); // set express to use this port
app.set('views', __dirname + '/views'); // set express to look in this folder to render our view
app.set('view engine', 'ejs'); // configure template engine
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json()); // parse form data client
app.use(express.static(path.join(__dirname, 'public'))); // configure express to use public folder
app.locals.moment = require('moment');
app.use(session({
secret: '2C44-4D44-WppQ38S',
resave: true,
saveUninitialized: true,
cookie: { maxAge: 1800000}
}));
// routes for the app
app.get('/', getHomePage);
app.get('/main', auth, getmain);
app.get('/add', auth, addUserPage);
app.get('/edit/:id', auth, editUserPage);
app.get('/delete/:id', auth, deleteUser);
app.get('/addtemp/:login', auth, addTemplates);
app.get('/history', auth, getHistory)
app.post('/add', auth, addUser);
app.post('/edit/:id', auth, editUser);
app.post('/addtemp/:login', auth, addTemplates);
// Login endpoint
app.get('/login', function (req, res) {
if (!req.query.username || !req.query.password) {
res.send('login failed');
} else if(req.query.username === "amy" || req.query.password === "amy") {
req.session.user = "amy";
req.session.admin = true;
res.redirect('/main');
}
});
// Logout endpoint
app.get('/logout', function (req, res) {
req.session.destroy();
res.redirect("/");
});
// set the app to listen on the port
app.listen(port, () => {
console.log(`Server running on port: ${port}`);
});
I know I have some problems in
app.get('login...
Theres everything ok with success login using correct username and password but when I use incorrect nothing happend.
This is my module getmain (after correct login):
getmain: (req, res) => {
let query = "SELECT * FROM `users` ORDER BY id ASC";
// execute query
db.query(query, (err, result) => {
if (err) {
res.redirect('/main');
}
res.render('main.ejs', {
title: "blablabla"
,users: result
});
});
},
And this is index.js:
module.exports = {
getHomePage: (req, res) => {
let query = "SELECT * FROM `users` ORDER BY id ASC";
// execute query
db.query(query, (err, result) => {
if (err) {
res.redirect('/');
}
res.render('index.ejs', {
title: "blablabla"
,users: result
});
});
},
};
I read that's all because by looping but I can not figure it out.
I will be grateful for directing me to the source of the problem.

Node.js server running and listening on Port 3000 but Localhost:3000 didn't send any data

So the server is running and listening to port 3000 which isn't being blocked
as far as I can tell, but when I try and load the page it hangs for a while before providing the following message:
"This page isn’t working
localhost didn’t send any data.
ERR_EMPTY_RESPONSE
I'm not sure why I can't get anything to pull through even when I adjust the get('/") homepage to render something simple. There also aren't any errors pulling through the terminal so I'm unsure how to diagnose the problem.
My app.js code:
const express = require('express');
const path = require('path');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const expressValidator = require('express-validator');
const flash = require('connect-flash');
const session = require('express-session');
mongoose.connect('mongodb://localhost/nodekb');
let db = mongoose.connection;
// Check connection
db.once('open', function(){
console.log('Connected to MongoDB');
});
// Check for DB errors
db.on('error', function(err){
console.log(err);
});
// Init App
const app = express();
// Bring in Models
let Article = require('./models/article');
// Load View Engine
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
// Body Parser Middleware
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }));
// parse application/json
app.use(bodyParser.json());
// Set Public Folder
app.use(express.static(path.join(__dirname, 'public')));
// Express Session Middleware
app.use(session({
secret: 'keyboard cat',
resave: true,
saveUninitialized: true
}));
// Express Messages Middleware
app.use(require('connect-flash')());
app.use(function (req, res, next) {
res.locals.messages = require('express-messages')(req, res);
next();
});
// Express Validator Middleware
app.use(expressValidator);
// Home Route
app.get('/', function(req, res){
Article.find({}, function(err, articles){
if(err){
console.log(err);
} else {
res.render('index', {
title:'Articles',
articles: articles
});
}
});
});
// Route Files
let articles = require('./routes/articles');
let users = require('./routes/users');
app.use('/articles', articles);
app.use('/users', users);
app.listen(3000, function(){
console.log("We are running on port 3000");
});
I think the problem is your query is fetching empty array.
I have added condition in callback of query to check array length of articles.
app.get('/', function(req, res){
Article.find({}, function(err, articles){
if(err){
console.log(err);
res.json(err);
}else if(articles.length == 0){
console.log("Articles not found");
res.json({error : "Articles not found"});
} else {
console.log("Articles found");
res.render('index', {
title:'Articles',
articles: articles
});
}
});
});
I hope it will help you.

Categories