This is how I've designed my route and I'm not really happy with it. I'd rather be able to break down each hasValid into its own middleware, but I don't see how that would work since it wouldn't stop execution.
const secretSender = async (req, res, next) => {
if (!hasValidA(req)) {
return next()
}
if (!hasValidB(req)) {
return next()
}
if (!hasValidC(req)) {
return next()
}
if (!hasValidD(req)) {
return next()
}
res.send('something secret')
}
router.use(secretSender)
router.get('*', (req, res) => {
res.send('something public')
})
It also doesn't really make sense to make the default route "something secret" and have "something public" be the middleware since "something public" is the default behavior.
Here's one way to break down each hasValid into its own middleware. Each middleware will short-circuit the execution if the outcome is valid:
const validA = async (req, res, next) => {
if (hasValidA(req)) {
res.send('something secret')
} else {
next()
}
}
const validB = async (req, res, next) => {
if (hasValidB(req)) {
res.send('something secret')
} else {
next()
}
}
const validC = async (req, res, next) => {
if (hasValidC(req)) {
res.send('something secret')
} else {
next()
}
}
const validD = async (req, res, next) => {
if (hasValidD(req)) {
res.send('something secret')
} else {
next()
}
}
router.use(validA, validB, validC, validD)
router.get('*', (req, res) => {
res.send('something public')
})
Update
To achieve somewhat similiar result with OP's code, but only with single next():
const secretSender = async (req, res, next) => {
if (hasValidA(req) && hasValidB(req) && hasValidC(req) && hasValidD(req)) {
res.send('something secret')
} else {
next()
}
}
router.use(secretSender)
router.get('*', (req, res) => {
res.send('something public')
})
Related
currently I am making a notification system with node/express/hbs and mysql. I can insert notification to the database properly , but I can't fetch it my partial file.
here is my code
countNotification: async (req, res) => {
await Notification.count({ where: { status: 'unread' } }).then((notification) => {
if (notification) {
const dashNotification = req.notification;
return dashNotification;
}
});
},
app.use((req, res, next) => {
res.locals.user = req.user;
res.locals.notification = req.dashNotification;
next();
});
i have tried this like that but it is not working , anyone has any solution ?
You should use your middleware above the router:
app.use((req, res, next) => {
res.locals.user = req.user;
res.locals.notification = req.dashNotification;
next();
});
countNotification: async (req, res) => {
await Notification.count({ where: { status: 'unread' } }).then((notification) => {
if (notification) {
const dashNotification = req.notification;
return dashNotification;
}
});
},
How can i inject my middleware function 'checkAuthenticated' into my get route below?
not sure how to properly inject the code below. Please let me know. thank you very much.
function checkAuthenticated(req, res, next) {
if(!req.header('authorization')) {
return res.status(401).send({message: 'Unauthorized request. Missing authentication header'});
}
let token = req.header('authorization').split(' ')[1];
let payload = jwt.decode(token, '123');
if(!payload) {
return res.status(401).send({message: 'Unauthorized request. Authetication header invalid'});
}
req.user = payload;
next();
}
router.route('/:user_id')
.get((req, res) => {
User.findById(req.params.user_id, (err, user) => {
if (err) {
res.send(err);
} else {
res.json(user);
}
});
})
There are a few options here. I typically use:
router.use('*', checkAuthenticated);
Another option is:
router.get('/:user_id', checkAuthenticated, (req, res) => { ... })
Or, using your example of router.route...:
router.route('/:user_id').get(checkAuthenticated, (req, res) => { ... })
You can also chain them together:
router.route('/:user_id').get(checkAuthenticated).get((req, res) => { ... })
check this hope it will help you
router.route('/:user_id')
.all((req, res, next) => {
if (req.user) {
next();
} else {
res.redirect('/');
}
})
.get((req, res) => {
res.json(req.user);
});
I have a doubt about middelware in express.
I want to many thinks in one middleware. For example
I have this code y me middleware
module.exports = function(req,res,next) {
if(req.method === 'GET') {
res.end('GET method not supported');
} else {
next();
}
};
and I use it like this
app.route('/', <the_middleware>, (res, req, next) => {
// Code
})
But I am wondering if is possible to do something like this
app.route('/', <the_middleware>.<the function1>, (res, req, next) => {
// Code
})
app.route('/', <the_middleware>.<the_function2>, (res, req, next) => {
// Code
})
is there a possiblitity to do something like
function function1 (req,res,next) {
if(req.method === 'GET') {
res.end('GET method not supported');
} else {
next();
}
};
function function2 (req,res,next) {
if(req.method === 'GET') {
res.end('GET method not supported');
} else {
next();
}
};
module.exports = <I don`t know what go here>
Thanks.
Update. IT works, my code now is
The router
router.post('/', checkAuth.sayHi, checkAuth.sayBye, (req, res, next) => {
console.log('good');
res.status(200).send('it works');
console.log('yes');
});
The middleware
module.exports = {
sayHi(req, res, next) {
console.log('hi');
next();
},
sayBye(req, res, next) {
console.log('bye')
next();
}
};
You can just export an object containing both functions:
module.exports = {
function1,
function2
}
I am following a middleware chaining example from this question.
I have a route app.put('/users/:id', isAuthenticated, (req, res) => {db.updateUser(req.params.id, req.body)}. I am trying to write a middleware function that verifies that the ID provided in the URL matches the ID retrieved from the JWT included with the request.
I already have a function isAuthenticated that verifies the JWT and sets res.locals.userId to the UID retrieved; so I would like to simply make use of that in this new function canModifyTarget but for some reason the request hangs forever:
// This function works fine
isAuthenticated: function(req, res, next) {
let token;
if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
token = req.headers.authorization.split(' ')[1];
admin.auth().verifyIdToken(token).then((decodedToken) => {
res.locals.userId = decodedToken.uid;
return next();
}).catch((error) => {
return res.status(HttpStatus.UNAUTHORIZED).send();
})
}
}
// Switching out isAuthenticated for this in the route causes a permanent hang
canModifyTarget: function(req, res, next) {
console.log('This is printed');
return (req, res, next) => {
console.log('This is NOT printed');
isAuthenticated(req, res, () => {
if (req.params.id === res.locals.userId) {
return next();
}
return res.status(HttpStatus.FORBIDDEN).send();
})
}
}
middlewares should be callback functions that call "next()" once finished.
Your first function, when executed, is calling next() (eventually, after your promise is resolved)
Your second function isn't calling next(), it is just returning a function definition.
Define it like this
canModifyTarget: function(req, res, next) {
isAuthenticated(req, res, () => {
if (req.params.id === res.locals.userId) {
return next();
}
return res.status(HttpStatus.FORBIDDEN).send();
})
}
}
and if the third parameter of isAuthenticated is a callback, it should work
Also, you should define an "else" case in your isAuthenticated function, otherwise it will hang as well (maybe throw an exception or something?)
If you need to reference them, store them in variables rather than directly defining them in your module.exports:
const isAuthenticated = function(req, res, next) {
// code here
}
const canModifyTarget: function(req, res, next) {
// code here
}
module.exports = {
isAuthenticated,
canModifyTarget,
};
I think simpler is to define canModifyTarget as one more middleware. I.e:
function canModifyTarget(req, res, next) {
console.log('This is NOT printed');
if (req.params.id === res.locals.userId) {
return next();
}
return res.status(HttpStatus.FORBIDDEN).send();
}
and then just apply it after isAuthenticated middleware:
app.put(
'/users/:id',
isAuthenticated,
canModifyTarget,
(req, res) => {db.updateUser(req.params.id, req.body)}
);
Hope it helps.
I am just writing a solution where I needed to unify two kind of auth middlewares: password-based and apikey-based into one middleware: unifiedOrgAuth middleware.
So, basically this would enable me to just put unifiedOrgAuth middleware on those routes which need either the password-based or apikey-based auth.
The key thing was to pass the next function from the umbrella middleware to the underlying middleware by just calling the underlying middleware with the next function of the umbrella middleware:
unified auth middleware:
function unifiedOrgAuthMiddleware(
path: string,
perm: Permission
): express.RequestHandler {
return async (req: RequestWithOrg, _res: Response, next: NextFunction) => {
const cookies = req.cookies;
if (cookies && cookies.Authorization) {
(userAuthMiddleware(path, perm))(req, _res, next);
return;
}
const apiKey = req.header('X-API-KEY');
if (apiKey && apiKey.length > 0) {
(apiAuthMiddleware(path, perm))(req, _res, next);
return;
}
return next(new Error401Exception());
// Make linter happy.
};
}
Here are the underlying middlewares:
password-based auth middleware:
function userAuthMiddleware(
path: string,
perm: Permission
): express.RequestHandler {
return async (req, _res, next) => {
try {
const cookies = req.cookies;
if (!(cookies && cookies.Authorization)) {
next(new Error401Exception());
// Make linter happy.
return;
}
if (!validCookies(cookies)) {
next(new Error401Exception());
// Make linter happy.
return;
}
} catch (error) {
next(new Error401Exception());
// Make linter happy.
return;
}
next();
};
}
api-based auth middleware:
function apiAuthMiddleware(
path: string,
perm: Permission
): express.RequestHandler {
return async (req: RequestWithOrg, _res: Response, next: NextFunction) => {
const apiKey = req.header('X-API-KEY');
if (!apiKey) {
next(new Error401Exception());
// Make linter happy.
return;
}
if (!validApiKey(apiKey)) {
next(new Error401Exception());
// Make linter happy.
return;
}
next();
};
}
I don't understand why urls starting with 2016 don't work. It just loads forever.
main (/), admin (/admin/*) and /home work without an issue.
function stringStartsWith(string, prefix) {
return string.slice(0, prefix.length) == prefix;
}
router.get('/', csrfProtection, indexOnly, indexController.index);
router.get('/admin', adminOnly, adminController.index);
router.all('/*', function(req, res, next) {
if (req.originalUrl == '/home') {
next();
} else if (stringStartsWith(req.originalUrl, "/admin")) {
router.all('/admin/*', function(req, res, next) {
if (req.originalUrl == '/admin') {
next(); // it doesn't do anything, just allows the route above to work (admin welcome page.)
} else {
res.sendFile(path.resolve('views/backend/index.html'));
}
});
} else if (stringStartsWith(req.originalUrl, "/2016")) {
router.all('/2016/*', function(req, res, next) {
res.sendFile(path.resolve('views/frontend/index/index.html'));
});
} else {
res.sendFile(path.resolve('views/frontend/index.html'));
}
});
why do you put the 2016 route inside other route? it should simply be another route like the others:
function stringStartsWith(string, prefix) {
return string.slice(0, prefix.length) == prefix;
}
router.get('/', csrfProtection, indexOnly, indexController.index);
router.get('/admin', adminOnly, adminController.index);
router.all('/2016/*', function(req, res, next) {
res.sendFile(path.resolve('views/frontend/index/index.html'));
});
router.all('/*', function(req, res, next) {
if (req.originalUrl == '/home') {
next();
} else if (stringStartsWith(req.originalUrl, "/admin")) {
router.all('/admin/*', function(req, res, next) {
if (req.originalUrl == '/admin') {
next(); // it doesn't do anything, just allows the route above to work (admin welcome page.)
} else {
res.sendFile(path.resolve('views/backend/index.html'));
}
});
} else {
res.sendFile(path.resolve('views/frontend/index.html'));
}
});