I want to verify that a user has a role which allows him/her to use an endpoint in my API. Usually I would go about doing so by taking the userId sent as part of a JWT and do a lookup on the DB to see what the user's role is. It would happen inside an API call and would look something like this:
var userId = getUserIdFromJwt();
app.models.User.findOne({_id: userId}, function (err, user) {
...check if user is in role...
});
Now I want to try and move that code to a piece of middleware, which would look like this:
exports.isUserInRole = function(app, allowableRoles) {
var userId = getUserIdFromJwt();
app.models.User.findOne({_id: userId}, function (error, user) {
if (error) {
return res.status(500).json(error);
}
return function (req, res, next) {
if(_.includes(allowableRoles, user.Role)) {
next();
} else {
return res.status(401).json({"error": "User not in role"});
}
}
});
};
The middleware would be implemented like this:
const allowableRoles = ['admin', 'implementor'];
app.get('/getStuff/', isUserInRole(app, allowableRoles), function (req, res) {
... do stuff if user is in role ...
});
At this point I am running into a problem where the app.models.User value is always undefined.
I do not understand why app.models.User is undefined at this point as I can access it within the anonymous function inside the get call.
How would I go about about access the DB from within my middleware if I cannot send it app.models.User?
For reference, I am using Mongoose and exposing it to my app in the server.js from which I access the a MongoDB db.
I think you're problem is that you are trying to get models before they are actually initialized, as you are binding the app to the parameters on initialization of the app.
So in your main app file, I would do module.exports = app; and then in your middleware file, simply include the app by doing var app = require('./path/to/app');. Then remove the app from the middleware. You might end up with something like this:
var app = require('./path/to/app');
exports.isUserInRole = function(allowableRoles) {};
And in your route change it to this:
const allowableRoles = ['admin', 'implementor'];
app.get('/getStuff/', isUserInRole(allowableRoles), function (req, res) {}
And lastly in your app file, add the app to the exports:
module.exports = app;
EDIT:
If you're using Mongoose, you can also do something more simple like this:
var mongoose = require('mongoose');
var User = mongoose.model('User');
My first guess would be b/c you are actually calling the function instead of just passing it in. Additionally, you're not actually passing in middleware. Middleware functions look like
function(req,res,next){}
Additionally, you probably want to leverage sessions instead of hitting the database on each request
Related
I am trying to pass a Boolean value from one file to another in a Node.js application.
auth.js
const express = require("express");
const router = express.Router();
var isLoggedIn = false;
router.get("/", (req, res) => {
res.render("pages/auth");
console.log(isLoggedIn);
});
router.post("/", (req, res) => {
if (req.body.password == "Secret") {
isLoggedIn = true;
res.redirect('/home');
} else {
res.send("<h1 align='center'>Wrong password</h1>");
}
console.log(isLoggedIn);
});
module.exports = {
auth: router,
isLoggedIn: isLoggedIn,
};
If the password is correct, the value of isLoggedIn should be true but if I import it in another file, it turns false.
How do I fix this?
I don't mind installing another package.
You are creating and exporting the object at the moment the server starts up - and at the moment the server starts up (that is, that the top-level code here runs), the value assigned to isLoggedIn is false.
While you could kind of fix it by exporting a function that, when called, returns the value of the variable:
module.exports = {
auth: router,
getIsLoggedIn: () => isLoggedIn,
};
A more fundamental problem is that the variable is being set for the lifetime of the server being online, for all users. If, for example, user A logs in, unregistered person B who accesses the website from some other browser halfway around the world shouldn't be seen as logged in. The isLoggedIn variable should be ditched entirely (unless the logic you want is that once one person logs in, the server sees everyone hitting your endpoints as being logged in - which sounds very unusual).
Use something like session variables instead, so each person accessing the website has separate settable and retrievable state.
I've just made an Node.js app modular by splitting up data models and routes into separate files.
My routes are exported by express.Router(). In these routes I would like to import queried values from my app.js to be rendered with the templates.
How would I in the easiest way save things lets say with app.locals or req.variableName?
Since the route using express.Router() ties it together with app.js, should I be using app.params() and somehow make these values accessible?
Using globals seems like a worse idea as I'm scaling up the app. I'm not sure if best practice would be saving values to the process environment either using app.locals.valueKey = key.someValue...
Big thanks in advance to anyone
If I understand the question correctly, you want to pass a value to a later middleware:
app.js:
// Let's say it's like this in this example
var express = require('express');
var app = express();
app.use(function (req, res, next) {
var user = User.findOne({ email: 'someValue' }, function (err, user) {
// Returning a document with the keys I'm interested in
req.user = { key1: value1, key2: value2... }; // add the user to the request object
next(); // tell express to execute the next middleware
});
});
// Here I include the route
require('./routes/public.js')(app); // I would recommend passing in the app object
/routes/public.js:
module.export = function(app) {
app.get('/', function(req, res) {
// Serving Home Page (where I want to pass in the values)
router.get('/', function (req, res) {
// Passing in the values for Swig to render
var user = req.user; // this is the object you set in the earlier middleware (in app.js)
res.render('index.html', { pagename: user.key2, ... });
});
});
});
I am building an app with nodejs and expressjs. For authentication and user roles I am using respectively passport and connect-roles.
I have built the connect-roles object as shown in here: https://github.com/ForbesLindesay/connect-roles
Like so:
var user = new ConnectRoles()
This is in my app.js. Then I have exported such object like so:
exports.user = user;
However, if I import this object with require I see a strange behavior, please check code below:
var express = require('express');
var router = express.Router();
var user = require('../app.js');
//1
console.log(user);
/* GET users listing. */
router.get('/', function(req, res) {
//2
console.log(user);
res.send('respond with a resource');
});
module.exports = router;
The object is undefined in case 1 and is as it should be in case 2. Basically, I get 2 different objects depending if I am inside or outside the router.get function. I have been debugging this for half day, but I can't figure out what is happening.
The issue is that this object should be injected to provide roles management like so:
router.get('/', user.is("admin"), function(req, res) {
Of course this gives an error since user outside the get function object is undefined. The error is "cannot call method is of undefined".
The problem you have is most likely a cyclic dependency. Your router file requires your app.js file and your app.js file requires your router. What this means is that your router file gets a partially initialised object, that is only later filled in. The best way around this is to factor out the roles into a separate module, then require it from both places. i.e. have an authorisation.js file that looks like:
var ConnectRoles = require('connect-roles');
var user = new ConnectRoles();
// set up all the authorisation rules here
module.exports = user;
Then in both app.js and the router file, do var user = require('./authorisation.js');
This is a general problem whenever you create cyclic dependencies, and is not specific to connect-roles.
I can't see how user would be undefined there... but I can suggest that you use
module.exports = user
As this will give you the object:
{ functionList: [],
failureHandler: [Function: defaultFailureHandler],
async: false,
userProperty: 'user' }
Rather than:
{ user:
{ functionList: [],
failureHandler: [Function: defaultFailureHandler],
async: false,
userProperty: 'user' } }
With your current implementation you could do:
router.get('/', user.user.is("admin"), function(req, res) {
If this does not solve your issue you may need to provide app.js in it's entirety.
I'm using sessions and cookies to authenticate the users. I would like to check for users having a cookie and if so i will set the sessions variables.
So basicly what i do is :
Check if sessions variables exist
If not, check if user has cookie
If he has a cookie, I compare the value in my database.
If everything's ok, I set up the session.
Now i'd like to have that process into a module so i don't have to paste that code into each routes of my site.
Let's say I've put all that code in a middleware route located at routes/middleware/check_auth.js.
How do I export this module so I can check in my route page if the user has auth or not, something like :
//routes/index.js
var check_auth = require('./middleware/check_auth');
module.exports = function(app){
app.get('/', check_auth, function(req, res){
if(variable_from_check_auth == true){
res.render('index_with_auth');
}else{
res.render('index_without_auth');
}
});
};
Btw, I'm not sure if it's the right way to do or if I simply have to :
Call the module on each routes.
Check for some sessions variables before rendering.
If someone could help me!
You can just export your middleware as simple as this(assuming you are using express session handler and cookie parser):
var userModel = require('./user');
module.exports = function check_auth(res, req, next) {
if (!res.session) {
req.send(401);
return;
}
userModel.isAuthenticated(req.session.id, function (result) {
if (!result) {
req.send(401);
return;
});
next();
});
};
I am working on a nodejs app.
Folder structure is
app
app.js
package.json
../model/schema.js
../controller/controller.js
../views
All the logic is in controller.js while app.js performing routing itself....
I want to know how to get/return data(object) from controller.js to app.js.
I am using 'return' to send mongodb document from controller to app but its undefined.
Heres code of app.js..i have removed unneccessary code
var express = require('express'),
app = express.createServer(express.logger()),
io = require('socket.io').listen(app),
routes = require('./routes');
var controller=require('./controller/controller');
var model=require('./model/schema');
app.get("/", function(req, res) {
res.render("chatroom.html");
});
io.sockets.on('connection', function (socket) {
socket.on('login',function(user)
{
var onliner=controller.loginUser(user);
console.log("Onlinersss: ",onliner);
});
socket.on('registerUser',function(user){
controller.registerUser(user);
});
});
Heres controller.js code:
var model=require('../model/schema');
exports.loginUser=function(user)
{
model.registerUser.findOne({uname:user.uname,pass:user.pass},function(err,doc){
if(!err){
console.log("Here loggedin: ",doc);
return doc;
}
else
console.log("Invalid username or password");
});
};
I've just pushed a project to GitHub that uses pretty much the same libraries and RethinkDB as the database (very similar to MongoDB). Basically you need to pass callbacks to your controller and have them invoked with the data retrieved from the DB. Take a look at app.js and lib/db.js in the linked project.
JavaScript (and therefore node.js) is asynchronous. When you 'return doc' the anonymous function defined at function(err, doc) is returned... not the function loginUser that you are trying to get data from.
loginUser returns immediately, and it returns undefined (since you don't specify anything else). To see what I mean, put 'return true;' as the last line in loginUser and you'll notice that you get the value back of 'true.'
However, you don't want to return a value. You want to callback with a value. That's beyond the scope of a stackoverflow answer, so here is a good resource:
http://bjouhier.wordpress.com/2011/01/09/asynchronous-javascript-the-tale-of-harry/