express-validator not producing validation errors - node.js

I am facing issues while trying express-validator v4.3.0.
There is one basic example of login with post request. There are two parameters email and password. Below is my code.
routes.js file:
var express = require('express');
var router = express.Router();
var validator = require('./validator');
router.post('/login', [sanitize('email').trim(),
validator.publicRouteValidate('login')], (req, res, next) => {
console.log(req);
}
validator.js file:
'use strict';
const { check, validationResult } = require('express-validator/check');
const { matchedData, sanitize } = require('express-validator/filter');
module.exports = {
publicRouteValidate: function (method) {
return (req, res, next) => {
switch (method) {
case 'login':
check('email').isEmail().withMessage('email must be an email')
.isLength({min: 109}).withMessage('minimum length should be 100 characters')
break;
}
var errors = validationResult(req)
console.log(errors.mapped())
if (!errors.isEmpty()) {
res.status(422).json({ errors: errors.array()[0].msg })
} else {
res.send('login')
}
}}}
Now when I am doing POST request with only email and value of email is abc. So I should get the error like email must be an email. But I did not get any response. So What is the issue I do not know?

Your publicRouteValidate function is only creating a validator, but it is never being called.
This happens because, as documented, check returns an express middleware. Such middleware must be given to an express route in order for it to do its work.
I recommend you to break that function in two: one that creates your validators, and another that checks the request for validation errors, returning earlier before touching your route.
router.post(
'/login',
createValidationFor('login'),
checkValidationResult,
(req, res, next) => {
res.json({ allGood: true });
}
);
function createValidationFor(route) {
switch (route) {
case 'login':
return [
check('email').isEmail().withMessage('must be an email'),
check('password').not().isEmpty().withMessage('some message')
];
default:
return [];
}
}
function checkValidationResult(req, res, next) {
const result = validationResult(req);
if (result.isEmpty()) {
return next();
}
res.status(422).json({ errors: result.array() });
}

Related

Im trying to update a user in MongoDB but it's giving me an error "You are not authenticated" why is that?

Im creating a video sharing app that allows users to sign in and update their profiles. i've created an error handler to identify errors in the backend and when im submitting my put request to update a user its going off.
here are my routes that im using
import express from 'express';
import { update} from '../controllers/user.js'
import { verifyToken } from '../verifyToken.js';
const router = express.Router();
router.put("/:id", verifyToken, update);
here is the controller for updating the user
import { createError } from "../error.js";
import User from '../models/User.js'
export const update = async (req, res, next) => {
if(req.params.id === req.user.id) {
try {
const updatedUser = await User.findByIdAndUpdate(req.params.id, {
$set: req.body
}, {new: true})
res.status(200).json(updatedUser)
} catch (error) {
next(error)
}
} else {
return next(createError(403, 'You can only update this account'));
}
}
here is the custom error handling im using which is in the index.js below and error.js
app.use((err, req, res, next) => {
const status = err.status || 500
const message = err.message || "something went wrong"
return res.status(status).json({
success: false,
status,
message
})
})
export const createError = (status, message) => {
const err = new Error()
err.status=status
err.message=message
return err
}
here is the middleware for the token
import jwt from 'jsonwebtoken'
import { createError } from './error.js'
export const verifyToken = (req, res, next) => {
const token = req.cookies.acess_token
if(!token) return next(createError(401, 'You are not authenticated'))
jwt.verify(token, process.env.JWT, (err, user)=>{
if(err) return next(createError(403, "Token invalid"))
req.user = user
next()
})
}

Login route looks for /:id middleware in Nodejs

I have a middleware function for my post route /blog that prevents users not logged in to add blogs to the site, below is the function:
app.use('/blog',(req,res,next) => {
if ( req.path == '/blog/login') next();
if (!req.session.user){
res.status(403)
res.send('Please login to add blog')
return
}
else{
next()
}
})
I have a login route that looks like this:
router.post('/blog/login', async (req, res) => {
let { username,password } = req.body;
try{
checkString(username);
checkString(password);
username = username.toLowerCase();
if (!username.match(/^[a-zA-Z0-9!##\$%\^\&*\)\(+=._-]+$/g)) throw 'Only alphanumeric characters allowed for username'
if(!password.match(/^[a-zA-Z0-9!##\$%\^\&*\)\(+=._-]+$/g)) throw 'Only alphanumeric and special characters allowed for password'
if (username.length<4) throw 'Username should have 4 characters or more';
if (password.length<6) throw 'Password should have 6 characters or more';
const ans = await userData.checkUser(username,password)
if (ans==1){
const [id,user,name,pass] = await userData.getValues(username);
req.session.user = { username:user, name:name, password:pass, userId:id };
res.send({authenticated:true})
}
} catch(e){
res.send(400,{msg:e})
}
});
Below is the route that I want to be able to run without going to the middleware:
router.get('/blog/:id', async (req, res) => {
try{
const blog = await blogData.getOneBlog(req.params.id);
res.send(blog)
}
catch{
res.sendStatus(500);
}
});
My index.js:
const blogRoutes = require('./blogs');
const constructorMethod = (app) => {
app.use('/blog', blogRoutes);
app.use('*', (req, res) => {
res.sendStatus(404);
});
};
module.exports = constructorMethod;
The above route goes directly to the middleware because anything I call after the /blog sends it to the middleware but I only want to send the /blog/:id to the middleware.

Handle exceptions on every Express route

I would like some basic error handling on every route, so if there is ever an exception, the API at least responds with 500.
According to this pattern, you still need to include a try/catch block in every route:
app.post('/post', async (req, res, next) => {
const { title, author } = req.body;
try {
if (!title || !author) {
throw new BadRequest('Missing required fields: title or author');
}
const post = await db.post.insert({ title, author });
res.json(post);
} catch (err) {
next(err) // passed to the error-handling middleware
}
});
That seems a little repetitive. Is there a higher-level way where exceptions are automatically caught everywhere and passed to the middleware?
I mean, it would obviously be possible for me to just define my own appGet():
function appGet(route, cb) {
app.get(route, async (req, res, next) => {
try {
await cb(req, res, next);
} catch (e) {
next(e);
}
});
}
Is there some built in version of this?
You can use express-promise-router package.
A simple wrapper for Express 4's Router that allows middleware to return promises. This package makes it simpler to write route handlers for Express when dealing with promises by reducing duplicate code.
E.g.
app.ts:
import express from 'express';
import Router from 'express-promise-router';
import bodyParser from 'body-parser';
const router = Router();
const app = express();
const port = 3000;
app.use(bodyParser.json());
app.use(router);
router.post('/post', async (req, res) => {
const { title, author } = req.body;
if (!title || !author) {
throw new Error('Missing required fields: title or author');
}
const post = { title, author };
res.json(post);
});
router.use((err, req, res, next) => {
res.status(500).send(err.message);
});
app.listen(port, () => console.log(`Server started at http://localhost:${port}`));
You don't need try/catch statement block anymore.
Test result:
I think the better approach would be to divide the services and the controllers which is demonstrated below.
Add post service:
async function addPostService (title, author) => {
if (!title || !author)
throw new BadRequest('Missing required fields: title or author');
return await db.post.insert({ title, author });
};
Add post controller:
function addPost(req, res, next){
const { title, author }= req.body;
addPostService
.then((post) => {
res.json(post);
})
.catch(next) // will go through global error handler middleware
}
Now, we can make a global error handler middleware which will catch the error thrown by any controller throughout the app.
function globalErrorHandler(err, req, res, next){
switch(true){
case typeof err === 'string':
// works for any errors thrown directly
// eg: throw 'Some error occured!';
return res.status(404).json({ message: 'Error: Not found!'});
// our custom error
case err.name = 'BadRequest':
return res.status(400).json({ message: 'Missing required fields: title or author!'})
default:
return res.status(500).json({ message: err.message });
}
}
And, don't forget to use the error handler middleware right before starting the server.
// ....
app.use(globalErrorHandler);
app.listen(port, () => { console.log('Server started...')});

Return middleware from middleware

I am using express-validator and would like to have different checks based on a value in the request body.
I have created a function for this, but I am not getting any responses back (i.e. express just hangs.):
validation/profile.js
module.exports = function (req,res,next) {
if (req.body.type == 'teacher') {
return check('name').exists().withMessage('Name is required'),
} else {
return check('student_id').exists().withMessage('Student id is required'),
}
}
app.js
router.put('/', require('./validation/profile'), (req, res, next) => {
const errors = validationResult(req).formatWith(errorFormatter)
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.mapped() })
} else {
res.send(req.user)
}
})
If however, I write my function as a normal function (not as middleware with 3 params) and call it, it all works. But this way, I won't have access to the request object. I have to "hard-code" the params.
validation/profile.js
module.exports = function (type) {
if (type == 'teacher') {
return check('name').exists().withMessage('Name is required'),
} else {
return check('student_id').exists().withMessage('Student id is required'),
}
}
app.js
router.put('/', require('./validation/profile')('teacher'), (req, res, next) => {
const errors = validationResult(req).formatWith(errorFormatter)
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.mapped() })
} else {
res.send(req.user)
}
})
Any suggestions on how could I achieve having different checks based on a value in the request body?
The express-validator check API creates the middleware, you should attach it to express directly or call it yourself as express would.
// Use routers so multiple checks can be attached to them.
const teacherChecks = express.Router();
teacherChecks.use(check('name').exists().withMessage('Name is required'));
const studentChecks = express.Router();
studentChecks .use(check('student_id').exists().withMessage('Student id is required'));
module.exports = function (req,res,next) {
if (req.body.type == 'teacher') {
teacherChecks(req, res, next);
} else {
studentChecks(req, res, next);
}
}
You could also potentially use oneOf to do the same thing.
router.put('/', oneOf([
check('name').exists().withMessage('Name is required'),
check('student_id').exists().withMessage('Student id is required')
], 'Invalid request body'), (req, res, next) => {
const errors = validationResult(req).formatWith(errorFormatter)
if (
!errors.isEmpty()
) {
return res.status(422).json({errors: errors.mapped()})
}
else {
res.send(req.user)
}
});

Testing Express / Passport middleware using Jasmine — passport.authenticate never completes

I'm trying to unit test a simple piece of Express middleware, a cascading athenticator that checks first for a JWT token using a passport-jwt-strategy, and then if that fails, using a passport-openid-strategy. Each of the strategies is already well tested so what I am trying to test is their integration.
The module I am testing looks like this:
"use strict";
let passport = require('passport');
let Strategies = require('./strategies');
let setupDone = false;
// set up passport
let setup = function (app) {
passport.serializeUser(function (user, done) {
done(null, user);
});
passport.deserializeUser(function (obj, done) {
done(null, obj);
});
passport.use('jwt', Strategies.jwt);
passport.use('openid', Strategies.openId);
app.use(passport.initialize());
app.use(passport.session());
setupDone = true;
};
let authenticate = function (req, res, next) {
if (!setupDone) throw new Error('You must have run setup(app) before you can use the middleware');
console.log(' cascadingAuthentication');
// first try the token option
passport.authenticate('jwt', function (jwterr, user, info) {
console.log(' jwt auth', jwterr, user, info);
if (jwterr || !user) {
passport.authenticate('openid', function (oautherr, user, info) {
if (oautherr || !user) {
return next(oautherr);
} else {
next();
}
});
} else {
req.user = user;
next();
}
});
};
module.exports = {
setup: setup,
authenticate: authenticate
}
My Jasmine test looks like this
"use strict";
let CascadingAuthentication = require('../../lib/middleware/cascadingAuthentication');
let TokenUtils = require('../support/tokenUtils');
let email = 'testing#test.tes';
describe('cascadingAuthentication', function () {
describe('when there is a token in the header', function () {
let req;
let res = {};
let app = {
use: function (used) { console.log('app.use called with', typeof used); }
};
beforeEach(function (done) {
let token = TokenUtils.makeJWT(email);
req = {
app: app,
header: {
Authorization: `Bearer ${token}`
}
}
CascadingAuthentication.setup(app);
CascadingAuthentication.authenticate(req, res, function () {
done();
});
});
it('populates req.user', function () {
expect(req.user).toEqual(jasmine.any(Object));
});
});
});
The issue I have is that, when I run the test, I see the first console.log(' cascadingAuthentication') but I never see the second console.log('jwt auth', err, user, info). The code just dies inside passport.authenticate without ever calling the callback, without raising an error, or without providing any kind of feedback at all.
I'm running my tests via gulp using Jasmine.
My questions are: in order,
Can you see anything obvious that I have done that I might have just missed?
Is there anything else I ought to mock out in my req, res, or app that might make this test work?
Is there any way to debug this interactively; stepping through the code under test as it runs, rather than just adding console.log statements (which seems a little 1980s to me).
Digging through passport's source I have worked out there were two problems with my code.
The first is that passport.authenticate returns a middleware function, it doesn't actually execute that function. So the solution was simply to call the returned function.
So my authenticate method now looks like:
let authenticate = function(req, res, next) {
if (!setupDone) throw new Error('You must have run setup(app) before you can use the middleware');
// first try the token option
passport.authenticate('jwt', function(jwterr, user, info) {
if (jwterr || !user) {
passport.authenticate('openid', function(autherr, user, info) {
if (autherr || !user) {
return next(autherr);
} else {
next();
}
})(req, res, next);
} else {
req.user = user;
next();
}
})(req, res, next);
};
(The above example is trimmed for use in the question)
The other issue was in my test I used header instead of headers in my mock req object, and also authorization ought to have had a lower case a.
With those two fixes the test now passes.
I fiddled with this for quite some time and eventually landed on the following setup (to test passport.authenticate('local', () => {})).
auth-router.js
const express = require('express');
const passport = require('passport');
const login = (req, res, next) => {
passport.authenticate('local', (err, user, info) => {
if (err) {
next(err);
return;
}
if (!user) {
const error = new Error(info.message);
error.status = 404;
next(error);
return;
}
// Add the found user record to the request to
// allow other middlewares to access it.
req.user = user;
next();
})(req, res, next);
};
const router = express.Router();
router.post('/auth/login', login);
module.exports = {
login,
router
};
auth-router.spec.js
const passport = require('passport');
describe('login', () => {
it('should login and add the user to the request object', (done) => {
spyOn(passport, 'authenticate').and.callFake((strategy, callback) => {
const err = null;
const user = {};
const info = {};
callback(err, user, info);
return (req, res, next) => {};
});
const auth = require('./auth'); // my middleware function
const req = { body: {} };
const res = {};
const next = () => {
expect(req.user).toBeDefined();
done();
};
auth.login(req, res, next);
});
});

Resources