I used meanjs to generate a project.
There are hasAuthorization functions generated automatically for crud modules. Using the articles example in app/controllers/articles.server.controller.js the authorization is something like:
exports.hasAuthorization = function(req, res, next) {
if (req.article.user.id !== req.user.id) {
return res.status(403).send('User is not authorized');
}
next();
};
I want to add in user roles so admin or owner can edit like:
exports.hasAuthorization = function(req, res, next) {
if (req.article.user.id !== req.user.id) {
var isAdmin = false;
for (var userRoleIndex in req.user.roles) {
if ('admin' === req.user.roles[userRoleIndex]) {
isAdmin = true;
}
}
if (!isAdmin) {
return res.status(403).send('User is not authorized');
}
}
next();
};
Question 1 is this secure? Or maybe better question is How secure is this?
Question 2 if it is not secure then what is the Meanjs way to make it secure?
Do I have to do something like in this SO Question or is something already built in?
You don't have to do anything like in that answer, because (if I remember fine) meanjs is using passportjs for that stuff.
If I may suggest you, you could pass role from route, so you will not have admin role hardcoded, and this method will be more reusable. You can than use it like this:
app.route('/articles/:id')
.get(users.requiresLogin, users.hasAuthorization(['user']), articles.read)
.delete(users.requiresLogin, users.hasAuthorization(['admin']), articles.delete);
Related
I have written an express authentication middleware. The first one which uses app.all('*') is used to setup a flash object which is then used to setup a locals object. The checkAdmin middleware allows the route to proceed but gives me a local variable which I can check in my ejs for displaying portions of the page that should be viewed by admin only. However, other users still have access to this page. They just cannot see everything. Therefore, in my checkAdmin() middleware, I am using return next() wheather a user is admin or not.
The middleware function:
app.all('*',middleware.checkAdmin)
middleware.isAdmin =function(req,res,next){
//Check if the admin is set to true or not
pool.query(`Select RoleID from userroles where UserID = ${req.user.id}`,function(err,rows,fields){
if(err){
console.log(err)
}
if(rows[0]){ //This should not really happen, where the role is undefined. Every user should have a role, but checking it just in case
if (rows[0].RoleID == 1){
return next()
}
}
req.flash("message", "You need to have admin previledge to acccess this page")
res.redirect('back'); //From express 4 onwards this should allow me to redirect to the same page the request came from.
})
}
middleware.checkAdmin=function(req,res,next){
//Check if the admin is set to true or not
if(req.user){
pool.query(`Select RoleID from userroles where UserID = ${req.user.id}`,function(err,rows,fields){
if(err){
console.log(err)
}
if(rows[0]){ //This should not really happen, where the role is undefined. Every user should have a role, but checking it just in case
if (rows[0].RoleID == 1){
req.flash("checkAdmin","admin")
return next()
}
}
return next()
})
}else{
return next()
}
}
app.use(function(req,res,next){
res.locals.currentUser=req.user;
res.locals.error=req.flash("error");
res.locals.success=req.flash("success");
res.locals.message=req.flash("message");
res.locals.checkAdmin=req.flash("checkAdmin"); //I am using this so that I can show the admin navbar element only if the user is signed in as admin
next()
})
My isAdmin middleware is used in routes like:
router.get("/admin", middleware.isAdmin, function (req, res) {
res.render("admin.ejs")
})
I could not find an authenticaiton setup online that solves this issue therefore I came up this this code. But I am not sure if this is an optimal way to do this.
Why not use sessions?
Then you can set the role on login and you can write middleware like this:
function authMiddleware(req, res, next) {
if(req.session.isAdmin) {
next();
}
else {
//set error message here.
res.redirect('/login');
}
}
Then you could add this with app or router .use like this:
//protected routers
app.use('/protected', authMiddleWare, (req, res) => {});
The library you should use for sessions is express-session.
I am not sure this answered your question, I hope it helped.
I have two user collections in my db and I want to make different login types for every one, so I have made two strategy on passport for my site ('local-user' & 'local-manager').
My question is how to check logged in user type (by used strategy) in my app?
In this code, passport just checks user auth but I want to check by strategy. (eg: if user logged in by 'local-manager', then show the page)
function isLoggedIn(req, res, next){
if (req.isAuthenticated()) {
next();
return;
}
res.redirect('/login');
}
It's better you use role mapping for this.
Anyway for now you can use this concept :
var passport = require('passport')
, LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy(
{passReqToCallback: true},
function(req, username, password, done) {
req.usedStrategy = 'local-user';
//do auth stuff
});
}
));
And use like this :
function isLoggedIn(req, res, next){
if (req.isAuthenticated() && req.usedStrategy === 'local-user') {
next();
return;
}
res.redirect('/login');
}
Also you can use session if you enable it in passport.
It must be said (and has been in other answers/comments) that you should really look again at your modelling of the domain. User objects can be really simple (just login information) and the rest can be broken out into other models/schemas.
Anyway on to the answer to your original question:
You can switch on the user type. Passport doesn't reach too far into the rest of your application. The log in strategies are not known about outside of the actual log in section.
You can handle that as middleware and add some extra information to the request object by checking for a unique property in one of the models:
function(request, response, next){
request.isManager = !!(request.user && request.user['unique_prop']);
next();
}
Place this after the auth middleware. Then in your route you can switch based on request.isManager. Also encapsulating this in middleware will abstract it from the user model and allow you to refactor it in the background.
Another alternative would be to add the function as a static/method/virtual (depending on the implementation) to the schema if you're using mongoose.
Hope this helps 👍 If you have further questions feel free to add comments and I can amend the answer. 🤔
I know this question is old, but I just had the same problem, and I managed to find a way to at least know what kind of account the user is. As someone else said, I don't think there is a way to see what strategy a user used to log in, but if you have a field in your user database to show what kind of account they are (e.g.: admin, client, etc.), then you can pass this information along in the req.user variable when you serialize the user:
passport.serializeUser(function(user, cb) {
process.nextTick(function() {
return cb(null, {
id: user.user_id,
username: user.user_email,
type: user.user_type
});
});
});
passport.deserializeUser(function(user, cb) {
process.nextTick(function() {
return cb(null, user);
});
});
In the example above, I'm using an SQL database, and one of the columns is called user_type. Once the user is deserialized, passport passes on the fields you ask for in passport.serialize() to req.user. So, if you wanted to know what type of account is currently logged in, you could say something like:
console.log("This user is a(n) " + req.user.type);
Or, something more realistic, if you're using Express:
app.get("/adminsOnly", (req, res) {
if (req.isAuthenticated() { // returns true if a user successfully logged in
if (req.user.type === "admin") { //check if user is an admin
res.render("adminsOnly.ejs"); //render admin section of website if true
} else {
res.redirect("/adminsLogin"); //redirected somewhere else because user is not an admin
}
} else {
res.redirect("/login"); //req.isAuthenticated() returns false, so user is redirected to the login page
}
});
I'm pretty new to coding in general, so I'm sure there are better ways to tackle this question, but this is a way to work around the problem of not being able to pinpoint which strategy the user logged in with.
i am trying to learn how to make a multi-tenant app with stormpath and node with express. This is the official document on that topic. As for now i am using express-stormpath lib to make my login and stuff. But i can not find how i do the multi-tenant.
UPDATE
I got it to work with passport stormpath strategy. I do not know if that is the right way but it works... The problem now is how do i change accountStore dynamic in the express version? It feels like a public declared variable is not so good?
var href = {
href: null
}
function hrefUrl(req, res, next){
var host = req.headers.host;
var account = host.split(".")[0];
spClient.getDirectories(function (err, directories) {
directories.each(function (dir, cb){
if(account.toLowerCase() == dir.name.toLowerCase()){
href.href = dir.href
}
cb();
}, function (err){
if(href.href == null){
return res.redirect(301, 'http://dashboard.local.dev/selectCompany');
}
next();
});
});
}
// Authenticate a user.
router.post('/login', hrefUrl, passport.authenticate('stormpath',
{
successRedirect: '/dashboard',
failureRedirect: '/login',
failureFlash: 'Invalid email or password.',
accountStore: href
}
)
);
Express-stormpath has provided APIs for you to access account information in your application. These accounts belong to directories. From the official document, you have two solutions to support multi-tenant. One is to create group per tenant, and another is to create directory per tenant.
For either solution you choose, you would have to use the APIs provided by express-stormpath to access these information associated with an account.
For example if you have created different directories for each tenant, you may need to add your business logics regarding to the multi-tenant in the postLoginHandler.
app.use(stormpath.init(app, {
postLoginHandler: function (account, req, res, next) {
account.getDirectory(function(err, directory) {
console.log(directory)
// if directory is tenant-1
// if directory is tenant-2
})
}
})
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 =)
I made my own login system in nodejs and I have couple question about that.
To check user login I make this:
function loadUser (req, res, next) {
// Check user_id
if (req.session.user_id) {
// Is there in db
User.findById({_id: req.session.user_id}, function (err, user) {
if (user) {
req.currentUser = user;
next();
} else {
res.redirect('/login');
}
});
}
}
app.get('/secure', loadUser, function (req, res) {
res.render('secure.jade', {user: req.currentUser});
});
how safe is it? Can a hacker to pick up a session key? And are there best practice to make this approach better
Well if the hacker steals the cookie of the user he can impersonate him, but that's the case for many websites. You shouldn't worry to much about that though.
Also, it's better to have the username remembered along with the user_id, no point in making two queries over the time.