I am wondering how to communicate between controllers - node.js

I want to invoke the user creation API after confirming the token internally in the server when I click the authentication link in the e-mail to implement the membership method using e-mail authentication.
//emailcontroller.js
router.get('/register/token', function(req, res) {
// check token
if(check(req.params.token)) {
request('http://localhost:8080/api/user', function(data) {
});
}
});
//usercontroller.js
router.post('/api/user', function(req, res) {
var user = new User();
user.userId = req.body.userId;
user.userPw = req.body.userPw;
user.save();
});
I want to invoke the user creation API after confirming the token internally in the server when I click the authentication link in email in order to implement membership method using email authentication.
As mentioned above, the email controller and the user controller are divided and each is routed. I want to modularize the code so that I want to call the existing user creation API to use it for general purpose rather than creating and exports common functions for a specific controller.
/*I do not want to implement it this way.*/
//emailController.js
router.get('/register/token', function(req, res) {
// check token
if(check(req.params.token)) {
userContoller.createUserFromEmail(userId, userPw);
}
});
//userController.js
exports.createUserFromEmail = function(userId, userPw) {
var user = new User();
user.userId = userId;
user.userPw = userPw;
user.save();
}
However, I have never seen communication between controllers in many examples. So I do not know if the way I thought was right. Rather, I think the cost of calling api internally on the server might be higher.
I want to know the correct pattern for communication between controllers. Please bear in mind that there is only a stack overflow when raising a question.

You got the right idea about exposing your API functionality as stand-alone functions (or classes). To avoid duplication, just call your internal methods from within your route handlers. So in your example:
router.post('/api/user', function(req, res) {
createUserFromEmail(req.body.userId, req.body.userPw);
});
In my own projects, I use classes to create my API. First I define a class with just the functionality and then I expose the methods in the route handlers:
export default class User {
read() {
}
create() {
}
update() {
}
delete() {
}
}
const user = new User();
router.get('/user/:id', (req, res) => user.read(req.params.id));
router.post('/user', (req, res) => user.create(req.body.data));
router.put('/user/:id', (req, res) => user.update(req.params.id, req.body.data));
router.delete('/user/:id', (req, res) => user.delete(req.params.id));
This should give you an idea of what you can do. You can write custom middleware and class decorators to reduce the boilerplate.

From your question what I understood:
You want to validate internally the token passed in query parameter, before doing anything else in the user controller.
I believe you are using express, and with express comes middlewares.
From docs:
Middleware functions are functions that have access to the request object (req), the response object (res), and the next middleware function in the application’s request-response cycle. The next middleware function is commonly denoted by a variable named next.
What I usually do and a generally good practice is, pass the token in create user api and attach to email body.
for example:
api/user?token=somerandomstringloremispum
Route file:
router.post('/user', validateEmail, userController.create);
here validateEmail is a middleware function and will be invoked before create method of userController.
Now in your validateToken method, you can simply validate your token like:
function validateEmail (req, res, next) {
if(!valid(req.query.token)) {
//return with appropriate invalid token msg using res.json() or however you like
}
// if validated call `next middleware` like so:
next();
// this will allow `create` method of userController be invoked
}

Related

Simple Express JS API token

I'm wonder how I can implement a simple API auth token without need for users? I just want one token that I can authenticate by adding it as a parameter when I call my API.
All the current examples on Google seem to be over engineered for my needs. I have some data stored in MongoDB and I simply serve this data like so:
app.get("/", (req, res) => {
Car.find((err, cars) => {
if(err){
throw err;
}
res.json({"cars": cars});
});
});
Is it possible to add some simple middleware that checks my environment file for an element with the name of api_token. Then check that the api_token in my env file matches the parameter that has been passed as a URL query.
Is there a way to do this? I'm aware that you can't use URL queries on a GET route so I am unsure how this would work.
Sure, use middleware: https://expressjs.com/en/guide/using-middleware.html
For your case, it can be as simple as the following:
// checkForApiToken.js
module.exports = (req, res, next) => {
const apiToken = req.header("api-token");
if (process.env.API_TOKEN !== apiToken) {
next(new Error("Unauthorized."));
return;
}
next();
}
The logic is simple:
Retrieve API-TOKEN value from the header.
Check it matches what I've defined in my env.
Does not match, throw an error by passing an error object into the next function.
Matches so I call next() with no error to proceed to the next request handler.
You would then use it like so:
app.get("/", checkForApiToken, async (req, res) => {
const cars = await Car.find().exec();
res.json({ cars });
});
Remember, Tokens are responsible for at least 2 API security mandatory things, authenticate and authorize. You don't need to authenticate users, but you need to be sure that the token you received is a Token and not a "HEADER".
If you use a static token,or anything else, first time i get your token your security is down. You need to specify AT LEAST when this token will die, and if it is a valid one based on some random assignment. You can't check for it's presence, you need to check if it is valid.
Javascript has an amazing convention background, whenever you have the opportunity, follow the convention. It is easier as it seems to implement a JWT based.
Just follow this : https://github.com/auth0/node-jsonwebtoken
and implement it in your middleware as you wishh.
Easily as this /
jwt.sign({
exp: Math.floor(Date.now() / 1000) + (60 * 60),
data: 'foobar'
}, 'secret');
jwt.verify(token, 'shhhhh', function(err, decoded) {
console.log(decoded.foo) // bar
});
You can always redo the concepts by using a "Web HEADER" and calling it a "TOKEN". But as i said, it is a "Web Header" not an "WEB TOKEN".

Migrating Node JS code to Apollo server

I am setting up Apollo Server on my Node app and wondered about moving the functionality over to Apollo.
I have business logic like this:
router.post(
'/login',
(req, res, next) => {
if (!req.body.email || !req.body.password) {
return 'You must send the username and the password.';
}
Users.findOne({ email: req.body.email })
.then(user => {
bcrypt.compare(req.body.password, user.password, (err, success) => {
req.user = user;
next();
});
})
},
auth.createToken,
auth.createRefreshToken,
auth.logUserActivity,
(req, res) => {
res.status(201).send({
success: true,
authToken: req.authToken,
refreshToken: req.refreshToken
});
}
);
It follows Node router architecture where I add the found user object to req object, which passes the user to the next functions - createToken etc.. using the next() function. This was ok for my server before trying to introduce GraphQL/Apollo, but now I want all this logic to be easily accessible to the Apollo resolvers.
I often hear that people are having an easy time turning their server from REST/non-GraphQL into a GraphQL server, but at the moment it's looking like it's going to be a bit of a job to go through all the logic and separate everything in to their own functions which take parameters directly rather than using the req object.
Is this a correct assumption? Or am I missing something?
Thanks!
Migrating the code you have shown above would be a very easy task. Once you build your graphql server and create your schema, etc. Then all you need to do is create login mutation. Then your resolver would handle the logic you have shown above. Then, instead of pulling the values from from req.body they would be function parameters.
A good pattern I am currently following is creating a login method on the model itself. Then the resolver calls the method on the schema (Here is an example of a project I'm doing it on now: Login method. Then here is an example of what the resolver looks like: Resolver
Hopefully that helped!

Restify: Calling a middleware within a middleware

I have a middleware for authentication which decrypts the token in the header and put the userId from the token to req.userId. Otherwise it throws an error if token doesn't exists or is invalid.
I call it like this in routes where I need authentication:
server.get('/api/somecall', authMiddleware, callService.somecall);
Now I will also have some routes which will carry a user ID in its params, like this:
server.get('/api/somecall/:userId', callService.somecall);
Here :userId can be a Mongoose Object ID or just me.
So I want to write another middleware which will be called for all routes that looks for particular params like userId and/or adminId. And if their value equals me, I want to make sure that authMiddleware automatically comes to action, PLUS replace me with the logged in user's ID so the callService.somecall handles the logic inside as it received a Mongoose Object ID.
There are two problems I'm facing:
How do I get req.params.userId in a middleware which is called before server.get(...)?
Suppose if I get req.params.userId somehow, how do I make the middleware to:
perform some condition (check if req.params.userId === 'me'),
then call authMiddleware so that I get req.userId from token, and
then perform more actions (replace req.params.userId = req.userId
then call next() to continue.
Your question is a little confusing so I don't know if this will help, but why not do something like this:
server.get('/api/somecall/:userId', otherMiddleware, callService.somecall);
function otherMiddleware(req, res, next){
if(req.params.userId === 'me'){
authMiddleware(req, res, next);
}
// Do other stuff here.
next();
}

Partially protect API endpoint with Passport.js

Based on the user login status, I want to serve different results from the SAME api endpoint.
The usecase is having articles served from the same api endpoints.
So some articles meant to be public, while some private.
There are also special pages, where a list of articles can be queried.
Example:
api/articles/special/all
This API should return all pages currently in the database.
But it should also filter out private results, if the user is not logged in.
With Passport.js I can only protect the whole API endpoint, eg:
router.get('/', auth.isAuthenticated(), controller.index);
While I would like to call the auth.isAuthenticated() method from within the actual function, eg:
// Get list of articles
exports.index = function(req, res) {
Article.find(function (err, articles) {
if(err) { return handleError(res, err); }
var titles = [];
var loggedIn = false;
if (auth.isAuthenticated()) {loggedIn = true;} // NOT WORKING
for (var i = 0; i<articles.length; ++i) {
if (articles[i].role === 'loggedIn' && loggedIn) {
titles.push(articles[i].name);
} else if(articles[i].role !== 'loggedIn') {
titles.push(articles[i].name);
}
}
return res.json(200, titles);
});
};
Any idea, how to use Passport.js from within the controller, and not protecting the whole API endpoint?
I'm not sure how to accomplish this with passport.js, but if you're using something like Stormpath, this is quite easy to accomplish out of the box.
Once you've initialized / configured the express-stormpath middleware, it will automatically attempt to authenticate ANY user without making you explicitly use middleware.
So for instance, you could say:
var express = require('express');
var stormpath = require('express-stormpath');
var app = express();
app.use(stormpath.init(app));
app.get('/myapi', function(req, res) {
if (req.user) {
res.json({ status: 'YOU ARE LOGGED IN: ' + req.user.email });
} else {
res.json({ status: 'YOU ARE NOT LOGGED IN!' });
}
});
app.listen(3000);
Because Stormpath stores your users for you, if you're using the express-stormpath library this means that it will try to authenticate users either via a web session (in the browser), or a user's API keys (which Stormpath also stores).
This is why it's pretty simple to work, as the middleware will inspect the incoming HTTP headers and authenticate a user properly with either Basic Auth or OAuth2 =)

How to handle authorization in a layered nodejs with passport app?

So I'm trying to build an app with nodejs, using express and passport, but as I try to do some kind of TDD, I want to decouple bussiness logic from controllers.
So I have a common scenario like this:
An authenticated user wants to delete an item, he sends a request to the api:
DELETE /api/item/1
The request is handled by the controller method, which passes the user that makes the request to the next layer (which doesn't seem like a good approach):
exports.delete = function (req, res, next) {
var itemId = req.params.id;
var userId = req.user._id;
itemService.delete(itemId, userId, function (err, item) {
if (err) next(err);
return res.json(item);
});
};
The service layer (or whatever you want to call it, the layer that has all the bussiness logic) then checks if the item is owned by that user, and then deletes it or returns an error otherwise.
So I was wondering if there is any way to get the current user from any layer without passing it from the controller.
You should ensure the user owns the item before even passing it to the controller, in the routes configuration:
app.del('/api/item/1', ensureUserOwnsItem, itemController.delete);
This will cause the function ensureUserOwnsItem to be called before calling the controller.
It should looks like this:
function ensureUserOwnsItem(req, res, next) {
if (/* user owns item */) {
next();
} else {
res.send(401, 'You can\'t delete an item you don\'t own');
}
}
You would be able to reuse it on the POST route:
app.post('/api/item/1', ensureUserOwnsItem, itemController.post);
I recommend you put this function inside an AuthController or something like that.

Resources