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'});
Related
1.Can anyone tell me how to allow my error to pass through the app.use('/blogs','blogRoutes') middle ware that scopes my url to a api file?
2.Is the answer something like app.use('/blogs','blogRoutes', next){next(err)}?
const express = require('express');
const morgan = require('morgan');
//use Mongo Db
const mongoose = require('mongoose');
const blogRoutes = require ('./route/blogroutes');
const app = express();
//connect to mongo db
const dBurI = mongo database key
mongoose.connect(dBurI, {useNewUrlParser: true, useUnifiedTopology: true});
.then((result)=> app.listen(3000))
.catch((err)=>console.log(err))
//middleware stack
// allows use of view engine to render - html files in views foles are suffixed with ejs these are found automatically due to views folder beign recognised
app.set('view engine','ejs');
app.use(express.static('public')); //access ppblic folder files - css
app.use(express.urlencoded({extended: true})); //url data and passes into object that can be used on req => req.body
app.use(morgan('dev'));
//redirect route
app.get('/',(req, res, next)=>{
res.redirect('/blogs');
next(err);
});
app.get('/about',(req, res, next)=>{
//send file to a browser
res.render('about', {title: 'About' });
next(err);
});
//redirect 301
app.get('about-me',(req,res, next)=>{
res.redirect('./about');
next(err);
});
//blogroutes
app.use('/blogs','blogRoutes'); //scope all blog urls to blogrouter
// middle ware app.use is called immediately if above are not found - error page 404
app.use((err,req,res)=>{
res.status(404).render('404', {title: '404', error: err });
});
1.after reading the express documentation I believe the answer for espress5+ is as follows:
//redirect route
app.get('/',(req, res, next)=>{
res.redirect('/blogs');
});
app.get('/about',(req, res, next)=>{
//send file to a browser
res.render('about', {title: 'About' });
});
//redirect 301
app.get('about-me',(req,res, next)=>{
res.redirect('./about');
;
});
//blogroutes
app.use('/blogs','blogRoutes',(req,res,next)); //scope all blog urls to blogrouter
// middle ware app.use is called immediately if above are not found - error page 404
app.use((err,req,res, next)=>{
res.status(404).render('404', {title: '404', error: err });
});
2.read this doucmentations and see if im write if not then I believe I have to do this:
//redirect route
app.get('/',(req, res, next)=>{
res.redirect('/blogs');
next(err);
});
app.get('/about',(req, res, next)=>{
//send file to a browser
res.render('about', {title: 'About' });
next(err);
});
//redirect 301
app.get('about-me',(req,res, next)=>{
res.redirect('./about');
next(err);
});
//blogroutes
app.use('/blogs','blogRoutes',(req,res,next)=>{next(err);}); //scope all blog urls to blogrouter
// middle ware app.use is called immediately if above are not found - error page 404
app.use((err,req,res, next)=>{
res.status(404).render('404', {title: '404', error: err });//next would call the express error handler automatically if used here..
});
Following is the error handling behaviour in Express - 4.17:
You do not need to explicitly pass the error to the error handling middleware for synchronized operation. Express will take care of it for you. This can be confirmed by sending a GET request to the / route. In this case, your app does not crash.
If an error occurs during async operations, you must explicitly pass the error to the next middleware using next(). I created two routes to test this behaviour: /async and /async_next. If you visit the /async route, your app should crash because we are not explicitly handling the error. Once you restart your app and navigate to the /async_next route, you will notice that it does not crash and that the error is handled by the custom error handling middleware.
For understanding the above points refer to the following code -
const express = require('express');
const app = express();
app.listen('3001', () => console.log("listening port - 3001"));
app.get('/async', (req, res, next) => {
console.log('Async function');
setTimeout(() => { throw new Error('Async Err from settimeout') }, 1);
})
app.get('/async_next', (req, res, next) => {
console.log('Async function');
setTimeout(() => {
next(new Error('Async Err from settimeout'))
}, 1);
})
app.get('/', (req, res, next) => {
throw new Error('Random Sync Err');
});
app.use((err, req, res, next) => {
console.log('Error from custom handler ', err);
res.send('Error');
});
1.so this seems right but need testing...
//redirect route
app.get('/',(req, res, next)=>{
res.redirect('/blogs');
});
app.get('/about',(req, res, next)=>{
//send file to a browser
res.render('about', {title: 'About' });
});
//redirect 301
app.get('about-me',(req,res,next)=>{
res.redirect('./about');
});
//blogroutes
app.use('/blogs','blogRoutes',(req,res,next)); //scope all blog urls to blogrouter
// middle ware app.use is called immediately if above are not found - error page 404
app.use((err,req,res,next)=>{
res.status(err.status).render('404', {title: 'Error Page', error_message: 'err.message'});
}):
You error handler needs to look like this even with express 5+
function errorHandler (err, req, res, next) {
if (res.headersSent) {
return next(err)
}
res.status(500)
res.render('error', { error: err })
}
You need the err argument to reference to the error...
I am trying to handle simple user information through cookies, this information is visible in my layout through res.render, but I saw it very tedious, since it will always be simple information and I would have to be doing it in each .get (), then in app.js I did this:
app.js
app.use(function(req,res, next){
var cookies = req.cookies;
if(!cookies.UserData){
console.log('cookies no exists');
}
else{
console.log('cookies');
res.locals.user = cookies.UserData;
next();
}
});
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
layout.ejs
<%- user.username %>
but "user" was not defined, but in my error.ejs When a 404 was generated, I put "<%- user.username %>" it in and it worked.
but it only works on 404 catches and not in general, what am I doing wrong? how I do this?
how I solved it
In my Router-level middleware:
var express = require('express');
var router = express.Router();
router.use(function(req,res, next){
var cookies = req.cookies;
if(!cookies.UserData){
console.log('cookies no exist');
}
else{
console.log('cookies');
res.locals.user = cookies.UserData;
next();
}
});
I put it first in the router-level middleware, above all the other http methods
I´m facing a confusion issue while implementing the authentication for my restful api using passport local strategy.
Note:
I got the authentication working successfully when I´m doing it all in my index.js. But I want to use in Classes for better Code separation.
I have a passport.js Module
// config/passport.js
// load all the things we need
var LocalStrategy = require('passport-local').Strategy;
// load up the user model
var mysql = require('mysql');
var dbconfig = require('./database');
var connection = mysql.createConnection(dbconfig.connection);
module.exports = function(passport) {
// passport needs ability to serialize and unserialize users out of session
passport.serializeUser(function (user, done) {
//console.log("SER");
console.log(user),
done(null, user);
});
passport.deserializeUser(function (user, done) {
console.log("XXXX");
console.log(user);
connection.query("SELECT * FROM users WHERE name = ? ",user.name, function(err, rows){
console.log("DER");
console.log(rows);
done(err, rows[0]);
});
});
// passport local strategy for local-login, local refers to this app
passport.use('local-login', new LocalStrategy(
function (username, password, done) {
console.log("hhh");
console.log(username);
connection.query("SELECT * FROM users WHERE name = ? ",username, function(err, rows){
console.log(rows);
return done(err, rows[0]);
});
})
);
// route middleware to ensure user is logged in
function isLoggedIn(req, res, next) {
if (req.isAuthenticated())
return next();
res.sendStatus(401);
}
};
This is my Controller Class:
class AuthenticateController {
constructor(router, passport) {
this.router = router;
this.registerRoutes();
this.passport = passport;
}
registerRoutes() {
this.router.post('/login/:username/:password', this.login.bind(this));
//this.router.get('/logout', this.logout.bind(this));
this.router.get('/content', this.content.bind(this));
}
login(req, res) {
this.passport.authenticate("local-login", { failureRedirect: "/login"}),
res.redirect("/content");
}
content(req, res ) {
console.log(req.user);
if (req.isAuthenticated()) {
res.send("Congratulations! you've successfully logged in.")
} else {
res.sendStatus(401);
}
}
isLoggedIn(req, res, next) {
console.log(req.user);
if (req.isAuthenticated())
return next();
res.sendStatus(401);
}
}
module.exports = AuthenticateController;
The Controller gets the router and passport fully configured as parameters from my index.js.
//index.js
var express = require('express')
, cors = require('cors')
, app = express()
, passport = require('passport')
, morgan = require('morgan');
require('./config/passport')(passport); // pass passport for configuration
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(require('express-session')({secret: 'vidyapathaisalwaysrunning',
resave: true,
saveUninitialized: true }));
app.use(passport.initialize());
app.use(passport.session());
app.use(cors());
var apiRouter = express.Router();
app.use('/api', apiRouter);
//
var apiV1 = express.Router();
apiRouter.use('/v1', apiV1);
var authenticateApiV1 = express.Router();
apiV1.use('/auth', authenticateApiV1);
var AuthenticateController = require('./controllers/authenticate');
var ac = new AuthenticateController(authenticateApiV1, passport); //pass in our fully configured passport
//If I call this /login instead of the /auth/login/ in the Controller Class it works!
//app.post("/login",
// passport.authenticate("local-login", { failureRedirect: "/login"}),
// function (req, res) {
// res.redirect("/content");
// });
What is working and what is not working
The Authentication in general is working. In my posted index.js you see app.post("/login", .... If I call this one the authentication is successfully and if I try to reach the restricted content in /auth/content/ req.user has a value (the user object) and I can successfully call req.isAuthenticated() .
BUT, If I use the authentication from /auth/login/username/password the req.user is undefined when trying to reach the restricted Content.
I get no error and the response of /auth/login/username/password/ HTTP Code 301 - 'redirecting to /content.
I have currently no idea what I´m doing wrong here and I´m pretty new to the topic of Node/express/ passport ..
Hope someone has an Idea. If you need something else to help me, just mention it in the comments and I will do my best to provide you everything you need.
Thanks
EDIT:
I recently tried to read the req.user in the login function and even there it is undefined
login(req, res) {
this.passport.authenticate("local-login", { failureRedirect: "/login"}),
console.log(req.user) //undefined
res.redirect("/content");
}
I guess it could be some async problem and I should use some callback functions, but I don´t know how to apply this in my login()
EDIT 2:
Another Issue I´m facing is the integration of the isLoggedIn() request.
If I do this:
registerRoutes() {
this.router.get('/', this.isLoggedIn, this.getUsers.bind(this));
this.router.get('/:id', this.getSingleUser.bind(this));
}
it results in 401 - Unauthorized
A console.log(req.user); in the isLoggedIn() results in undefined.
But if I call the first route without calling isLoggedIn() and do console.log(req.user); the user object exists.
The correct use of callback with passport authentication for local strategy can be as below:
function(req, res, next){
passport.authenticate('local-login', function(err, user, info){
if(err)
return logger.log('error', err);
if(user)
req.login(user, function(err){
if(err) return next(err);
return res.json({'success': true});
});
if(!user)
return res.json({'error':true, 'message': info.message, 'type': info.type});
})(req, res, next);
}
Please note the use of req.login() to explicitly set user in session.
The only thing I'm finding strange is that your route declarations are different.
In the AuthenticateController the route is declared as:
this.router.post('/login/:username/:password', ...
While in index.js, the route is simply declared as
app.post("/login", ...
How is your client submitting the login credentials to the server? If it is by form, like the tutorial, could it be that having :username and :password declared as route params but being sent by form messes with passport?
Try registering the route exactly like index.js
this.router.post('/login', ...
EDIT:
I've found another dicrepancy. In AuthenticateController the res.redirect("/content"); is not wrapped inside a callback. So it is being executed before Authenticate finishes running.
In the index.js example, passport is being used as a route middleware:
app.post("/login",
passport.authenticate("local-login", { failureRedirect: "/login"}),
function (req, res) {
res.redirect("/content");
});
While in the passport.js it is inside the callback. Consider declaring it in the route:
registerRoutes() {
this.router.post('/login', this.passport.authenticate("local-login", { failureRedirect: "/login"}), this.login.bind(this));
(...)
}
login(req, res) {
res.redirect("/content");
}
O, better yet, why not use passport's option to declare both success and failure redirects, since that seems to be all that you are doing:
login(req, res) {
this.passport.authenticate("local-login", { successRedirect: "/content", failureRedirect: "/login" });
}
You are passing this.login.bind(this) as a middleware to this.router.post('/login/:username/:password', this.login.bind(this)); but login(req, res) only responds to the request with res.redirect("/content"); i.e. redirecting to /content
So like you said, you need to supply a callback that does something with the user that is returned from passports middleware verify callback.
app.post("/login",
passport.authenticate("local-login", { failureRedirect: "/login"}),
function (req, res) {
console.log(req.user); // log user in console
res.json({user: req.user}); // send user as json response
});
The custom callback mentioned by #divsingh is if you want to explicitly have control of setting the session, error messages and redirecting the request. Any other information can be found under http://passportjs.org/docs
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();
}
}
I was wondering how should I be dealing with response and next in express.
My understanding is that when I say res.send(...) -> returns a response
If I want to throw an error, I say next(new Error('whatever')) -> auto sets the http status code using express errorhandler.
I am able to do either of those but not but, looks like I am messing up.
Can anyone give an example?
I tried,
somefunc(err, req, res, next) {
if(err) {
res.send(500, 'some error');
return next(err);
}
}
return [somefunc, express.errorHandler()];
thanks.
You can register some middleware to handle errors:
app.use(function(err, req, res, next){
console.error(err.stack);
res.send(500, 'Something broke!');
});
You can also simply send a 500 with your response if you encounter an error in your logic
function loginUser(req, res, next) {
try {
var valid;
//code to check user
if (valid) {
next();
} else {
res.send(401, 'not authorized');
}
} catch (err) {
res.send(500, 'Oopsy');
}
}
app.get('/some/route',loginUser, function(req, res) {
// ...
});
Just skip return next(); part. Use return res.send(500,'some error'); Calling next causes next middleware to be called which IMO is not what you want in this case. I wrote more about it here.
Here is minimal example of express stack:
express = require('express');
app = express();
app.configure(function() {
app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(app.router());
app.use(express.static(__dirname + '/public'));
});
app.get('/', function(req, res) {
if (some error)
res.send(500, 'Some error');
else
res.send('<body>Hello</body>');
});
app.listen(3000); // listen on port 3000
The get request will be called by router middleware. Middlewares are parsed as chain. You can use custom middleware in this chain, for example:
app.configure(function() {
app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(function(req, res, next){
if (some condition)
res.send(500, 'No way'); // this will break the request chain
else
next(); // this will continue processing and will call app.router middleware
});
app.use(app.router());
app.use(express.static(__dirname + '/public'));
});
app.router middleware is responsible for calling appropriate method based on requested URL (like app.get('/')). If it fails to find one, it calls next() to pass control to express.static middleware which tries to find static file in /public/ folder.