How to implement role based authorization / access control in nodejs using expressjs and passport also how to design Role Middleware perfectly ?
I have two types of login Admin and User
which is best, creating two model and router in the name of admin and user?
1.Checking user isAdmin or not
2.
// To authtenticate the User by JWT Startegy
module.exports = (userType, passport) => {
let opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme('jwt');
opts.secretOrKey = config.secret;
passport.use(new JwtStrategy(opts, (jwt_payload, done) => {
if (userType == 'admin') {
Admin.getAdminById(jwt_payload.data._id, (err, user) => {
if (err) return done(err, false);
if (user) return done(null, user);
return done(null, false);
});
}
if (userType == 'users') {
User.getUserById(jwt_payload.data._id, (err, user) => {
if (err) return done(err, false);
if (user) return done(null, user);
return done(null, false);
});
}
}));
}
Why do you want to have 2 places to get user data?
Have users and assign roles to them.
The easiest access control system is: guest, logged in, admin.
So in table users add a role column with values: user, admin.
And now you write middlewares:
const isLogged = function (req, res, next) {
if (req.user)
return next ();
res.send ('unauth')
}
const isAdmin = function (req, res, next) {
if (req.user.role == 'admin')
return next ();
res.send ('only admin')
}
And usage:
app.get('/', (req, res)=>{})
app.get('/profile',isLogged, (req, res)=>{})
app.get('/admin',isLogged, isAdmin, (req, res)=>{})
For more advanced needs try find some ACL modules
Related
I am trying to authenticate multiple user through multiple strategies. I have 2 table first is User table and second is employee but it's working when i try to login it show the error
User not exist
when i use just single authentication strategy it's work but when i use multiple strategy it shows error
Auth.js
const LocalStrategy = require('passport-local').Strategy
const bcrypt = require('bcrypt')
const User = require('../models/authUserModel')
const Employee = require('../models/employeeModel')
module.exports = function (passport) {
passport.use("user-local",
new LocalStrategy({usernameField: "email"}, function (email, password, done) {
User.findOne({email: email})
.then(user => {
if (!user) {
return done(null, false, ({message: 'Email not exist'}))
}
bcrypt.compare(password, user.password, (err, isMatch) => {
if (err) throw err
if (isMatch) {
return done(null, user)
} else {
return done(null, false, ({message: 'Password incorrect'}))
}
})
}).catch(err => console.log(err))
})
)
passport.use('local',
new LocalStrategy({usernameField:"email"}, function (email, password, done){
Employee.findOne({email:"email"})
.then(user=>{
if(!user){
return done(null, false,'employee not exist')
}
bcrypt.compare(password, user.password, (err, isMatch)=>{
if(isMatch){
return done(null, user)
}
else {
return done(null, false, 'password or email is incorrect')
}
})
}).catch(err=>console.log(err))
})
)
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
}
Login.js
let express = require('express');
let router = express.Router();
const passport = require('passport')
router.get('/login', function(req, res, next) {
res.render('login');
})
router.post('/login', function (req, res, next) {
passport.authenticate("user-local", function (err, user, info) {
if (err) {
return next(err)
}
if (!user) {
console.log('User not exist')
return res.render('login')
}
req.logIn(user, function (err) {
if (err) {
return next(err)
}
req.session.isLoggedIn = true
req.session.user = user
req.session.save(err => {
console.log(err)
if (req.isAuthenticated()) {
return res.redirect('/customerMenu')
}
console.log('user not exist')
return res.render('login')
})
})
})(req, res, next)
passport.authenticate('local', function (err, user, info){
if(err){
return next(err)
}
if(!user)
{
console.log("employee not exist")
return res.render('login')
}
req.logIn(user,function (err){
if(err){return next(err)}
req.session.isLoggedIn = true
req.session.user = user
req.session.save(err=>{
console.log(err)
if (req.isAuthenticated()) {
return res.redirect(200,'/employeeMenu')
}
console.log('user not exist')
return res.render('login')
})
})
})(req, res, next)
})
function isLoggedIn(req, res, next){
if(req.isAuthenticated()){
req.isLogged = true
return next();
}
else{
req.isLogged = false
return next()
}
}
module.exports = isLoggedIn
module.exports = router
I think you need to edit your .serializeUser() and .deserializeUser() functions so that passport knows what type of user you are trying to serialize and deserialize.
The way I have done it in the past is by using a SessionConstructor that puts both the user's id and the type of user they are inside an object. You can then use this in your deserialize function to figure out which usertype you are trying to sign in.
function SessionConstructor(userId, userGroup){
this.userId = userId;
this.userGroup = userGroup;
}
passport.serializeUser((user, done) => {
let userGroup = 'user';
if (//condition to tell if it is the other user type){
userGroup = 'employee';
};
let sessionConstructor = new SessionConstructor(user.id, userGroup)
done(null, sessionConstructor);
});
passport.deserializeUser((sessionConstructor, done) => {
if (sessionConstructor.userGroup == 'user'){
User.findById(sessionConstructor.userId).then(user => done(null, user));
} else if (sessionConstructor.userGroup == 'employee'){
Employee.findById(sessionConstructor.userId).then( employee=> done(null, employee));
}
});
You will however need to think of a condition for the if statement inside of .serializeUser() that will tell you which type of user it is. If you include a unique identifier inside of the user object that is sent to .serializeUser(), then you can check that to tell. For example have a key called employee inside your user object where the value is true if its an employee submitting it, and false otherwise. If you choose this implementation it would look something like this.
passport.serializeUser((user, done) => {
let userGroup = 'user';
if (user.employee){
userGroup = 'employee';
};
let sessionConstructor = new SessionConstructor(user.id, userGroup)
done(null, sessionConstructor);
});
Outisde of that I think the below code should work, but feel free to ask any questions about this method and I'll do my best to answer!
Here is a link to where I learned about how to do this.
I already checked multiple answers on Stackoverflow, and also went through on the documentation but I still cannot figure out my problem. when I try to sign in and signup it's working perfectly I have my token. It's nightmare to just fetch my current_user get('/isAuth') I get undefined !!!!
const Authentication = require("../controllers/authentication");
const passport = require("passport");
const requireAuth = passport.authenticate('jwt', {session: false});
const requireSignin = passport.authenticate('local', {session: false});
module.exports = app => {
app.post('/signup', Authentication.signup);
app.post('/signin', requireSignin, Authentication.signin);
// Current User is undefined !!!!!
app.get('/isAuth', Authentication.fetchUser);
my passport.js
const keys = require("../config/keys");
const passport = require("passport");
const User = require("../models/User");
const JwtStrategy = require("passport-jwt").Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const localStrategy = require("passport-local");
// Create local strategy
const localOptions = { usernameField: "email" };
const localLogin = new localStrategy(localOptions, function(email,password,done) {
// verify this username and password, call done with the user
// if it is the correct username and password
// otherwise, call done with false
User.findOne({ email: email }, function(err, user) {
if (err) {return done(err);}
if (!user) {return done(null, false);}
// compare passwords - is password is equal to user.password?
user.comparePassword(password, function(err, isMatch) {
if (err) {return done(err);}
if (!isMatch) {return done(null, false);}
return done(null, user);
});
});
});
// setup option for jwt Strategy
const jwtOptions = {
jwtFromRequest: ExtractJwt.fromHeader('authorization'),
secretOrKey: keys.secret
};
// Create Jwt strategy
const jwtLogin = new JwtStrategy(jwtOptions, function(payload, done) {
// See if the user Id in the payload exists in our database
// If does, call 'done' with that other
// otherwise, call done without a user object
User.findById(payload.sub, function(err, user) {
if (err) {return done(err, false);}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
});
// Tell passport to use this strategy
passport.use(jwtLogin);
passport.use(localLogin);
// Generate token
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id).then(user => {
done(null, user);
});
});
./controller/authentication.js
const User = require('../models/User');
const jwt = require('jwt-simple');
const config = require('../config/keys');
function tokenForUser(user){
const timestamp = new Date().getTime();
return jwt.encode({sub: user.id, iat: timestamp}, config.secret);
}
exports.signup = function(req,res,next){
console.log(req.body)
const email = req.body.email;
const password = req.body.password;
if(!email || !password){
return res.status(422).send({error: 'You must provide email and password'});
}
// See if user with the given email exists
User.findOne({email: email}, function(error, existingUser){
if (error){return next(error)};
// if a user with email does exist, return an error
if (existingUser){
return res.status(422).send({error: 'Email is in use'});
}
// if a user with email does not exist, create and save record
const user = new User({
email: email,
password: password
});
user.save(function(error){
if (error){return next(error);}
// respond to request indicating the user was created
res.json({token: tokenForUser(user)});
})
})
}
exports.signin = function (req,res,next){
// user has already had their email and password auth
// we just need to give them a token
res.send({token: tokenForUser(req.user)});
}
// here is my problem...
exports.fetchUser = function (req, res, next) {
console.log('this is ',req.user)
};
Still stuck for many days... it's a nightmare!!! if someone has the solution.
after sign in if I want to go to my route /isAuth to check my user data:
Have your tried using a middleware that calls the isAuthenticated function on req object? This function is added by passport and is generally the recommended way to check if a request is authenticated.
function isLoggedIn(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect("/");
}
Then you can call this function when a user hits your isAuth route:
app.get('/isAuth', isLoggedIn, Authentication.fetchUser);
I've set up express server, i'm using passport.js
i'm using a local strategy right now where i'm authenticating the user
passport.use(new Strategy({
usernameField: 'email'
}, (email, password, cb) => {
db.get().collection('user').findOne({email: email}, (err, user) => {
if (err) {
return cb(err)
}
if (!user) {
return cb(null, false)
}
if (!bcrypt.compareSync(password, user.password)) {
return cb(null, false)
}
return cb(null, user)
})
}))
i'm creating user in the router like this:
router.post('/signup', (req, res) => {
const user = poster.user(req.body)
if(user){
db.get().collection('user').insert(user)
res.send('success')
}else{
res.send('error')
}
// res.send(req.body)
})
my question is that how i can create a user with oauth? Or shall i even create a user in my db when i'm using oauth? I want users to be able to login with facebook, twitter and google.
I am currently trying to set up an admin role in order to access a simple admin page using the following documentation provided via : connect-roles
I ave been banging my head against it for a while and am still lost on how to set a role E.G As of right now am pulling a admin value out of the DB and storing it in a global var for the time being but I have no idea how to use that with connect-roles say to only allow access to my admin page for a specific user.
Can anyone clarify or show an example on how to do this/some guidance as I documentation didn't help me to ensure access to a web page only if the user is an admin?
Ave posted some of the code kinda showing what it looks like at the moment.
Code
var admin = 'Admin';
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : '',
database : 'test'
});
var passport = require('passport');
var ConnectRoles = require('connect-roles');
var roles = new ConnectRoles();
var passportLocal = require('passport-local');
app.use(passport.initialize());
app.use(passport.session());
app.use(roles.middleware());
passport.use(new passportLocal.Strategy(function (username, password, done) {
connection.query({
sql : 'SELECT * from `userman_users` WHERE `username`= ?AND`password` = sha1(?)',
timeout : 40000, // 40s
values : [username, password]
}, function (error, results, rows) {
if (results.length > 0) {
response = "Success";
} else {
console.log('Error while performing Query.');
response = "Failed";
}
if (response === "Success") {
done(null, {
id : username
});
} else if (response === "Failed") {
done(null, null);
}
});
})
);
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
done(null, {
id : id
});
});
roles.use(function (req, action) {
if (!req.isAuthenticated()) return action === 'access home page';
})
roles.use(function (req) {
if (req.user.role === 'admin') {
return true;
}
});
app.get('/', redirectToIndexIfLoggedIn, function (req, res) {
res.render('login');
});
app.get('/index', checkLoggedIn, function (req, res) {
res.render('index', {
isAuthenticated : req.isAuthenticated(),
user : req.user
});
});
app.get('/admin', user.can('access admin page'), function (req, res) {
res.render('admin');
});
function checkLoggedIn(req, res, next) {
if (req.isAuthenticated())
return next();
res.redirect('/');
}
this is an example:
var express = require('express');
...
var passport = require('passport');
var LocalStrategy = require('passport-local');
var ConnectRoles = require('connect-roles');
...
var app = express();
//===============PASSPORT=================
// Passport session setup.
passport.serializeUser(function(user, done) {
console.log("serializing " + user.username);
done(null, user);
});
passport.deserializeUser(function(obj, done) {
console.log("deserializing " + obj);
// simulate an admin user
obj.role = obj.username == 'admin' ? 'admin' : 'user';
done(null, obj);
});
...
//===============CONNECTION RULES=================
var user = new ConnectRoles({
failureHandler: function (req, res, action) {
// optional function to customise code that runs when
// user fails authorisation
var accept = req.headers.accept || '';
res.status(403);
if (~accept.indexOf('html')) {
res.render('access-denied', {action: action});
} else {
res.send('Access Denied - You don\'t have permission to: ' + action);
}
}
});
...
app.use(passport.initialize());
app.use(passport.session());
app.use(user.middleware());
//anonymous users can only access the home page
//returning false stops any more rules from being
//considered
user.use(function (req, action) {
if (!req.isAuthenticated()) return action === 'access home page';
});
//users logged can access to public pages
user.use(function(req, action){
if(req.isAuthenticated() && action != 'access private page' && action != 'access admin page')
return true;
});
//moderator users can access private page, but
//they might not be the only ones so we don't return
//false if the user isn't a moderator
user.use('access private page', function (req) {
console.log('access private page');
if (req.user.role === 'moderator') {
return true;
}
});
//admin users can access all pages
user.use(function (req) {
if (req.user.role === 'admin') {
return true;
}
});
...
/* GET home page. */
app.get('/', user.can('access home page'), function(req, res, next) {
res.render('index', { title: 'Express' });
});
//displays our signup page
app.get('/signin', function(req, res){
res.render('signin');
});
//sends the request through our local signup strategy, and if successful takes user to homepage, otherwise returns then to signin page
app.post('/local-reg', passport.authenticate('local-signup', {
successRedirect: '/',
failureRedirect: '/signin'
})
);
//sends the request through our local login/signin strategy, and if successful takes user to homepage, otherwise returns then to signin page
app.post('/login', passport.authenticate('local-signin', {
successRedirect: '/',
failureRedirect: '/signin'
})
);
// Simple route middleware to ensure user is authenticated.
app.use(function(req, res, next) {
if (req.isAuthenticated()) { return next(); }
req.session.error = 'Please sign in!';
res.redirect('/signin');
});
//logs user out of site, deleting them from the session, and returns to homepage
app.get('/logout', function(req, res){
var name = req.user.username;
console.log("LOGGIN OUT " + req.user.username)
req.logout();
res.redirect('/');
req.session.notice = "You have successfully been logged out " + name + "!";
});
app.get('/private', user.can('access private page'), function (req, res) {
res.render('private');
});
app.get('/admin', user.can('access admin page'), function (req, res) {
res.render('admin');
});
app.use('/users', users);
....
module.exports = app;
With connect-rules you define the rules do you want to use (user.use in this case). If you pass an action as first parameter the strategy is only used if the action passed in the function is equal to it. Then you trigger the rules in the routes with user.can passing the action. In this example I define an extra filter strategy to grant access to users that are logged and request routes that are not marked with admin or moderator privileges e.g
/* GET home page. */
app.get('/', user.can('access home page'), function(req, res, next) {
res.render('index', { title: 'Express' });
});
After the user is logged, we need to have another strategy in case the user isn't admin or moderator.
U can use framework like sailsJS and npm module sails-generate-auth
And after setup, use your own middleware to block routes
//allow admin only localhost:PORT/admin at policies.js
'admin': ['passport', 'sessionAuth', 'isAdmin'],
'*': ['passport', 'sessionAuth'],
//isAdmin policy
module.exports = function(req, res, next) {
// User is allowed, proceed to the next policy,
// or if this is the last policy, the controller
if (req.user.role == 'admin') {
return next();
}
// User is not allowed
return res.forbidden('You are not permitted to perform this action.');
};
Using the following logic I was able to have admin functionality based on value within the DB:
app.get('/admin', function (req, res) {
connection.query({
sql : 'SELECT role from `auth_users` WHERE `username`= ?',
timeout : 40000, // 40s
values : [req.user['id']]
}, function (error, results, rows) {
if (results[0]['role'] === "admin") {
admin = (results[0]['role']);
res.render('admin', {
isAuthenticated : req.isAuthenticated(),
user : req.user
});
} else {
admin = "";
res.redirect('/index');
}
})
});
I am trying to make a node.js web application that tells the user to sign in using their gmail.
So I tried to use the instructions over here: http://passportjs.org/guide/google/. I changed the url www.example.com to localhost, then ran the application. It tells me that it can't find User. Here is the whole log: User.findOrCreate({openID: identifier }, function(err, user) {(and then on the next line) ReferenceError: User is not defined.
You need to define "User" by calling it from a model. Create a User model (if you haven't already) and import it as a variable. E.g.
var User = require('/path/to/User');
Sometimes I find it helpful for debugging to log the callback to the console, to see if the desired output is being spit out.
I just implemented one maybe this will help , I'm using Express the routes section is on the bottom.. Remember to set your host in the Google Key, my App has de full url of the AWS Server
var passport = require('passport');
// ====== Passport and OAuth2 API
var GoogleStretegy = require('passport-google-oauth').OAuth2Strategy;
passport.serializeUser(function (user, done) {
done(null, user);});
passport.deserializeUser(function (obj, done){
done(null, obj);});
// Set Passport Initialize and Sessions
app.use(passport.initialize());
app.use(passport.session());
passport.use(new GoogleStretegy({
clientID: CREDENTIALS.google.GOOGLE_CLIENT_ID,
clientSecret: CREDENTIALS.google.GOOGLE_CLIENT_SECRET,
callbackURL:"<host>/oauth2callback"
},
function (req, accessToken, refreshToken, profile, done) {
process.nextTick(function () {
console.log(JSON.stringify(profile));
console.log(req);
var username= profile.emails[0].value.split('#');
User.findOne({email: profile.emails[0].value,username:username[0]}).exec(function (err,user) {
if(!user){
var user = new User({username: username[0]});
user.set('email',profile.emails[0].value);
user.set('FullName',profile.DisplayName);
user.save(function (err) {
if(err){
console.log(err);
profile=null;
return done(null,profile);
}else {
return done(null, profile);
}
});
}else {
return done(null, profile);
}
});
// return done(null, profile);
});
}
));
/// ROUTES !
router.get('/logout', function (req, res) {
req.session.destroy(function () {
// Google log out
req.logout();
res.redirect('/login');
});
});
//Google OAuth2
router.get('/auth/google',passport.authenticate('google', { scope: ['https://www.googleapis.com/auth/userinfo.profile','https://www.googleapis.com/auth/userinfo.email'] }));
router.get('/oauth2callback', passport.authenticate('google', { failureRedirect: '/login' }), function (req, res) {
res.redirect('/');
});