I have used passport local for authenticate purpose but not working.It is working only one from these two(user and admin).I do not know why it is working like this.If anyone know please help to find solution.
passportconfig.js:
const passportuser = require('passport');
const localStrategyuser = require('passport-local').Strategy;
const mongooseuser = require('mongoose');
const passportAdmin = require('passport');
const localStrategyAdmin = require('passport-local').Strategy;
const mongooseadmin = require('mongoose');
var User = mongooseuser.model('User');
var Admin = mongooseadmin .model('Admin');
passportuser.use(
new localStrategyuser({ usernameField: 'email' },
(username, password, done) => {
User.findOne({ email: username },
(err, user) => {
if (err) { return done(err); } else if (!user) {
return done(null, false, { message: 'Email is not registered for User' });
} else if (!user.verifyPassword(password)) {
return done(null, false, { message: 'Wrong password.' });
} else {
return done(null, user);
}
});
})
);
passportAdmin.use(
new localStrategyAdmin({ usernameField: 'email' },
(username, password, done) => {
Admin.findOne({ email: username },
(err, admin) => {
if (err)
return done(err);
// unknown user
else if (!admin)
return done(null, false, { message: 'Email is not registered for Admin' });
// wrong password
else if (!admin.verifyPassword(password))
return done(null, false, { message: 'Wrong password.' });
// authentication succeeded
else
return done(null, admin);
});
})
);
Like #Panther mentioned in the comment, passportuser and passportAdmin is just the same module, you have to create separate Passport instances instead of using the default one
const { Passport } = require('passport');
const passportuser = new Passport();
const passportAdmin = new Passport();
And also as #Panther mentioned, there is no need to require('mongoose') multiple times. This would work equally well:
const mongoose = require('mongoose');
const User = mongoose.model('User');
const Admin = mongoose.model('Admin');
Related
I have the following middleware that works for authentication with JWT and passport.js. The thing is that I also need somehow verify for all controllers if the user is admin or not. I am using this passport.js middleware for authentication:
if (typeof app == "function") {
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function (user, done) {
done(null, JSON.stringify(user));
});
passport.deserializeUser(function (user, done) {
done(null, JSON.parse(user));
});
var opts = {};
opts.jwtFromRequest = passportJwtExctract.fromAuthHeaderAsBearerToken();
opts.secretOrKey = process.env.JWT_SECRET;
passport.use(
new passportJwtStrategy(opts, async (jwt_payload, done) => {
var user = await User.findByPk(jwt_payload.id);
if (user === null) {
return done(null, false, {
message: "Sorry, we couldn't find an account.",
});
}
done(null, user);
await User.update(
{ last_signin_date: "now()" },
{
where: {
id: user.id,
},
}
);
return;
})
);
passport.use(
new passportLocalStrategy(
{
usernameField: "email",
passwordField: "password",
},
function (username, password, done) {
process.nextTick(async function () {
var valid =
validator.isEmail(username) && validator.isLength(password, 8);
if (!valid) {
return done(null, false, {
message: "Incorrect username or password",
});
}
username = username.toLowerCase();
let user = await User.findOne({ where: { email: username } });
user = user.toJSON();
if (user === undefined) {
return done(null, false, {
message: "Sorry, we couldn't find an account with that email.",
});
}
var hashed_password = await bcrypt.hash(password, user.salt);
if (hashed_password == user.password) {
delete user.password;
delete user.salt;
user.user_mode = process.env.USER_MODE;
user.token = jwtLib.sign(user, process.env.JWT_SECRET);
//l('done user', user)
done(null, user);
await User.update(
{ last_signin_date: "now()" },
{
where: {
id: user.id,
},
}
);
return;
}
return done(null, false, {
message: "Sorry, that password isn't right.",
});
});
}
)
);
}
How can I verify JWT correctly for all related requests and be sure that the user is admin? Something like the bellow option.
Common.ensureAuthenticated("Administrator"),
you can investigate about Outh2 authentication where in JWT token you can claim number of parameter according to need and at time of verification you can validate it and extract and use it everywhere you want !
For admin and different different role you can define "actor" as key and its role in respect to priority as value and create a check actor flag at run time !
I'm assuming your application starts from index.js In index.js, you can use a middleware before initiating your routes.
For example:
const express = require('express');
const app = express();
const router = require('./src/router'); // Your router file (router.js)
app.use(AuthMiddleware); // This is how you use your middleware
app.use('/', router);
This is the error I'm getting in passport.js config. I don't understand what it means: Why passport.use is not a function?
TypeError: passport.use is not a function
This is my code:
const LocalStrategy = require('passport-local').Strategy;
const passport = require('passport');
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
// load user models
const User = require('../models/Users');
module.exports = function (passport) {
passport.use (
new LocalStrategy({ usernameField: 'email' }, (email, password, done) => {
// Match User
User.findOne({ email: email })
.then((user) => {
if (!user) {
return done(null, false, { message: 'Email is not registered' })
}
})
.catch((err) => {
console.log(err);
})
// match password
bcrypt.compare(password, user.password, (err, isMatch) => {
if (err) throw (err)
if (isMatch) {
return done(null, user)
} else {
return done(null, false, { message: 'Incorrect Password' })
}
})
})
);
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser( (id, done) => {
User.findById(id, (err, user) => {
done(err, user);
});
});
}
passport.js
const passport = require("passport")
const LocalStrategy = require('passport-local').Strategy;
const User = require('./models/user');
// Local Strategy
passport.use(new LocalStrategy({
usernameField: 'email'
}, async (email, password, done) => {
try {
// find the user given the email
const user = await User.findOne({ "email": email });
// if not, handle it
if (!user) {
return done(null, false);
}
// check if password is correct
const isMatch = await user.isValidPassword(password);
// if not handle it
if (!isMatch) {
return done(null, false);
}
// otherwise return the user
done(null, user);
} catch (error) {
done(error, false);
}
}));
Within my project I have both hcpuser and regular user. I have got the registration working for HCP but when i go to do my login function it still only reads from my users collection and not from the hcpuser I want it to. Is there a simple line of code I can declare before my function that allows this.
Hcp model:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var bcrypt = require('bcryptjs');
var User = require('../model/user.model').schema
var schema = new Schema({
email : {type:String, require:true},
username: {type:String, require:true},
password:{type:String, require:true},
creation_dt:{type:Date, require:true},
hcp : {type:Boolean, require : true},
clinic:{type:String, require:true},
patients: [User],
});
schema.statics.hashPassword = function hashPassword(password){
return bcrypt.hashSync(password,10);
}
schema.methods.isValid = function(hashedpassword){
return bcrypt.compareSync(hashedpassword, this.password);
}
schema.set('collection', 'hcpuser');
module.exports = mongoose.model('Hcpuser',schema);
Hcp controller with first register function working as expected.
const express = require("express");
const mongoose = require("mongoose");
const cors = require("cors");
const router = express.Router();
const Hcpusermodel = mongoose.model("Hcpuser")
const {ObjectId} = require("mongodb");
var Hcpuser = require('../model/hcp.model')
var passport = require('passport');
router.post('/register', function (req, res, next) {
addToDB(req, res);
});
async function addToDB(req, res) {
var hcpuser = new Hcpuser({
email: req.body.email,
hcp : true,
username: req.body.username,
password: Hcpuser.hashPassword(req.body.password),
clinic: req.body.clinic,
creation_dt: Date.now()
});
try {
doc = await hcpuser.save();
return res.status(201).json(doc);
}
catch (err) {
return res.status(501).json(err);
}
}
//login
router.post('/login', function(req,res,next){
passport.authenticate('local', function(err, hcpuser, info) {
if (err) { return res.status(501).json(err); }
if (!hcpuser) { return res.status(501).json(info); }
req.logIn(hcpuser, function(err) {
if (err) { return res.status(501).json(err); }
return res.status(200).json({message:'Login Success'});
});
})(req, res, next);
});
From your question, you either want to auth one OR the other, or check both - I think you're asking for how to auth separately (one OR the other, not both)?
please note, this specific code has been untested, but the principles are there and still stand.
One OR the Other
You need to define the name of each strategy in your passport code.
For example:
passport.use('users', new LocalStrategy({
usernameField: 'user[email]',
passwordField: 'user[password]',
},(email, password, done) => {
Users.findOne({ email })
.then((user) => {
if(!user || !user.validatePassword(password)) {
return done(null, false, { errors: { 'email or password' : 'is valid' } });
}
return done(null, user);
}).catch(done);
}));
passport.use('hcpusers', new LocalStrategy({
usernameField: 'user[email]',
passwordField: 'user[password]',
},(email, password, done) => {
HCPUser.findOne({ email })
.then((user) => {
if(!user || !user.validatePassword(password)) {
return done(null, false, { errors: { 'email or password' : 'is valid' } });
}
return done(null, user);
}).catch(done);
}));
And then in your passport.authenticate method, specify the strategy name:
passport.authenticate('users', function(err, user, info) { ...
and
passport.authenticate('hcpusers', function(err, user, info) { ...
In this case you'll need two separate endpoints for each login method, or just an extra parameter specifying which one to check from an if statement.
Update
For your comment of not knowing where the passport code should be, this is up to you. However, I like to keep passport code in an 'auth' folder and add the following code to a passport.js file:
const mongose = require('mongoose');
const passport = require('passport');
const LocalStrategy = require('passport-local');
const Users = mongose.model('Users');
passport.use('...', new LocalStrategy({
...
...
}));
Include this in your server/index/app.js (whatever yours is) app.use(passport.initialize());
You can then just use the passport code as normal in your user controllers.
My passport.authenticate code looks like:
return passport.authenticate('local', function(err, passUser, info) {
if (err) {
return next(err);
}
if (!passUser) {
return res.status(503).send('error');
}
const user = passUser;
user.token = user.generateJWT();
return res.json({ token: user.token });
})(req, res, next);
But this can be different for you (i.e. you may not be using sessions?) Either way, if authenticated, just send the response to client so it can proceed.
Hi so to solve this issue i followed what was mentioned. I needed to define the name of the collection within the hcp model using this:
module.exports = mongoose.model('Hcpuser', Hcpuser, 'Hcpuser');
I then created a local strategy ensuring that i was searching using the right model which would then point to the right collection within my DB.
Solution:
var passport = require('passport')
, LocalStrategy = require('passport-local').Strategy;
passport.use('hcplocal', new LocalStrategy(
function(uemail, password, done) {
Hcpuser.findOne({ "email" : uemail }, function(err, user) { console.log(user)
if (err) { return done(err); }
if (!user) {
console.log(user);
console.log(err);
console.log(uemail)
return done(null, false, { message: 'Incorrect email.' });
}
if (!user.isValid(password)) {
console.log(user);
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
router.post('/login',function(req,res,next){
passport.authenticate('hcplocal', function(err, user, info) {
if (err) { return res.status(501).json(err); }
if (!user) { return res.status(501).json(info); }
req.logIn(user, function(err) {
if (err) { return res.status(501).json(err); }
console.log(user);
return res.status(200).json({message:'Login Success'});
});
})(req, res, next);
});
I try to authentificate on a /login route with passport, I give an email, and a password (already stored in the database -email + hashed password with bcrypt). However, when I try to authentificate, my code never go into the passport.use...
const passport = require("passport");
const LocalStrategy = require("passport-local").Strategy;
const db = require("../config/database");
/* Route methods */
exports.login = (req, res) => {
const email = req.body.email;
const password = req.body.password;
console.log("It will be displayed");
passport.use(
new LocalStrategy(function(email, password, done) {
console.log("It won't");
db.User.findOne({ email: email }, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, { message: "Incorrect email." });
}
if (!bcrypt.compareSync(password, user.dataValues.password)) {
return done(null, false, { message: "Incorrect password." });
}
return done(null, user);
});
})
);
};
Furthermore, I create an API, and I'm wondering how authentificate someone (with session) with a REST API. Do I have to send on a endpoint the email and the password, then I create a session ? Thank you if you have any ressources.
This happens because you have only declared the actual Strategy but you'll need to create a separate route where you will authenticate the user using this local strategy with passport.authenticate.
Take a look at the example app I've created: https://github.com/BCooperA/express-authentication-starter-api
In config/passport.js I've created the actual Strategy:
const passport = require('passport')
, mongoose = require('mongoose')
, User = mongoose.model('User')
, LocalStrategy = require('passport-local').Strategy;
/**
|--------------------------------------------------------------------------
| Local authentication strategy (email, password)
|--------------------------------------------------------------------------
*/
passport.use(new LocalStrategy({ usernameField: 'user[email]', passwordField: 'user[password]' },
function(email, password, done) {
User.findOne({email: email}, function (err, user) {
if(err)
return done(err);
// incorrect credentials
if (!user || !user.validPassword(password) || user.password === '') {
return done(null, false, { errors: [{ msg: "Incorrect credentials" }] });
}
// inactive account
if(user.activation_token !== '' || user.active === 0) {
// account is not activated
return done(null, false, { errors: [{ msg: "Inactive account" }] });
}
// all good
return done(null, user);
});
}));
In addition, I've also created a separate POST route for signing in users locally where I'm using passport.authenticate.
In routes/auth.routes.js:
const router = require('express').Router()
, mongoose = require('mongoose')
, User = mongoose.model('User')
, passport = require('passport');
/**
|--------------------------------------------------------------------------
| Local Authentication
|--------------------------------------------------------------------------
| Authenticates user using Local Strategy by Passport
*/
router.post('/signin', function(req, res, next) {
if(req.body.user.email === '' || req.body.user.password === '')
// overrides passports own error handler
return res.status(422).json({errors: [{ msg: 'Missing credentials'}]});
passport.authenticate('local', { session: false }, function(err, user, info) {
if(err)
return next(err);
if(user) {
// generate JSON web token to user
user.token = user.generateJWT();
// return user object
return res.status(200).json({ user: user.toAuthJSON() });
} else {
// return any errors
return res.status(422).json(info);
}
})(req, res, next);
});
module.exports = router;
I'm making a User Authentication with passport. First I created a default Admin User. Now this Admin must able to create users but not any other users. For this I created a Admin user in Database. Now my Question is how to create the other users by the Admin and as well only this Admin should have access to all API's routes but not for any other Users how to protect the API's? In server.js file i created middleware function as
//Catch unauthorized errors
app.use(function (err, req, res, next) {
if(err.name === 'UnauthorizedError') {
res.status(401);
res.json({"message": err.name + ":" + err.message});
}
});
Please help with this. I hope you guys don't mind for posting such a long files.
'authentication.js'
'use strict';
var passport = require('passport'),
mongoose = require('mongoose'),
Users = mongoose.model('Users');
var authentication = {
register: function(req, res, name, email, password) {
var userData = req.body;
var user = new Users({
email: userData.email,
name: userData.name,
});
user.setPassword(userData.password);
if(!user) {
res.status(400).send({error: 'All fields required'});
}
user.save(function(err, result) {
if(err) {
console.log('Could not save the User');
res.status(500).send({error: 'Could not save the User'});
}else {
res.send('New User Created Successfully');
}
});
},
login: function (req, res) {
if(!req.body.email || !req.body.password) {
res.status(400).send({"message": "All fields required"});
return;
}
passport.authenticate('local', function (err, user, info) {
var token;
if (err) {
res.status(404).send({err: 'An Error Occured'});
return;
}
if(user) {
token = user.generateJwt();
res.status(300).send({"token": token});
}else {
res.status(401).send('Unauthorized User');
}
});
}
};
module.exports = authentication;
'user-model.js'
'use strict';
var mongoose = require('mongoose'),
crypto = require('crypto'),
jwt = require('jsonwebtoken'),
Schema = mongoose.Schema;
var userSchema = new mongoose.Schema({
email: {
type: String,
required: true,
unique: true
},
name: {
type: String,
required: true
},
hash: String,
salt: String
});
userSchema.methods.setPassword = function (password) {
this.salt = crypto.randomBytes(16).toString('hex');
this.hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64).toString('hex');
};
//Validating a submitted password
userSchema.methods.validPassword = function (password) {
var hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64).toString('hex');
return this.hash === hash;
};
//Generating a JSON Web Token
userSchema.methods.generateJwt = function () {
var expiry = new Date();
expiry.setDate(expiry.getDate() + 7);
return jwt.sign({
_id: this._id,
email: this.email,
name: this.name,
exp: parseInt(expiry.getTime() / 1000)
}, process.env.JWT_SECRET);
};
var User = mongoose.model('Users', userSchema);
var user = new User();
user.name = 'Arjun Kumar';
user.email = 'arjun#kumar.com';
user.setPassword('myPassword');
user.save();
'user-route.js'
'use strict';
var express = require('express'),
userRoute = express.Router(),
jwt = require('express-jwt'),
authentication = require('../controllers/authentication');
var auth = jwt({
secret: process.env.JWT_SECRET,
userProperty: 'payload'
});
userRoute.post('/:adminuserid/register', auth, authentication.register)
.post('/login', authentication.login);
module.exports = userRoute;
'passport.js'
var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
mongoose = require('mongoose'),
Users = mongoose.model('Users');
passport.use(new LocalStrategy({usernameField: 'email'}, function (username, password, done) {
Users.findOne({ email: username }, function (err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, {
message: 'Incorrect username.'
})
}
if (!user.validPassword(password)) {
return done(null, false, {
message: 'Incorrect password.'
});
}
return done(null, user);
});
}));
One thing you can do it to put all your functions in a conditional like this to give access only to admin:
If(req.user.email === your admin email) {
Your function
}
This should go under the routes that you want only the admin have access to.
Or if you have several admins , then you should alter your schema a bit and add an admin : Number which you can later declare for example any user with admin:1 are system administrators otherwise not .
I hope I understood your question correctly.
Good luck