I've got a NodeJS + Express Server setup with a router that looks like this:
app.route('/clients/:clientId)
.get(users.ensureAuthenticated, clients.read)
.put(users.ensureAuthenticated, clients.hasAuthorization, clients.update)
.delete(users.ensureAuthenticated, clients.hasAuthorization, clients.delete);
app.param('clientId', clients.clientByID);
My Problem is that users.ensureAuthenticated fills the req parameter with the current user req.user.
Basically it does this: req.user = payload.sub; (with some other background stuff)
Then the req.user is available in the following functions e.g. clients.update, but not in clients.clientByID.
I know I could execute users.ensureAuthenticated in clients.clientByID again, but this would execute the code twice and be extra load on the server, right? I guess there must be another way, but I couldn't find anything in the documentation of express.
I'd like to know how I can access the req.user in clients.clientByID without executing the code in users.ensureAuthenticated twice.
Based on your question, I assume you would like to execute users.ensureAuthenticated before clients.clientByID is executed. This can be achieved by using the app.use functionality. app.use handlers will get executed before the app.param and app.route handlers.
For example:
var express = require('express');
var app = express();
app.use('/user', function(req, res, next) {
console.log('First! Time to do some authentication!');
next();
});
app.param('id', function(req, res, next, id) {
console.log('Second! Now we can lookup the actual user.');
next();
});
app.get('/user/:id', function(req, res, next) {
console.log('Third! Here we do all our other stuff.');
next();
});
app.listen(3000, function() {
});
Related
I would like to know what happens if I write that in expressjs ( we assume app is an expressjs app).
app.get.use('/here' , function(req, res) {
// EXECUTE SET OF ACTION NUMBER 1
});
app.get.use('/here' , function(req, res) {
// EXECUTE SET OF ACTION NUMBER 2
})
We write the same route twice but with different set of actions. Will the second declaration overload the first one ?
It depends on what's happening inside the callback function. Consider three different examples:
a)
const express = require('express');
const app = express();
app.get('/', (req, res, next) => {
console.log('1!');
res.json(true);
});
app.get('/', (req, res) => {
console.log('2!');
res.json(true);
});
app.listen(3000);
b)
const express = require('express');
const app = express();
app.get('/', (req, res, next) => {
console.log('1!');
});
app.get('/', (req, res) => {
console.log('2!');
res.json(true);
});
app.listen(3000);
c)
const express = require('express');
const app = express();
app.get('/', (req, res, next) => {
console.log('1!');
next();
});
app.get('/', (req, res) => {
console.log('2!');
res.json(true);
});
app.listen(3000);
In the (a) case, the workflow is top-to-bottom. The first route handler kicks in, console.logs "1!" and responds with true. Since the complete response has been sent, the following route handler will never be reached.
In the (b) case, the execution stalls at the first route handler. It neither responds or allows execution to go further. If you curl this app, you'll end up with request timeout, although you'll see "1!" on the console.
Finally, in the (c) case, you can see next function call. This is what makes Express proceed with execution and go to the next route handler, if any. Since there's another route handler, it is executed, so you end up with both "1!" and "2!" printed on the console, as well as the response sent correctly.
A few points here:
always call next if you expect another route handler to run after current one, and it is especially important when you're writing a middleware or, as in your case, a "middleware-like" app that you are possibly going to add to another app,
it's okay to build up response in more than one handler, for example, when the first one declares headers and/or body, and the next one adds something to headers and/or body (which is weird and I'd strongly discourage you from doing so), and
once the response has been sent, the execution stops, and none of the further route handlers can be reached (which isn't guaranteed when you use conditional blocks, for example).
I used express-generator to generate the basic structure of an express app.
I have this in routes/my.js:
router.use('/update', function(req, res, next) {
req.headers['content-type'] = 'application/json';
console.log('router use invoked');
next();
});
router.get('/update', function(req, res, next) {
console.log('=== GET /my/update', req.body);
});
router.post('/update', function(req, res, next) {
console.log('=== POST /my/update', req.body);
});
And in app.js I added:
var my = require('./routes/my');
app.use('/', routes);
app.use('/users', users);
app.use('/my', my);
It's working fine for GET, and the router middle-ware is actually working (console.log is called), but the headers is not being set to app/json (as the log message in post outputs the body to be empty).
I can solve this by adding this before any other middle-ware
app.use('/my/update', function(req, res, next) {
req.headers['content-type'] = 'application/json';
next()
});
And it works. I tried moving the app.use('my', my); line before any other middle-ware but it stopped working altogether.
What can I do in order to give priority to my router.use?
You're adding the header to the request object, not the response. Also the below is the preferred way to do it.
router.use('/update', function(req, res, next) {
res.header('content-type', 'application/json');
console.log('router use invoked');
next();
});
ExpressJS is all about working with middle wares.
According to official documentation here, Middleware is a function with access to the request object (req), the response object (res), and the next middleware in line in the request-response cycle of an Express application, commonly denoted by a variable named next.
Middleware can:
Execute any code
Make changes to the request and the response objects
End the request-response cycle
Call the next middleware in the stack
I would like to point a few things about your my.js router, if you don't want to send any response back then it should be something like :
router.get('/update', function(req, res, next) {
res.end();
});
router.post('/update', function(req, res, next) {
res.end();
});
You must end your request, response cycle else it will be left hanging. Practically you would like to send some data(preferably JSON data) back to client so you can use res.json() or res.send(). You don't need to set application/json headers if you use res.json().
I've been watching a SailsJS tutorial and for some user authentication I saw that function applied but I did not get what is the specific function of next();
The purpose of next is to continue processing a request -- this allows things like middleware to work.
Here's an example.
Imagine you have a simple Express.js app that looks like this:
var app = express();
app.get('/', function(req, res) {
res.send('hi!');
});
app.get('/dashboard', function(req, res) {
res.send('there!');
});
The above app has 2 routes, and each does something particular.
Now, what if we wanted to create a special function that prints hello world to the console before every request is executed? To do that, we'd need to modify either both routes above, or create a middleware like so:
var app = express();
app.use(function(req, res, next) {
console.log('hello, world!');
next(); // continue processing
});
app.get('/', function(req, res) {
res.send('hi!');
});
app.get('/dashboard', function(req, res) {
res.send('there!');
});
Now, what happens is that, for each request, the function we defined above will be called BEFORE any of our route code, and once it makes the call to next(), that means we'll then run our route code.
Nifty, right?
At a basic level, the next object is just the 'next function' to execute, so when you say next(), you're telling express to keep processing the user request, essentially.
More specifically, I have an auth system that uses passportjs and req.user is defined if the user is authenticated.
Right now my website only has about 5 pages, but it's growing, and at the top of every route, I check if req.user exists and I pass a true or false variable to the rendered template, and the template renders accordingly.
I messed around with things such as app.get("*") but I didn't end up finding anything good.
How could I check if req.user (or anything else that could exist within req...) exists -- when a user goes to any page of my website, without repeting code?
Progress:
Using this code in app.js:
app.use(function (req, res, next) {
// Using req.locals.isAuthenticated would be better, as it's automatically passed to every rendered templates.
req.context = {};
req.context.isLoggedIn = req.isAuthenticated();
// req.locals.isAuthenticated = req.isAuthenticated();
next();
});
app.use('/dashboard', dashboard);
and this in the routes/dashboard route:
router.get('/', function (req, res) {
res.render('dashboard', { isLoggedIn: req.context.isLoggedIn });
});
Works - I can then see if the user is logged in by doing for example {{ isLoggedIn }}.
However when I uncomment the req.locals line in the first code snippet, I get a 500 error.
Two things to note:
Usually when your application needs to do something for a bunch of different pages, you want to setup a middleware function via app.use
Express has a res.locals variable whose properties will be included in any rendered template
With the above points in mind, you can construct something like the following:
app.use(function(res, req, next) {
res.locals.isAuthenticated = typeof(req.user) !== 'undefined';
next();
});
You then supply your additional template variables when your routes call res.render. For example:
app.get('/about', function(res, req) {
res.render('about', { 'testValue': 14} );
});
Your template will have access to both isAuthenticated and testValue.
I recommend you put some middleware in place before your route handlers but after passport's.
app.use(function(req, res, next) {
// Create a `context` object for use in any view.
// This allows the context to grow without impacting too much.
req.context = {};
// Assign your flag for authenticated.
req.context.isAuthenticated = typeof req.user !== 'undefined';
// Let the next middleware function perform it's processing.
next();
});
Then you can render each view with the context.
app.use('/', function(req, res) {
res.render('index', req.context); // Context is passed to view for usage.
});
This is all untested code.
You can do it as is already mentioned here ,but in this case you are going to check completely every request. Maybe you have got / you are going to have some pages that don't require any authentification and in this case you have to make some statement that will skip auth for that particular page or you can use something like this:
function checkUser(req, res, next) {
req.userAuth = (req.user !== undefined);
next();
}
app.post("settings", checkUser, doSomething);
app.post("administration", checkUser, doSomething);
app.post("index", doSomething); // Doesn't require any authentification
Or you can straight a way redirect a user
function checkUser(req, res, next) {
if (req.user === undefined) {
res.redirect("/login"); // res.render
}
else {
next();
}
}
I'm new to using Passport.js, but I find it's going pretty well so far. I'm using Passport with passport-local.
However, I want to require authentication for an entire directory excluding one page. So in my node server I'm serving up this direcory like so (using express):
app.use("/admin", express.static(__dirname + "/admin"));
And then I want to let the user hit /admin/login.html, so I wanted to do something like this:
app.get('/gb-admin/login.html', function(req, res){ });
Then I want to require authentication for the rest, so something like this:
app.get('/gb-admin/*', ensureAuthenticated, function(req, res){});
Here is my ensureAuthenticated function, for reference if it helps:
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/gb-admin/login.html')
}
How would I go about doing this? I've been generally sending things in infinite loops and causing the browser to timeout. Can anyone help?
The reason you're getting timeouts is because you can't have an empty route handler; at one point, you have to either return a response, or hand the request over the the next route handler/middleware.
That said, try this:
function ensureAuthenticated(req, res, next) {
if (req.path === '/gb-admin/login.html' || req.isAuthenticated()) {
return next();
}
res.redirect('/gb-admin/login.html')
}
app.get('/gb-admin/*', ensureAuthenticated, function(req, res, next) {
next();
});
// the static middleware needs to be declared after the route above, otherwise
// it will take precedence and ensureAuthenticated will never be called.
app.use("/gb-admin", express.static(__dirname + "/admin"));
I don't think there's a way to get it working with a separate route for the login page (unless you actually implement reading login.html and sending it back from without that routes handler), hence the check for it in the ensureAuthenticated middleware.
I wonder if it is your callback. Try:
app.get('/gb-admin/*', function (req, res, next) {
ensureAuthentication(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/gb-admin/login.html')
});
});