I am trying to implement a middleware that will check if a user is authenticated before the server delivers a page. Although it looks like the process of doing this is simple, node is throwing an error which says "Can't set headers after they are sent".
My router's code is:
module.exports = function(app) {
app.get('/', checkAuth, require('./myAuthenticatedPage').get);
app.get('/login', require('./myLoginPage').get);
};
The myAuthenticatedPage.js:
exports.get = function(req, res) {
res.render('index');
};
The myLoginPage.js:
exports.get = function(req, res) {
res.render('login');
};
The checkAuth.js:
module.exports = function (req, res, next) {
if(!req.session.user) {
res.redirect('/login');
}
next();
}
Any help on this will be greatly appreciated.
Thanks
If you aren't authenticated, you'll redirect the user and then try to render the index page. This causes the http headers to be sent twice, hence the error "Can't set headers after they are sent".
In checkAuth.js try:
module.exports = function (req, res, next) {
if(!req.session.user) {
res.redirect('/login');
} else {
next();
}
}
Related
This part of code handles the login authorization routing in my app
const express = require("express");
const authController = require('../controllers/authController');
const indexController = require('../controllers/indexController');
const router = express.Router();
router.use("/login", (req, res, next) => {
if(req.session.loggedIn) {
res.redirect('/account');
}
next();
});
router.use("/account", (req, res, next) => {
if(!req.session.loggedIn) {
res.redirect('/login');
}
next();
});
router.get('/', (req, res) => {
res.redirect('/login');
});
router.get('/login', (req, res) => {
res.render('login');
});
router.get('/log-out', authController.logOut);
router.get("/account", indexController.getAccountData);
module.exports = router;
There were no problems and it was working fine till recent days.
I haven't change anything in this file nor authController nor indexController.
When I make a change (in other parts), nodemon restarts the app and I automatically jump to login page cause obviously all sessions are destroyed. But I get an error in getAccountData function (Error says req.session is undefined).
As you can see there's no way for the app to reach that function with no sessions set.
I have to restart the app again to act correct.
The session will be cleared each time the server restarts. So to escape from that you need to save the session to database. If you are using mongodb or I can give you example using mongodb.
import MongoStore from "connect-mongo";
import session from "express-session";
app.use(
session({
store: MongoStore.create({
mongoUrl: process.env.MONGODB_URI || "mongodb://localhost:27017/project",
}),
secret: "secret key",
cookie: { maxAge: sessionExpireInMilliseconds },
})
);
req.session.loggedIn would error out whenever session is undefined. You need to check if it is defined before trying to access loggedIn.
Try:
router.use("/login", (req, res, next) => {
if(req.session && req.session.loggedIn) {
res.redirect('/account');
}
next();
});
router.use("/account", (req, res, next) => {
if(!req.session || !req.session.loggedIn) {
res.redirect('/login');
}
next();
});
I'm trying to run a middleware inside another middleware. When I call the nested middleware, I get an error saying:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
Here's my code:
// middleware1
module.exports = function(req, res, next) {
req.user = 'current user';
next();
};
// middleware2
module.exports = function(req, res, next) {
middleware1(req, res, next);
if (req.user !== 'current user') return res.status(403).send('Access denied');
next();
};
// API
router.get('/api', middleware2, async (req, res) => {
return res.send(req.user);
});
What am I doing wrong, and what's the correct way to chain or nest a middleware into another one?
(The web app has more to it, but I only included whatever is necessary to reproduce the error)
Following on #jknotek comments:
Since you are passing the next function from middleware2 to middleware1, you are risking that the final middleware gets called in the part of the call stack, which triggers res.send. Afterwards it proceeds in middleware2, which tries to do a res.status, which will fail.
Either you want to chain middlewares as:
router.get('/api', middleware1, middleware2, [...])
Or you would want your middleware2 to behave somewhat like:
//middleware2
module.exports = function(req, res, next) {
middleware1(req, res, () =>
if (req.user !== 'current user') return res.status(403).send('Access denied');
next();
);
};
when dealing with routes, I want to check if the route is valid. If not, I always want to redirect to a notFound page.
If the user is not authorized I always want to redirect to the login page.
Otherwise I want to use the valid routes.
In my app.js I require my router.js and pass in the app as a parameter
require('./server/router')(app);
So my router works fine when having
module.exports = function(app){
app.use('/route1', require('./routes/route1'));
app.use('/route2', require('./routes/route2'));
app.use('/route3', require('./routes/route3'));
};
in there. When using this structure
module.exports = function(){
var router = require('express').Router();
router.use('/route1', require('./routes/route1'));
};
it results in
Cannot GET /route1
All my routes contain this base structure
var router = require('express').Router();
// -- Route --
router.get('/', function (req, res) { // Render the HTML here
res.render('route1', {
});
});
// -- Ajax POST --
router.post('/doSomething', function (req, res) { // Load some data
res.send({});
});
module.exports = router;
Is it not possible to use the router when it comes to require the routes?
Further I want to implement the check for invalid routes or authorized users.
app.use(function(req, res, next) {
if(!req.route){
res.redirect('/notFound'); // invalid route
} else {
var session = req.session;
if (session.user == null){ // unauthorized user
res.redirect('/login');
} else {
// valid routes here
}
}
});
How can I connect this pseudo code with my valid routes?
put at the end of your express app.js
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handler
app.use(function(err, req, res, next) {
if(err.status == 404) res.redirect('/notFound')
// can handle more conditions (like 500) also
})
You have to create a middleware for checking authorized users on top of all routes, and another to handle not found routes at the end:
handle unauthorized users:
app.use(function(req, res, next) {
if (!req.session.user) {
return res.redirect('/login');
}
next();
});
catch not found routes:
app.use(function(req, res, next) {
res.status(404).send('Page Not Found');
});
You may want to render error view by using res.render('error', {code: 404, msg: 'Page Not Found'});
Error image:
app.get('/home', function (req, res, next) {
usersession = req.session;
if (usersession.loggedin == true)
res.redirect('/home');
res.sendFile(path.join(__dirname, 'index.html'));
});
When home page refresh then error comes
The if doesnt stop res.sendFile from executing after you have redirected.
app.get('/home', function (req, res, next) {
usersession = req.session;
if (usersession.loggedin) {
return res.redirect('/home');
}
res.sendFile(path.join(__dirname, 'index.html'));
});
Note: You should be ideally using router instead of app.
I'm trying create my own application based on rails (not equal, but similar).
So, i'm creating this basic stuff to send to github, so i can use in any project, and i have a problem with my routes.
I am using the express-resource to create the cruds routes.
This is my app.
Controller/example.js:
exports.index = function(req, res, next){
res.send('forum index');
next();
};
exports.new = function(req, res, next){
res.send('new forum');
next();
};
exports.create = function(req, res, next){
res.send('create forum');
next();
};
exports.show = function(req, res, next){
res.send('show forum');
next();
};
exports.edit = function(req, res, next){
res.send('edit forum');
next();
};
exports.update = function(req, res, next){
res.send('update forum');
next();
};
exports.destroy = function(req, res, next){
res.send('destroy forum');
next();
};
exports.load = function(id, fn){
process.nextTick(function(){
fn(null, { title: 'Ferrets' });
});
};
Them in my routes.js:
var express = require('express');
var resource = require('express-resource');
var client = express();
routes.resource('example', require('../controllers/example'));
module.exports = routes;
and my app.js:
// Routes
var routes = require('./routes/routes.js');
app.use('/', routes);
Now the problem:
I can access only the index and the new routes. When i try access:
http://localhost:3000/example - will show right, but with a 304 http code.
http://localhost:3000/example/new - will show right, but with a 304 http code.
http://localhost:3000/example/create - will show the /show/ and a 304 http code.
http://localhost:3000/example/show - will show the /show/ and a 304 http code.
http://localhost:3000/example/edit - will show the /show/ and a 304 http code.
http://localhost:3000/example/update - will show the /show/ and a 304 http code.
http://localhost:3000/example/destroy - will show the /show/ and a 304 http code.
In the terminal, the following error occur:
GET /example/edit 304 1.080 ms - -
Error: Can't set headers after they are sent.
I'm stuck in this.. i dont know the problem. Please, somebody help! haha
Thanks Very Much!
res.send('forum index');
next();
either respond or pass the request along for other middleware to respond, not both.
Replace:
function(req, res, next)
with
function(req, res)
And remove next();
next don't stop a request.
res.send('final output'); // end output
Try:
exports.index = function(req, res){
res.send('forum index');
};
exports.new = function(req, res){
res.send('new forum');
};
exports.create = function(req, res){
res.send('create forum');
};
exports.show = function(req, res){
res.send('show forum');
};
exports.edit = function(req, res){
res.send('edit forum');
};
exports.update = function(req, res){
res.send('update forum');
};
exports.destroy = function(req, res){
res.send('destroy forum');
};
exports.load = function(id, fn){
process.nextTick(function(){
fn(null, { title: 'Ferrets' });
});
};
This is other alternative: https://www.npmjs.com/package/express-enrouten
Check this article (spanish) http://blog.mcnallydevelopers.com/cargar-controladores-de-forma-automatica-en-expressjs-con-node-js/
Rails clone? http://sailsjs.org/
You don't have to call next() while you already called send() the middleware that called by next() trying to call send but couldn't because already sent as the .send() doesn't return and your process keep executing, just remove next()