I have a project based on express with a required authentication based on passport.
The backoffice is an angularjs app served as static files.
My authentication code is completly based on https://github.com/jaredhanson/passport-local/blob/master/examples/express3-no-connect-flash/app.js
To do not serve the angular app if you are not authenticated. I have try by adding ensureAuthenticated on the /admin route but it make the route not working (404). Once I remove ensureAuthenticated the /admin is served.
app.use(express.static(path.join(__dirname, 'public')));
app.use('/admin', ensureAuthenticated, express.static(path.join(__dirname, 'admin')));
//serve routes
app.use(app.router);
The public folder contains the login page.
How could I achieve this ?
Ran into same issue, this is what I ended up doing!
app.use doesn't let you chain middlewares in that way. The various
app.VERB functions do, but app.use doesn't. That's for one middleware
at a time.
If you split the 2 middlewares out into separate calls, you should get
the results you want:
app.use('/admin', ensureAuthenticated);
app.use('/admin', express.static(path.join(__dirname, 'admin')));
Cannot use basic authentication while serving static files using express
You can check the route using middleware and redirect them if they aren't logged in and are hitting admin pages, something like (untested):
app.use(function(req, res, next) {
if (req.user == null && req.path.indexOf('/admin') === 0)
{
res.redirect('/login');
}
next();
});
app.use('/admin', function(req,res,next){
if(req.user){
return express.static(path.join(__dirname, 'public'));
} else {
res.render(403, 'login', {message:'Please, login!'});
}
});
//serve routes
app.use(app.router);
Update for express#4.16.4+, passport-jtw#0.4.0, and passport-jwt#4.0.0
First setup a passport auth strategy. If you use a jwt, you can take a token from a query parameter, if not you can use another Extract function (or multiple using Jwt.ExtractJwt.fromExtractors())
passport.use('basic-user',
new Jwt.Strategy({
...jwtConfig.options,
jwtFromRequest: Jwt.ExtractJwt.fromUrlQueryParameter('token')
}, verifyUser)
);
Then you can use a passport authenticate function before serving static files
app.use('/files', [
passport.authenticate(['basic-user'], { session: false }),
express.static(path.join(__dirname, 'files')) //make sure you access proper directory
])
You could also chain middlewares as an array to achieve this goal:
app.use('/admin', [ensureAuthenticated, express.static(path.join(__dirname, 'admin'))]);
The accepted answer felt a bit partial (and may not work in some cases), so here's a bit more verbose and generalized answer:
// Here we'll attach the user object. The correct ordering
// of these middleware functions is important.
app.use(
'/some-restricted-static-path',
passport.authenticate("bearer", { session: false })
);
// As the user object should now be attached (if authorized), we can now
// verify it to be so.
app.use('/some-restricted-static-path', (req, res, next) => {
if (!!req.user) {
// The user exists, we can continue.
// Here you can also validate the role etc if necessary.
next();
} else {
// No user object found, terminate the pipeline with .end().
res.status(401).end();
}
});
// And finally, here's the actual handler that won't be accessed if
// something went wrong earlier.
app.use(
'/some-restricted-static-path',
express.static(
path.join(
__dirname,
"../dist/attachments"
)
)
);
Explanation: In Express, the middleware is processed one-by-one. If one of the middleware terminate the process, every middleware after it will be skipped. So, knowing this, we can first attach the user object, then validate it, and finally either grant or deny access.
Related
I was trying to comprehend when do we need to use app.use in our node Express
While searching on web, I stumbled on this answer on reddit stating the difference between app.get and app.use
Based on which, I was able to summarise the following things.
app.use act as a super route or middleware? meaning that it gets called on every route written below/after app.use?
Also, would appreciate if someone could add more information/practise about app.use.
When using ExpressJS with NodeJS you can use app.get and app.use for several useful aspects.
After initializing your App like let app = express();, you can find below some examples:
app.use(...)
As you correctly pointed, it is useful for "middlewares", it will apply to all the GETs, POSTs, etc. you indicate afterwords. For example, you can use a Middleware only before the GETs you want to be "with user/pass authentication".
Indicate the folder for static contents: app.use(express.static(__dirname + "/public"));
Including a parser for JSON contents: app.use(bodyParser.json());
Define the "Cookie Parser" signing string: app.use(cookieParser("Signing text example"));
Separate Routers for your URLs in different files: app.use("/api", apiRouter); or app.use("/news", newsRouter); or app.use("/", siteRouter);
For a custom error handler: app.use(sites404handler); or app.use(globalErrorHandler);
app.get(...)
When talking about app.get(...) you are indicating which URLs will be visited via a GET method. But you can use several options here:
Indicate you have a home page: app.get("/", function(req, res) { res.send("Hello world!"); });
Accept POST requests: app.post("/", function(req, res) { res.send("Hello world! With POST call."); });
You can also separate it in another file as "apiRouter.js" and include there: let router = express.Router(); router.route("/books").get(function(req, res) { res.send("/api/books/ called via a Router"); });
app.set(...)
Remember that you also have the option app.set(...). This is useful for example to define View Engines like Handlebars (.hbs files).
Hope this can help!
Use for static path
//Set static path
app.use(express.static(__dirname + '/public'));
use as router
//user
app.use('/', require('./controllers/user'));
use for handline middleware
//Body-parser
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));// Body parser use JSON data
Use for custom middleware
// force https
app.use ( (req, res, next) =>{
if (req.secure) {
// request was via https, so do no special handling
next();
} else {
// request was via http, so redirect to https
res.redirect('https://' + req.headers.host + req.url);
}
});
app.get route handler is applied to GET requests, either for specified paths or all paths:
Routes HTTP GET requests to the specified path with the specified callback functions.
app.use middleware is applied to all requests, either for specified paths or all paths:
Mounts the specified middleware function or functions at the specified path: the middleware function is executed when the base of the requested path matches path.
use is used to apply some logic (middleware) to specific route or entire application, regardless of request method.
I have created a function to check if a user is authenticated or not like this:-
app.use(compression({threshold:1}));
app.use(logger('dev'));
app.use(session({
secret:'node.js app',
resave:false,
saveUninitialized:true,
cookie:{secure:true}
}))
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
function CheckAuth(req,res,next){
if(!req.session.authenticated){
res.redirect('/users/login');
return;
}
next();
}
And used as a middleware
app.use(CheckAuth);
but the function running multiple times and multiple redirects and stops page load.
You have to many redirects because the middleware also runs in /users/login
You should add that to your check in the middleware function.
function CheckAuth(req,res,next){
if(!req.session.authenticated && req.url != '/users/login'){
res.redirect('/users/login');
return;
}
next();
}
You need routing for '/users/login', otherwise you're redirected back to the same place and CheckAuth is called over and over in an endless loop.
https://expressjs.com/en/guide/routing.html
You used the middleware for all routes so that the redirection loops forever.
So you need to specify which route need to be guarded explicitly. The code may be something like this:
//routes without auth guard
app.post('/users/login', login);
app.post('/users/register', register);
//routes guarded by auth
app.get('/api/xxx', CheckAuth, xxx);
app.post('/api/yyy', CheckAuth, yyy);
I have my application structured with 3 Routes (api, admin, default). Each lives in there own file and has it's own middleware and exports a Route. The problem I am facing is when I want to forward to another route that lives on a different router. Essentially I want to call the same function so that I am not serving up the same view from multiple locations.
I don't want to user res.redirect('/someplace') because I want to be able to pass the req and res objects on to the method.
|-app.js
|-routes
|---admin.js
|---api.js
|---default.js
The routes are required and used in app.js as follows
app.use('/api', require('./routes/api')(passport);
app.use('/admin', require('./routes/admin')(passport);
app.use('/', require('./routes/default')(passport);
Inside of admin if have a situation where I need redirect to login and pass some data
// authenticates all routes for the admin router
router.use(function(req, res, next){
if(req.isAuthenticated()){
return next();
}
res.flashMessage.push('Session expired'); //is lost after redirect
res.redirect('/login');
//do I need to restructure my whole app so that I don't
//have to call res.redirect('login')
});
Any ideas on how to structure this? Do I need to export every method and keep all of my routes in one router file? That doesn't very clean, but if the functions are somewhere else it may be too messy.
You can forward it by calling the next callback ,but only if you do not use any paths.
app.use(function(req, res, next) {
// ... api
next();
});
app.use(function(req, res, next) {
// ... admin
next();
});
Another option is use * that will match all paths:
app.use("*", function(req, res, next) {
var path = req.path; // just example how it can be done
if (path === "/api") {
// ...
path = "/admin";
}
if (path === "/admin") {
// ...
}
});
Edit:
I don't think that express has something like next('/login'); ,so basically function that can forward a request to another path and I don't think that is right to have something like this. If a client ask for /admin you should send this particular page and not the page that is under /login. If you want to send back to a client the login page than just redirect it as you did it in your question. I understand that you want to keep the req, res ,but then is the problem in the proposal/structure of your webapp.
I am using Express with Passport for an Node.js API app, and I can disable sessions by passing {session:false} to passport.authenticate, but is there a way I can do this in one place so I don't repeat myself 600 times?
var app = express();
app.configure(function() {
app.use(express.bodyParser());
app.use(passport.initialize());
});
app.get('/users', passport.authenticate('hash', {session:false}), controllers.users.getAll);
app.get('/users/me', passport.authenticate('hash', {session:false}), controllers.users.getCurrentUser);
// and so on...
It would be great if I could just do:
app.donotuse(sessions).thanks();
Just save the result in a variable and re-use it:
var PassportAuthenticateMiddleware = passport.authenticate('hash', {session:false});
...
app.get('/users', PassportAuthenticateMiddleware, controllers.users.getAll);
app.get('/users/me', PassportAuthenticateMiddleware, controllers.users.getCurrentUser);
(or do as #hexacyanide suggests and use the middleware globally, if that's an option in your setup)
Alternatively, you can use something similar to this:
app.all('/users*', passport.authenticate('hash', {session:false}));
app.get('/users', controllers.users.getAll);
app.get('/users/me', controllers.users.getCurrentUser);
This will filter all requests (instead of .all you can use .get too) whose URL starts with /users to be run through the authentication middleware.
The authenticator itself is middleware. Therefore, you can assign it globally.
app.use(express.bodyParser());
app.use(passport.initialize());
app.use(passport.authenticate('hash', {session:false}));
I guess that you have some routes which need session. It is possible to set this globally, but this will not work in your case. What you can do is to improve the code a bit:
var controller = function(controller) {
return function(req, res, next) {
passport.authenticate('hash', {session:false})(req, res, next);
controller(req, res, next);
}
}
app.get('/users', controller(controllers.users.getAll));
app.get('/users/me', controller(controllers.users.getCurrentUser));
I want to check the authorization of the users of my web app when they entered the url. But when I used an individually middleware to check the authorization, it's useless for the already existing routes, such as:
function authChecker(req, res, next) {
if (req.session.auth) {
next();
} else {
res.redirect("/auth");
}
}
app.use(authChecker);
app.get("/", routes.index);
app.get("/foo/bar", routes.foobar);
The authChecker is unabled to check the authority of the users who entered the two urls.
It only works for the unspecified urls.
And I saw a method that I can put the authChecker between the route and the route handler,
such as:
app.get("/", authChecker, routes.index);
But How can I achieve it in a simple way rather than putting the authChecker in every route?
As long as
app.use(authChecker);
is before
app.use(app.router);
it will get called for every request. However, you will get the "too many redirects" because it is being called for ALL ROUTES, including /auth. So in order to get around this, I would suggest modifying the function to something like:
function authChecker(req, res, next) {
if (req.session.auth || req.path==='/auth') {
next();
} else {
res.redirect("/auth");
}
}
This way you won't redirect for the auth url as well.
There are may ways to approach this problem but here is what works for me.
I like to create an array of middleware for protected and unprotected routes and then use when necessary.
var protected = [authChecker, fetchUserObject, ...]
var unprotected = [...]
app.get("/", unprotected, function(req, res){
// display landing page
})
app.get("/dashboard", protected, function(req, res){
// display private page (if they get this far)
})
app.get("/auth", unprotected, function(req, res){
// display login form
})
app.put("/auth", unprotected, function(req, res){
// if authentication successful redirect to dashboard
// otherwise display login form again with validation errors
})
This makes it easy to extend functionality for each middleware scopes by editing the array for each type of route. It also makes the function of each route more clear because it tells us the type of route it is.
Hope this helps.
But when I used an individually middleware to check the authorization, it's useless for the already existing routes
Express will run middleware in the order added to the stack. The router is one of these middleware functions. As long as you get your authChecker into the stack BEFORE the router, it will be used by all routes and things will work.
Most likely you have the router before authChecker because you have routes defined prior to getting your authChecker into the stack. Make sure to put all your app.use calls before any calls to app.get, app.post, etc to avoid express's infuriating implicit injection of the router into the middleware stack.