Node.js preresponse with Express - javascript

So I'm doing a web page with Node.js and Express framework. I already have registration and login (I'm holding users id in a session). Next step is to render different page whenever a user is authenticated.
Some of those pages require a User object which is just a mapping of a user from my db. So whenever an authenticated request comes I need to retrieve the user from my db. But writing this code every time seems to be a bad way to do this. So here's the question: is it possible (and if it, then how?) to do, say preresponse, so I can automaticaly retrieve User object whenever I know that the user is authenticated and THEN do the main response?

Middleware is what you are referring to. Middleware is just a function that gets called sequentially when the route is triggered. So to have a loadUser function:
function loadUser(req, res, next) {
// You would fetch your user from the db
var user = users[req.params.id];
if (user) {
req.user = user;
next();
} else {
next(new Error('Failed to load user ' + req.params.id));
}
}
app.get('/user/:id', loadUser, function(req, res){
res.send('Viewing user ' + req.user.name);
});
You can define as many middleware functions as your need. Just be sure to call next() at the end to pass the route handling on to the next function.

This EXACT example is covered in the express.js route middleware docs. Go read it and you'll see the pattern of using middleware to factor out common functionality that you need at many route paths in your app.

Related

Middleware OR other middleware validation

I need to check if a user is an administrator OR if the user is the same as the one requested. I tried but in one case it doesn't work.
router.get('/:id', isSigned, isSameUser || isAdmin, getUser)
Is there a way to do what I want without creating a dedicated function?
You could write a different middleware to avoid unnecessary middleware chaining, which you can call Auth or AuthMiddleware, the name you want is up to you. In this middleware you could do the logic for checking the user's authentication status, something similar to this:
function AuthMiddleware(request, response, next) {
if(isSigned) {
// User is signed in, so we can proceed doing the next check
if(isSameUser || isAdmin || getUser) {
// Validation checks out, go to next middleware without warnings.
return next();
} else {
// User failed to show some ID and cannot access the page.
return next(new Error("Unauthorized."));
}
} else {
// The user has not signed in to your application
return next(new Error("Unauthenticated"));
}
}
router.get('/:id', AuthMiddleWare, (request, response) => {
// DO LOGIC
});
Place this middleware inside a new file, and every time you need it, you can just import it rather than copy and paste the same code over and over.
! Note that this is a rough sketch code just to put you on the right track, so you most likely have to adjust this snippet to your own needs and logic.
The benefits of having a single authentication middleware, is so you can use it on every route, so you don't have to use so many chaining in your requests.

How to correctly send an email after an API request?

I'm building an express api with node.js. I'd like to send an email after a user creates an account. I've tried a couple of ways of doing that, but they don't seem to fit my needs, here's some pseudo code:
1. Pass the function that sends the email "at the end" of the route.
Basically, just call the function that sends the email at the end of the route that creates the user account:
app.post("/users", (req, res) => {
const { email, password } = req.body;
// create user...
sendEmailTo(email);
}
My concern with this approach is that the /POST users route is now no longer only for creating a user, but every time it creates one it also sends an email, which may be bad for future use cases where we'd like to create a user separately from the user signup form.
2. Have a unique route specifically for sending emails. In this case, we'd have to make two request to the api, one for creating a user, and another one for sending the email:
app.post("/send", (req, res) => {
const { recipientEmail } = req.body;
sendEmailTo(recipientEmail);
}
What I don't like about this approach is that my api is not an "email" api, I'd like to leave it free for all users to explore and use, I don't want users to just be able to send emails in behalf of the api by requesting this route.
I'm sure there must be other ways of doing this like using some type of callbacks or queues, yet I have found nothing researching for hours, everything is basically a rehearsal of the two options above. Taking into account the need for separation of concerns, how would you go on about sending an email after a request? do I only have these two options?
As I mentioned in the comments, you could just split your route handler in more fine-grained middlewares that would be reusable across all of your routes.
A route-level middleware is very similar to a route handler, except for the fact that instead of producing a response, it actually just does some more focused job and then forwards the request currently being processed to the next middleware or route handler in line. It does this with the extra next function parameter that is injected by express.
In your scenario, you could do something like this:
app.post(
"/users",
createUser,
sendEmail,
send201Response
)
async function createUser(req, res, next) {
// Create user...
​next();
}
​async function sendEmail(req, res, next) {
// Send email...
​next();
​}
function send201Response(req, res) {
​res.sendStatus(201);
}
Then if another use case happens to involve creating a user in a similar way for instance, you could do:
app.post(
"/otherendpoint",
createUser,
// Add other middleware/route handlers...
)
If you are concerned with people using the "/send" route, then you probably want the first option. In the future if you want to create a user but not send an email, then you can just add conditionals to not send the email in certain cases. For example, you could add another property to the body in order to determine if the email should be sent.
app.post("/users", (req, res) => {
const { email, password, sendEmail } = req.body;
// create user...
if(sendEmail === true) sendEmailTo(email);
}

How to use values between various Express routes

I am new to Express and writing an application in node js. I am having problem in using the same parameters. My code is:
app.get('/signin', function (req, res) {
renderView(req, res, 'signin.jade');
});
app.get('/config', function (req, res) {
addOrUpdateGroupConfig(req.query.group_name, req.query.webhook_url);
renderView(req, res, 'config.jade');
});
app.post('/config', function (req, res) {
..
}
function renderView(req, res, view, locals) {
if (locals === undefined) {
locals = {};
}
res.render(view, locals);
}
The sign in jade redirects to app.get(/config) and i am getting webhook_url and group_name. Now I am rendering a config jade page which has form post, after submit the control comes to app.post(/config) now the problem is i want the webhook_url and group_name here to store in database. SO how to pass those values in a good way ?
If the webhook_url and group_name values can't be put into the web page for security reasons, then the usual solution would be to create a session object and store it for that specific user in their session. Then, on the POST, you could get it out of that user's session. The express-session module is available to help you create a session object for each user. This is breaking with the usual stateless design of web pages so there'd have to be a really good reason to go this way.
Otherwise, the value should probably just be inserted into the form in a hidden field (by your renderer) so that when the POST happens, the value will be sent back with the form and you'll have everything you need in the POST data. That keeps your system entirely stateless which is good for a lot of reasons (simplicity, scalability, reliability, etc...).

How to go about creating temporary authentication for a website?

I'm new to authentication with websites, and I've been doing a lot of reading on the different kinds of user authentication (for example session vs token authentication) you can use. However, a lot of it seems more than what I need, and I'm not sure which ones will be suitable for my cause.
My idea is to generate temporary user accounts and passwords that will expire after the first use. I want this integrated with my website, so they have one chance to view restricted pages, after which they will not allowed access to those parts again (unless provided with new credentials).
Any direction in the right step will be appreciated.
Update: I'm using Javascript(Node) as my server side language
Session-based authentication is actually incredibly lightweight if you're using a Node backend, due to most (if not all) webserver libraries supporting "middleware", which modify requests before they hit your route functions. The Express-compatable middleware client-sessions is fantastic for this, and I used it previously in a project with great success. It adds a cookie on the first request a user makes to your site which identifies them, and if at some point they log in, you can flag that session as authenticated, store session information, and other data related to them specifically.
Assuming you want both login & logout, the simplest way would to be to use POSTs over HTTPS to login & logout routes. Inside of the resolution for the login route, you would simply "mark for deletion" inside whatever database you're working with.
An example might look like this:
var app = express();
function authenticate(user, pw){
//do your application specific login verification here
}
function deleteAccount(user){
//do your application specific user removal here
}
app.use(require("express-session")({
secret : "YOUR-SECRET-KEY-HERE"
cookieName : "Session"
//any other desired config options go here
})
app.post("/login", function(req, res){
var user = req.body.user;
var pw = req.body.pw;
req.Session.isAuthenticated = authenticate(user, pw)
if(req.Session.isAuthenticated){
markForDeletion(user, pw);
}
res.write("logged in as: " + user);
res.end();
});
app.post("/logout", function(req, res){
deleteAccount(req.Session.username);
req.Session.username = "";
req.Session.isAuthenticated = false;
res.write("logged out!");
res.end();
});

How can I impersonate another user with Passport.js in Node?

Using Passport.js in Node, is there a way for me to allow one user to impersonate another? eg. as an Administrator in the application, I want to be able to log in as another user, without knowing their password.
Most simply, I would be satisfied if I could change the serialized user data (user ID) so when deserializeUser is called it will just assume the identity of the alternate user. I've tried replacing the value at req._passport.session.user and the value at req.session.passport.user but the net effect is just that my session seems to become invalid and Passport logs me out.
Passport provides a req.logIn method in case you want to do the authentication manually. You can use it to login any user even regardless of authentication.
Here's how you can use it. Have the Admin login normally, who'll have an isAdmin flag set.
Then place a middleware before passport.authenticate in your login route. This will login the new user based only on their username, if the currently logged in user isAdmin.
app.post('/login',
function forceLogin(req, res, next) {
if (!req.user.isAdmin) return next(); // skip if not admin
User.findOne({
username: req.body.username // < entered username
}, function(err, user) {
// no checking for password
req.logIn(user);
res.redirect('/users/' + user.username);
});
},
passport.authenticate('local'),
function(req, res) {
res.redirect('/users/' + req.user.username);
}
);
I have another way to impersonate, because:
I didn't want to mess with internals of authentication/passport like
session storage / logIn / etc. You must understand them really well
and also they are prone to change so I'd say it's not an option for
me.
Also, I'd like to still be able to tell if action is made from
superuser (impersonated) or normal user (not impersonated).
What I do is:
Have a route for user with superadmin role to impersonate, like /superadmin/impersonate?username=normaluser1 which sets req.user.impersonated.userid = normaluser1.userid
Then I have a middleware, which checks if user is superadmin and is impersonated:
if (req.user.isAdmin && req.user.impersonated) {
req.user.userid = req.user.impersonated.userid;
}
Also, I have found this to be a good article about user impersonation. Similar to my approach, and good for inspiration for building something similar.
The answer to your question is basically: no. The reason is this: the sessions library that is being used 99% of the time is signing the cookies, so if you tamper with the data the web server will reject it.
The way around this is to write your own passport authentication strategy that obviously doesn't do this, but I'm assuming you're talking about working with the built-in strategies here.

Categories

Resources