the correct way using middleware in express - javascript

hey I want to make sure if I use the correct way for middleware in my simple express app, I am trying to find the email unique for register
here is my example
const isUnique = (req, res, next) => {
User.findOne({
where:{
email: req.body.email
}
})
.then(getUser => {
if(getUser){
next("/userAlreadyExist") // router
// or can i render to to html? i am using ejs
} else {
next()
}
})
.catch(next())
}
app.post('/register', isUnique ,(req, res) => {
res.send(`thank you for register`)
}
I want to make sure the email already exists or no, so I want to pass it on middleware first, and get a page for isUnique, if the email already in use, I want to redirect it to next router called '/emailExist', and if it success i want to redirect it to router /success
can anyone help me if that code wrong or no? just want to make sure :D

You have a lot of options, here are a couple.
You can redirect users to specific pages based on whether or not the email exists. Within your /emailAlreadyExists and /registerSuccess routes you can render whatever templates you want or return some data.
const isUnique = (req, res, next) => {
User.findOne({
where:{
email: req.body.email
}
})
.then(getUser => {
if (getUser) {
res.redirect('/emailAlreadyExists');
} else {
res.redirect('/registerSuccess'); // or just call next()
}
})
.catch(next("DB error"));
}
Pass along the results of the db query and let your final middleware handle it:
const isUnique = (req, res, next) => {
User.findOne({
where:{
email: req.body.email
}
})
.then(getUser => {
req.user = getUser;
next();
})
.catch(next());
}
app.post('/register', isUnique ,(req, res) => {
if (req.user) {
res.send('User already exists');
} else {
res.send(`thank you for register`);
}
}
You can also create an error handling middleware:
const isUnique = (req, res, next) => {
User.findOne({
where:{
email: req.body.email
}
})
.then(getUser => {
if(getUser){
next("Error: user already exists"); // or some other error message/object
} else {
next(); // continue to next middleware
}
})
.catch(next("DB error")); // handle errors throw from DB read
}
app.post('/register', isUnique ,(req, res) => {
res.send(`thank you for register`)
}
/*
If you call "next" with an argument, Express will skip
straight to this error handler route with the argument
passed as the "err" parameter
*/
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send(`An error occurred: ${err}`);
})

Related

How to get session variable in app.post request?

I would like to get the data from session variable (req.user.username) then use it for posting. I'm using passportjs as authentication. I'm using router. Here is my code:
router.use('/login', passport.authenticate("local-register", async (err, user, info) => {
if (err) {
return next('Error');
}
if (!user) {
return next('Error');
}
req.user = user;
return req.login(user, (error: Error) => {
if (error) {
return next('Error');
}
return req.session.save((erro: Error) => {
if (erro) {
return next('Error');
}
return next();
});
});
})(req, res, next);)
router.get('/', async (req, res) => {
console.log(req.user.username) // working just fine
});
router.post('/upload', async (req, res) => {
const uploaderName = req.user.username // I'm getting undefined
const upload = await database.query('INSERT INTO user WHERE username=$1', [uploaderName])
console.log(uploaderName);
})
So I finally found the answer to the question. For those who will encounter the problem in the future. You just add the session middleware AGAIN on the top of the routes. If your routes are separated to the main server file.
/src/routes/routes.ts -> add again the middleware on top.
const app = router();
app.use(sessions) // -> right here you need to add the middleware again to //access the req.user session variable
app.get('/', async (req, res) => {
console.log(req.user.username) // working just fine
});
app.post('/upload', async (req, res) => {
const uploaderName = req.user.username // I'm getting undefined
const upload = await database.query('INSERT INTO user WHERE username=$1', [uploaderName])
console.log(uploaderName);
})

Passport.js authentication middleware with Node.js

I'm trying to make secure routers by using jsonwebtoken on Node.js server.
And I'm using passport.js to authenticate user with JWT.
At first, I put all logics in controller.
But all secure routers need to check authentication, so I tried to divide the authenticate part as a middleware
Before
user.controller.js
/**
* GET /user
* Get user data
*/
exports.getUser = (req, res, next) => {
passport.authenticate("jwt", { session: false }, (err, payload, info) => {
if (err) return next(err);
if (!payload) return next(info);
User.findOne({ email: payload.email }, (err, user) => {
if (err) return next(err);
if (!user) return next("no matching user found");
res.status(200).send({ email: user.email });
});
})(req, res, next);
};
app.js
const userController = require('user.controller.js');
app.get('/user', userController.getUser);
After
passport.js
/**
* Check authentication
*/
exports.checkAuth = (req, res, next) => {
passport.authenticate("jwt", { session: false }, (err, payload, info) => {
if (err) return next(err);
if (!payload) return next(info);
req.user = payload;
next();
})(req, res, next);
};
user.controller.js
/**
* GET /user
* Get user data
*/
exports.getUser = (req, res, next) => {
User.findOne({ email: req.user.email }, (err, user) => {
if (err) return next(err);
if (!user) return next("no matching user found");
res.status(200).send({ email: user.email });
});
};
app.js
const passportConfig = require('passport.js');
const userController = require('user.controller.js');
app.get('/user', passportConfig.checkAuth, userController.getUser);
In original user.controller.js I could get email from payload.email.
BUT after I divided the original file, I cannot access the email value at user.controller.js.
So I searched some ways how to pass data from one middleware to another, and used req.user.
Question
Is this correct structure to authenticate with jwt, passport.js?
Is this correct way to pass data between middlewares? or is there any better way?
This is a good practice to use req to pass data from middleware to others.
By the way, you shouldn't call by yourself next() from passport custom callback (this is not a middleware). Passport will do next middleware call himself in case token is valid.
/**
* Check authentication
*/
exports.checkAuth = (req, res, next) => {
passport.authenticate("jwt", { session: false }, (err, payload, info) => {
if (err) return next(err);
if (!payload) return next(new Error('wrong to'));
//next()
})(req, res, next);
};
From your "Before" step, there is a reason to use a custom callback because you check user email existence from it.
But from your "After" step, the user check logic has moved into another middleware. So you can just use passport default middleware.
exports.checkAuth = passport.authenticate("jwt", { session: false });
And then user.controller.js will be called with the token data bind to req.user in case token is validated by Passport.
At this moment, you can proceed to email verification.
Controller function is executing before middleware check, update your middleware like below using Promisify doc
const util = require('util');
const authenticate = util.promisify(passport.authenticate);
exports.checkAuth = async (req, res, next) => {
// passport.authenticate("jwt", { session: false }, (err, payload, info) => {
// if (err) return next(err);
// if (!payload) return next(info);
// req.user = payload;
// next();
// })(req, res, next);
try {
const payload = await authenticate("jwt", { session: false });
req.user = payload;
//Do something
next();
} catch (error) {
}
};

Middleware calls in express not stopped after returning inside one of them

I am having issues with middleware in my express app, I have the following route:
app.post(
'/api/auth/signup',
[
verifySignUp.checkDuplicateUsernameOrEmail,
verifySignUp.checkRolesExist
],
controller.signup
);
There are the two middleware checkDuplicateUsernameOrEmail and checkRolesExist as follows:
const checkDuplicateUsernameOrEmail = (req, res, next) => {
console.log('checkDuplicateUsernameOrEmail');
User.findOne({username: req.body.username}).exec()
.then(user => {
if (user) {
console.log("user name exists");
return fail(res, {message: 'This username already exists'});
}
return User.findOne({email: req.body.email}).exec()
})
.then(user => {
if (user) {
return fail(res, {message: 'This email already exists'});
}
next();
})
.catch(error => {
console.log(error);
fail(res, {message: 'Database internal error occured.'});
});
};
const checkRolesExist = (req, res, next) => {
console.log('checkRolesExist');
console.log(req.body.roles);
for (const role of req.body.roles) {
if (!ROLES.includes(role)) {
return fail(res, {message: `${role} is not a valid role`});
}
}
next();
};
const fail = (res, err) => {
const message = err.message || 'Encountered a server error';
const status = err.status || 500;
res.status(status).json({status, message});
}
I make a request with a username that has already been used before and in the console I get user name exists as expected however the app goes on to call checkRolesExist, shouldn't execution of middleware stop when it hits a return? What am I doing wrong where?
That's because return fail returns to... nothing. you're returning stuff inside the then() callback function, not inside checkDuplicateUsernameOrEmail(). So, the execution keeps going, and you hit the next .then().
Go async/await style, it will make your life easier :
const checkDuplicateUsernameOrEmail = async(req, res, next) => {
console.log('checkDuplicateUsernameOrEmail');
try {
if ( await User.findOne({ username: req.body.username }).exec() ) {
console.log("user name exists");
return fail(res, { message: 'This username already exists' });
}
if ( await User.findOne({ email: req.body.email }).exec()) {
return fail(res, { message: 'This email already exists' });
}
next();
} catch (error) {
console.log(error);
fail(res, { message: 'Database internal error occured.' });
}
};
If you want them to run in the specific order that checkRolesExist is not fired unless the first passes, then do this:
app.post(
'/api/auth/signup',
verifySignUp.checkDuplicateUsernameOrEmail,
verifySignUp.checkRolesExist,
controller.signup
);

How to turn an export.signup and export.signin into a module.exports?

I'm developing a module which I'm using for passport authentication with ExpressJS, and I came up with this solution to gather all the passports methods I'm using:
// passport-controller-js
exports.signup = (passport) => (req, res, next) => {
// Authenticate methods ================
passport.authenticate('local-signup', function(err, user, info) {
if (err) {
return next(err); // will generate a 500 error
}
// Saving user...
return res.send({ success : true, message : 'signup succeeded' });
})(req, res, next);
};
exports.signin = (passport) => (req, res, next) => {
passport.authenticate('local-login', function(err, user, token, info) {
if (err) {
return next(err); // will generate a 500 error
}
req.login(user, loginErr => {
if (loginErr) {
return next(loginErr);
}
return res.send({ success : true, message : 'signin succeeded' });
});
})(req, res, next);
};
But since this module will increase adding more strategies I'm thinking if there is a way to put all of them inside a module.exports like:
module.exports = (passport) => {
function signin(req, res, next) {
passport.authenticate('local-login', function(err, user, token, info) {
if (err) {
return next(err); // will generate a 500 error
}
req.login(user, loginErr => {
if (loginErr) {
return next(loginErr);
}
return res.send({ success : true, message : 'signin succeeded' });
});
})(req, res, next);
};
I know that dosn't work Im just wondering if there is a possible solution like that so when I need to require those methods on my router file for example I can do this:
// auth.js (passport is passed from index.js)
const passportController = require('../controllers/passport-controller')(passport);
// Process the signup form
router.post('/signup', passportController.signup);
router.post('/signin', passportController.signin);
Instead of:
// auth.js (passport is passed from index.js)
const passportController = require('../controllers/passport-controller');
// Process the signup form
router.post('/signup', passportController.signup(passport));
router.post('/signin', passportController.signin(passport));
As you can see is just matter of looking for the most legible way to code and keep it simpler as possible.
Any help would be appreaciated, thank you very much.
Sounds like you want to make a module that exports a single function which returns an object. The syntax for that is
module.exports = passport => ({
signup(req, res, next) {
…
},
signin(req, res, next) {
…
}
});

Node.js async consistency

I have the following code :
server.use(function(req, res, next) {
users_db.set(req.user, function(err) { // async call to mongodb
if (err) {
console.error(err);
}
});
}
return next();
});
server.get('/', function(req, res) {
req.user.active = true; // this is a new field in user object
res.send(req.user);
}
});
So, As you see, when users_db.set() is called, req.user doesn't have the active=true field. It is being inserted only in the server.get() function.
Is it possible that user.active = true is registered in the db nevertheless because of the asynchronous nature of the call ?
As far as I know (it is like that in Express at least) .get method accepts many middleware functions. So I guess that the following will work:
server.get(
'/',
function(req, res, next) {
req.user.active = true; // this is a new field in user object
res.send(req.user);
next();
},
function(req, res, next) {
users_db.set(req.user, function(err) { // async call to mongodb
if (err) {
console.error(err);
}
});
}
return next();
}
);
Doing the things like that you are sure that req.user.active is populated always before to reach the moment with users_db.set.

Categories