I'm trying to save the userName of a user in express session. It saves the variable in session but it returns undefined on next request to the route. However, the problem is with POST route only.
With GET request, I can save session variable and it doesn't get destroyed on further requests. I think I must be doing something wrong. I tried save() method for POST but still it saves only first time and next time it is vanished.
Here's the code:
app.use(session({
secret: 'some secret',
resave: true,
saveUninitialized: true,
cookie: {
maxAge: 36000000,
httpOnly: false
},
}));
FOLLOWING GET ROUTE WORKS.
app.get("/test", function(req, res, next){
if(!req.session.name){
req.session.name = "vikas kumar";
} else {
console.log(req.session.name);
}
res.end();
});
app.use(function (req, res, next) {
var userName = req.session.userName;
// console.log(req.session.userName);
if(userName && userName != 'undefined'){
res.send({status: "success", value: userName});
} else {
if(req.path == '/checkIfLoggedIn'){
res.send({status: "error", message: "Session ended."});
} else {
next('route');
}
}
});
FOLLOWING ROUTE SAVES SESSION ONLY ONCE.
app.post('/getProfile', function (req, res, next) {
console.log("Session", req.session.userName, req.session.name);
if(typeof req.session.userName != 'undefined'){
var userName = req.session.userName;
} else {
var userName = req.body.userName;
}
connectionPool.getConnection(function (err, connection) {
if (err) {
res.send({status: "error", message: err});
} else {
connection.query("SELECT * FROM cs_chat.users WHERE username=?", [userName], function (err, rows, fields) {
if(rows.length==1){
// console.log(">>>", req.session.userName);
req.session.userName = userName;
req.session.save();
// console.log("<<<", req.session.userName);
next();
}else{
res.send({status: "error", message: "Sorry, you're not registered."});
}
connection.release();
});
}
});
}, function (req, res) {
connectionPool.getConnection(function (err, connection) {
if (err) {
res.send({status: "error", message: err});
} else {
req.session.username = req.body.userName;
req.session.save();
connection.query("SELECT usr.username, usr.name, tsn.sender, CASE WHEN tsn.unseen is not null THEN tsn.unseen ELSE 0 END as unseen, tsn.receiver FROM users usr LEFT JOIN (SELECT sender, receiver, SUM(CASE WHEN seen = 0 THEN 1 ELSE 0 END) AS unseen FROM messages WHERE receiver = ? GROUP BY sender , receiver) tsn ON usr.username = tsn.sender where usr.username!=?", [req.body.userName, req.body.userName], function (err, rows, fields) {
if(rows.length>0){
res.send({status: "success", values: rows});
}else{
res.send({status: "success", values: []});
}
connection.release();
});
}
});
});
Am I doing something wrong?
Since it is Javascript you should be checking quality with === instead of == and for better response time releasing the connection and then again connecting to it, is simply unnecessary, when you have the connection to connectionPool just query from it twice and then release the connection.
Here is the sample modified app.post().. request, this should work:
app.post('/getProfile', function (req, res, next) {
console.log("Session", req.session.userName, req.session.name);
var userName = (req.session.userName !== undefined) ? req.session.userName : req.body.userName;
connectionPool.getConnection(function (err, connection) {
if (err) {
res.send({ status: "error", message: err });
} else {
connection.query("SELECT * FROM cs_chat.users WHERE username=?", [userName], function (err, rows, fields) {
if (rows.length === 1) {
req.session.userName = userName;
req.session.save();
} else {
res.send({ status: "error", message: "Sorry, you're not registered." });
}
});
connection.query("SELECT usr.username, usr.name, tsn.sender, CASE WHEN tsn.unseen is not null THEN tsn.unseen ELSE 0 END as unseen, tsn.receiver FROM users usr LEFT JOIN (SELECT sender, receiver, SUM(CASE WHEN seen = 0 THEN 1 ELSE 0 END) AS unseen FROM messages WHERE receiver = ? GROUP BY sender , receiver) tsn ON usr.username = tsn.sender where usr.username!=?",
[req.body.userName, req.body.userName],
function (err, rows, fields) {
if (rows.length > 0) {
res.send({ status: "success", values: rows });
} else {
res.send({ status: "success", values: [] });
}
});
connection.release();
}
});
});
Related
I recently switched from php development to Javascript (I'm really amazed by the performance and possibilities).
Currently I try to create a simple authentification function (Username,hashed Password checked to mariadb Database)
After following some tutorials I managed to create the following structure:
But when I try to test the API via Postman and Insomnia I just get no response. Not even an Error Code. Just going on forever, just like an infinite Loop?
I'm thankful for any tip as I'm new to this. Thanks in advance.
My Stack: React, Nodejs, Mariadb, Express & Jwt / bcryptjs
My Express Router router.js:
router.post('/login', (req, res, next) => {
pool.query(
`SELECT * FROM TABLE WHERE username = ${pool.escape(req.body.username)};`,
(err, result) => {
// user does not exists
if (err) {
throw err;
return res.status(400).send({
msg: err
});
}
if (!result.length) {
return res.status(401).send({
msg: 'Username or password is incorrect!'
});
}
// check password
bcrypt.compare(
req.body.password,
result[0]['password'],
(bErr, bResult) => {
// wrong password
if (bErr) {
throw bErr;
}
if (bResult) {
const token = jwt.sign({
username: result[0].username,
userId: result[0].id
},
process.env.API_SecretKey, {
expiresIn: '2h'
}
);
return res.status(200).send({
msg: 'Logged in!',
token,
user: result[0]
});
}
return res.status(401).send({
msg: 'Username or password is incorrect!'
});
}
);
}
);
});
router.post('/sign-up', userMiddleware.validateRegister, (req, res, next) => {
pool.query(
`SELECT * FROM TABLE WHERE LOWER(username) = LOWER(${pool.escape(
req.body.username
)});`,
(err, result) => {
if (result.length) {
return res.status(409).send({
msg: 'This username is already in use!'
});
} else {
// username is available
bcrypt.hash(req.body.password, 10, (err, hash) => {
if (err) {
return res.status(500).send({
msg: err
});
} else {
// has hashed pw => add to database
pool.query(
`INSERT INTO TABLE (SecurityID, userPassword, username, userOTP) VALUES ('${pool.escape}', ${pool.escape(
req.body.SecurityID,
req.body.username,
req.body.password,
req.body.userOTP
)}, ${pool.escape(hash)}, now())`,
(err, result) => {
if (err) {
throw err;
return res.status(400).send({
msg: err
});
}
return res.status(201).send({
msg: 'Registered!'
});
}
);
}
});
}
}
);
pool.end;
});
router.get('/secret-route', userMiddleware.isLoggedIn, (req, res, next) => {
console.log(req.userData);
res.send('This is the secret content. Only logged in users can see that!');
});
module.exports = router;
My Middleware users.js
module.exports = {
validateRegister: (req, res, next) => {
// username min length 3
if (!req.body.username || req.body.username.length < 3) {
return res.status(400).send({
msg: 'Passwort:' + req.body.username + 'Please enter a username with at least 3 chars',
});
}
// password min 6 chars
if (!req.body.password || req.body.password.length < 6) {
return res.status(400).send({
msg: 'Passwort:' + req.body.password + 'Please enter a password with at least 6 chars'
});
}
// password (repeat) does not match
if (
!req.body.password_repeat ||
req.body.password != req.body.password_repeat
) {
return res.status(400).send({
msg: 'Both passwords must match'
});
}
next();
},
isLoggedIn: (req, res, next) => {
try {
const token = req.headers.authorization.split(' ')[1];
const decoded = jwt.verify(
token,
process.env.API_SecretKey
);
req.userData = decoded;
next();
} catch (err) {
return res.status(401).send({
msg: 'Your session is not valid!'
});
}
}
};
My index.js:
const express = require("express");
const DigitalMangement = express();
const cors = require('cors');
require("dotenv").config();
DigitalMangement.use(cors());
DigitalMangement.use(express.json());
// add routes
const router = require('./Routes/router.js');
DigitalMangement.use("/api", router);
DigitalMangement.listen(process.env.Application_Port, () => {
console.log("Server is running on Port " + process.env.Application_Port)
});
I haven't reviewed the whole code but, if you throw the error the code block will not continue. In this case, it won't be logged or sent as a response. Try removing the throw err line and rerun the code.
if (err) {
throw err; //! here
return res.status(400).send({
msg: err
});
}
Thanks for all the help fellow Coders:
It seems to be that the import MariaDB isn't 100% correct in this situation.
I changed it to mariadb/callback and it started to work.
The MariaDB library returns Promises and mariadb/callback allows callbacks.
I am new in nodejs. I am creating a basic API to get record by id. Everything is working fine. It is returning user data from database. But when i use password variable from response in same function it give me empty value whereas i am getting value in response. I think this is async issue but i dont know how to fix it.
This is API code
var express = require('express');
var db = require('../db/database');
var bcrypt = require('bcrypt');
const router = express.Router();
router.get("/:userId", (req, res, next) => {
let uid = req.params.userId;
db.query(`SELECT * FROM users WHERE u_id = ${uid}`, (err, data)=> {
if(!err) {
if(data && data.length > 0) {
var message = '';
if(data.u_password){
//var pass = data.u_password;
if(bcrypt.compare('123456', data.u_password)) {
// Passwords match
message = 'Passwords match';
} else {
// Passwords don't match
message = 'Passwords dont match';
}
}
res.status(200).json({
message:message,
});
} else {
res.status(200).json({
message:"User Not found."
});
}
}
});
});
database.js
var mysql = require('mysql');
const pool = mysql.createPool({
connectionLimit : 10,
host: 'localhost',
user: 'root',
password: '',
database: 'lost_and_found',
debug : false
});
function executeQuery(sql, callback) {
pool.getConnection((err,connection) => {
if(err) {
return callback(err, null);
} else {
if(connection) {
connection.query(sql, function (error, results, fields) {
connection.release();
if (error) {
return callback(error, null);
}
return callback(null, results);
});
}
}
});
}
function query(sql, callback) {
executeQuery(sql,function(err, data) {
if(err) {
return callback(err);
}
callback(null, data);
});
}
module.exports = {
query: query
}
Response
{"message":""}
Please change the bcrypt.compare code to following code. It is a callback function:
bcrypt.compare('123456', data.u_password, function(err, result) {
if (err) {
// Passwords don't match
message = 'Passwords dont match';
} else {
// Passwords match
message = 'Passwords match';
}
res.status(200).json({
message:message,
});
})
EDIT 1: Please update the method to following logic:
db.query(`SELECT * FROM users WHERE u_id = ${uid}`, (err, data) => {
if (err) {
throw err;
}
if (data && data.length > 0) {
var message = '';
if (data.u_password) {
bcrypt.compare('123456', data.u_password, function (err, result) {
if (err) {
// Passwords don't match
message = 'Passwords dont match';
} else {
// Passwords match
message = 'Passwords match';
}
res.status(200).json({
message: message,
});
})
}
res.status(200).json({
message: "User Not found."
});
}
res.status(200).json({
message: "User Not found."
});
});
So, I have everything working but it is not showing it is an authenticate user even though it arrives at the proper places...
javascript code from the page to validate login
var UserManager = {
validateLogin : function (username, password) {
var userData = {
username: username,
password: password
}
return new Promise(function(resolve, reject) {
$.ajax({
url: "/musicplayer/users/api/login",
dataType: "json",
data: userData,
type: "POST",
success: function loginSuccess(result, status, xhr) {
resolve(null);
},
error: function loginError(xhr, status, result) {
reject(new Error(result));
},
});
});
}
}
function userLogin(){
UserManager.validateLogin($('#loginEmail').val(), $('#loginPassword').val()).then(function(response) {
window.location = '/musicplayer/library'
},
function(error){
$("#msgBox").messageBox({"messages" : error.message, "title" : "Warning", boxtype: 4 });
$("#msgBox").messageBox("show");
});
return false;
}
local.strategy.js
var passport = require('passport');
var localStrategy = require('passport-local').Strategy;
var userLibrary = require('../../classes/music/userlibrary.js');
module.exports = function () {
passport.use(new localStrategy(
{
usernameField: 'username',
passwordField: 'password'
},
function(username, password, done) {
//validating user here
var userManager = new userLibrary.UserManager();
userManager.login(username, password).then(
function (user){
done(null, user);
},
function (reason){
if (reason.err) {
done(err, false, info);
}
else {
done(null, false, {message: reason.message});
}
}
);
})
);
};
Router
/******* validate the user login ********/
usersRouter.post('/api/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) {
console.log("Login Failed", err.message + " - " + err.stack);
if (req.xhr){
res.status(500).send({ error: 'Internal Error' });
}
else {
next(err);
}
}
else if (!err && !user){
err = new Error();
err.message = info.message;
err.status = 401;
console.log("Invalid Data", err.message);
if (req.xhr){
res.status(401).send({ error: err.message });
}
else {
next(err);
}
}
else if (user){
console.log("Successful Login:", user);
res.status(200).send({message: "successful"});
}
}
)(req, res, next);
});
passport.js file which has my Middleware...
var passport = require("passport");
module.exports = function (app) {
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function(user, done){
done(null, user);
});
passport.deserializeUser(function(user, done){
done(null, user);
});
require('./strategies/local.strategy')();
app.all('/musicplayer/*', function (req, res, next){
// logged in
//need function for exceptions
if (req.user || req.url === '/musicplayer/users/api/login' || req.url === '/musicplayer/users/signin') {
next();
}
// not logged in
else {
// 401 Not Authorized
var err = new Error("Not Authorized");
err.status = 401;
next(err);
}
});
}
Userlibrary/UserManager
I am using promises to be able to utilize the creation of a library and to deal with sync versus async issues that I ran into early on...
var sqlite3 = require('sqlite3').verbose();
function User() {
this.email = "";
this.password = "";
this.userid = "";
};
function UserManager () {
this.user = new User();
};
UserManager.prototype.login = function (email, password) {
var db = new sqlite3.Database('./data/MusicPlayer.db');
params = {
$email: email,
$password: password
}
var self = this;
return new Promise(function(resolve, reject){
db.serialize(function () {
db.get("SELECT * FROM users WHERE email = $email and password = $password", params, function (err, row) {
db.close();
if (!err && row) {
//log in passed
self.user.userid = row.userid;
self.user.email = row.email;
self.user.password = row.password;
resolve(self.user);
}
else if (!err) {
//log in failed log event
reject({
err: err,
message: null
});
}
else {
//error happened through out an event to log the error
reject({
message : "Email and/or Password combination was not found",
err : null
});
}
});
});
});
};
module.exports = {
User : User,
UserManager : UserManager
}
Now, I have debugged this and it is for sure getting to "successful Login"
Returns to the browser with success, the browser says okay let me redirect you to the library page (which is really just a blank page). When it goes to my library page I get a 401 unauthorized.
So if I debug inside the middleware to ensure authentication. I look at req.user and it is undefined and I try req.isAuthenticated() it returns a false.
I think I must be missing something...
What I want is a global authentication saying hey is this person logged in. And then I will set up the route/route basis say okay do they have permission for this page or web service call.
Right now I am sticking with session for everything as it is not useful to me to learn web tokens at this point and time.
Any help would be appreciated... I been around and around on this looking at examples out there. But the examples I find are the "basic" examples no one calling a library to validate from database or they are not trying to set up the authorization globally but rather on a route by route basis.
Upon searching I found this article
https://github.com/jaredhanson/passport/issues/255
then I found this in documentation
app.get('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/users/' + user.username);
});
})(req, res, next);
});
and that worked for me... I basically forgot to do the req.logIn method itself when using the custom callback.... I knew it was something simple... Hope this helps someone in the future.
I am currently practicing nodeJS and I would like to achieve something like this :
I am trying to check if another session with the same session.username exists. and if it does, display a warning. I am doing it this way :
app.post('/login', (req, res) => {
let options = {"username": req.body.username, "error": null};
if(!req.body.username) {
options.error = "username required nobi";
res.render('login', options);
} else if (req.body.username == req.session.username) {
res.redirect('/');
} else {
req.sessionStore.all( (err, sessions) => {
if(!err) {
let isUsed = false;
let i=0;
for(i; i < sessions.length; i++) {
let session = JSON.parse(sessions[i]);
if (session.username == req.body.username) {
err = "name already taken";
isUsed = true;
break;
}
}
}
if (err) {
options.error = err;
res.render('login', options);
} else {
req.session.username = req.body.username;
res.redirect("/");
}
});
}
});
It is not working : I am connecting on chrome and IE with the same username. I do not want that to be possible
as an illustration (if needed) :
{ If9a9SgOoq7roW8Za84CouSEzgqDs1Q3:
{ cookie: { originalMaxAge: null, expires: null, httpOnly: true, path: '/' },
username: 'pseudo' },
MBs41iJmoQpLLCDhP8aFAk5PWAZ_ZQSV:
{ cookie: { originalMaxAge: null, expires: null, httpOnly: true, path: '/' },
username: 'pseudo' } }
app.post('/login', (req, res) => {
// You can store username in res.locals.username after auth
// It's not necessary to pass it into render.
let username = req.body.username;
if(!username)
return res.render('login', {error: 'username required nobi'});
if (username == req.session.username)
return res.redirect('/');
if (username && username != req.session.username)
return ... // maybe destroy current session?
req.sessionStore.all( (err, sessions) => {
if (err)
return res.render('login', {error: err.message});
let isUsed = Object.keys(sessions).some((id) => sessions[id].username == username);
if (isUsed)
return res.render('login', {error: 'Already used'});
req.session.username = username;
res.locals.username = username;
res.redirect('/');
});
});
exports.updateUsuarioByEmail = function (req, res) {
console.log('updateUsuarioByEmail');
console.log("PARAM ID" + req.params.email);
return Usuario.find({ email: req.params.email }, function(err, usuario) {
if(!usuario) {
res.statusCode = 404;
return res.send({ error: 'Not found' });
}
if (req.body.email != null) usuario.email = req.body.email;
if (req.body.password != null) usuario.password = req.body.password;
if (req.body.listaCardsSorting != null) usuario.listaCardsSorting = req.body.listaCardsSorting;
return usuario.save(function(err) {
if(!err) {
console.log('Updated usuario');
return res.send({ status: 'OK', usuario:usuario });
} else {
if(err.name == 'ValidationError') {
res.statusCode = 400;
res.send({ error: 'Validation error' });
} else {
res.statusCode = 500;
res.send({ error: 'Server error' });
}
console.log('Internal error(%d): %s',res.statusCode,err.message);
}
res.send(usuario);
});
});
};
The error after to execute is :
I believe that the error is the line "return usuario.save(function(err)..."
find return an Array (list of documents) . you can't do save on an array object. Instead try findOne if your email field is unique.
findOne returns a single document, so you can save that.
Replace
Usuario.find({ email: req.params.email }, function(err, usuario)
with :
Usuario.findOne({ email: req.params.email }, function(err, usuario)
To update a model it's much easier to use the findOneAndUpdate() update API. The method finds a matching document, updates it according to the update arg, passing any options, and returns the found document (if any) to the callback. The query executes immediately if callback is passed.
The syntax is:
Model#findOneAndUpdate([conditions], [doc], [options], [callback])
Parameters:
[conditions] <Object> - the query to match
[doc] <Object> - the document to update
[options] <Object> - update options
[callback] <Function> - callback
For example, the above function can be re-written to use the findOneAndUpdate() method as:
exports.updateUsuarioByEmail = function (req, res) {
console.log('updateUsuarioByEmail');
console.log("PARAM ID" + req.params.email);
var doc = {},
conditions = { "email": req.params.email },
options = { "new": true };
if (req.body.email != null) doc.email = req.body.email;
if (req.body.password != null) doc.password = req.body.password;
if (req.body.listaCardsSorting != null)
doc.listaCardsSorting = req.body.listaCardsSorting;
Usuario.findOneAndUpdate(
conditions,
doc,
options,
function(err, usuario) {
if(!err) {
console.log('Updated usuario');
return res.send({
status: 'OK',
usuario: usuario
});
} else {
if(err.name == 'ValidationError') {
res.statusCode = 400;
res.send({ error: 'Validation error' });
} else {
res.statusCode = 500;
res.send({ error: 'Server error' });
}
console.log('Internal error(%d): %s',res.statusCode,err.message);
}
}
)
};
Here remained the solution:
exports.updateUsuarioByEmail = function (req, res) {
console.log('updateUsuarioByEmail');
return Usuario.findOne({email: req.params.email}, function (err, usuario) {
usuario.email = req.body.email || usuario.email;
usuario.password = req.body.password || usuario.password;
usuario.listaCardsSorting = req.body.listaCardsSorting || usuario.listaCardsSorting;
return usuario.save(function (err) {
if (!err) {
console.log('Updated');
return res.send({status: 'OK', usuario: usuario});
} else {
if (err.name == 'ValidationError') {
res.statusCode = 400;
res.send({error: 'Validation error'});
} else {
res.statusCode = 500;
res.send({error: 'Server error'});
}
console.log('Internal error(%d): %s', res.statusCode, err.message);
}
res.send(usuario);
});
});
};