Koa and Passport with MongoDb - passport.js

I'm having some troubles with Koa, Passport and Monk.
I'd like to have a simple local authentication with Passport. I've followed some tutorials and got as far as this: (auth.js)
const
passport = require('koa-passport'),
LocalStrategy = require('passport-local').Strategy,
monk = require('monk'),
wrap = require('co-monk'),
db = monk('localhost/try'),
users = wrap(db.get('users'));
var user = {
id: 1,
username: 'test'
};
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
done(null, user);
});
passport.use(new LocalStrategy(
function(username, password, done) {
if (username === 'test' && password === 'test') {
return done(null, user);
} else {
return done(null, false);
}
}
));
module.exports = passport;
Now this works ok, but how can I use my MongoDb database here instead of if (username === 'test' && password === 'test')?
I've tried to add this function:
function *getUser(name) {
var useri = yield users.find({name:name});
return useri;
};
and then use it like this:
passport.use(new LocalStrategy(
function(username, password, done) {
var useri = getUser(username);
console.log(useri);
if (username === 'test' && password === 'test') {
return done(null, user);
} else {
return done(null, false);
}
}
));
but only end up getting {} in my console.
So how do I do this? It's all so easy in Express but with this Koa thingy I'm really struggling to understand how it all works..

you should call a generator function with yield keyword. but you can use yield just in generator functions. so you can wrap your function in co() like bellow:
passport.use("login",new LocalStrategy(function(username, password, done) {
// retrieve user ...
co(function *() {
try {
var user=yield getUser(username);
console.log(user);
return user;
} catch (ex) {
return null;
}
})(done);
}));

You can see this demo: https://github.com/dozoisch/koa-react-full-example/blob/master/lib/authenticator.js
#Mohammad Rasti 's method doesn't work in my project (may be because of version). Following code works for me.
passport.use(new LocalStrategy(function(username, password, done) {
co(function*() {
try {
var user = yield getUser(username);
if (username === user.username && password === user.password) {
return user;
} else {
return null;
}
} catch(e) {
return null;
}
}).then(function(user) {
done(null, user);
});
}));

this is how I'm using it:
var LocalStrategy = require('passport-local').Strategy
passport.use(new LocalStrategy(function(username, password, done) {
co(function *() {
try {
return yield getUser(username, password);
} catch (ex) {
console.log('error: ', ex);
return null;
}
}).then(function(user) {
console.log('found: ', user);
done(null, user);
});
}))

Hm you just missing a yield in the passport.use() caller function.
passport.use(new LocalStrategy(
function* (username, password, done) {
var useri = yield getUser(username);
console.log(useri);
}
));
But like other answers, its best to use co for easier handling.

Related

Cannot read property 'use' of undefined, in the passport .js file

I created a folder config and i created passport.js file inside it, that holds all the configuration passport module when i tried to run my code i got this error TypeError: Cannot read property 'use' of undefined referring to this line of code passport.use(new LocalStrategy(function(username, password, done) {
passport.js file :
var express = require('express');
const LocalStrategy = require('passport-local').Strategy;
const User = require('../server/model/userModel');
const bcrypt = require('bcrypt');
var passport = require('passport');
module.exports = function(passport) {
// Local Strategy
passport.use(new LocalStrategy(function(username, password, done) {
// Match Username
let query = { username: username };
User.findOne(query, function(err, user) {
/*
if (err) throw err;
if (!user) {
return done(null, false, { message: 'No user found' });
}
*/
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
// Match Password
bcrypt.compare(password, user.password, function(err, isMatch) {
if (err) throw err;
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: 'Wrong password' });
}
});
});
}));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
}
You use two different references for the same name.
var passport = require('passport');
Here, you defined the passport variable.
module.exports = function(passport) {
Here, you define the parameter name as passport. Using passport in this context will refer to the parameter
passport.use(...
Calling on the parameter's use method instead of the passport.use method from the defined variable. Consider changing the function parameter name to something other than passport, maybe _passport?
module.exports = function(passport) {
//to
module.exports = function(_passport) {
Have you initialised:
app.use(passport.initialize());
app.use(passport.session());
To use Passport in an Express or Connect-based application, configure it with the required passport.initialize() middleware. If your application uses persistent login sessions (recommended, but not required), passport.session() middleware must also be used.
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
if (!user.verifyPassword(password)) { return done(null, false); }
return done(null, user);
});
}
));
https://github.com/jaredhanson/passport

How can I add a simple middleware that will verify JWT and be ensure that is authorized?

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);

User Authentication on MEAN stack application

I am building an API using the MEAN stack and am working on the login and signup functions.
And I want to respond with a json string as follows
{
success: 0,
message: ""
}
success:1 for successful login and 0 otherwise.
My authenticate.js is as follows
module.exports = function(passport){
//log in
router.post('/login', passport.authenticate('login', {
//success
//failure
}));
//sign up
router.post('/signup', passport.authenticate('signup', {
//success
//failure
}));
//log out
router.get('/signout', function(req, res) {
req.logout();
res.redirect('/');
});
return router;
}
My passport.init.js middleware is as follows
var mongoose = require('mongoose');
var User = mongoose.model('User');
var LocalStrategy = require('passport-local').Strategy;
var bCrypt = require('bcrypt-nodejs');
module.exports = function(passport){
// Passport needs to be able to serialize and deserialize users to support persistent login sessions
passport.serializeUser(function(user, done) {
console.log('serializing user:',user.username);
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
console.log('deserializing user:',user.username);
done(err, user);
});
});
passport.use('login', new LocalStrategy({
passReqToCallback : true
},
function(req, username, password, done) {
// check in mongo if a user with username exists or not
User.findOne({ 'username' : username },
function(err, user) {
// In case of any error, return using the done method
if (err)
return done(err);
// Username does not exist, log the error and redirect back
if (!user){
console.log('User Not Found with username '+username);
return done(null, false);
}
// User exists but wrong password, log the error
if (!isValidPassword(user, password)){
console.log('Invalid Password');
return done(null, false); // redirect back to login page
}
// User and password both match, return user from done method
// which will be treated like success
return done(null, user);
}
);
}
));
passport.use('signup', new LocalStrategy({
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, username, password, done) {
// find a user in mongo with provided username
User.findOne({ 'username' : username }, function(err, user) {
// In case of any error, return using the done method
if (err){
console.log('Error in SignUp: '+err);
return done(err);
}
// already exists
if (user) {
console.log('User already exists with username: '+username);
return done(null, false);
} else {
// if there is no user, create the user
var newUser = new User();
// set the user's local credentials
newUser.username = username;
newUser.password = createHash(password);
// save the user
newUser.save(function(err) {
if (err){
console.log('Error in Saving user: '+err);
throw err;
}
console.log(newUser.username + ' Registration succesful');
return done(null, newUser);
});
}
});
})
);
var isValidPassword = function(user, password){
return bCrypt.compareSync(password, user.password);
};
// Generates hash using bCrypt
var createHash = function(password){
return bCrypt.hashSync(password, bCrypt.genSaltSync(10), null);
};
};
Please help me out in passing the JSON string accordingly
Using Express, all you have to do is to execute res.json inside any controller, passing it any JavaScript object. Express will automatically convert it to JSON and return it to the user.
return res.json({ success: 0, message: '' }

Using promises in mongoose for passport authentication

var LocalStrategy = require('passport-local').Strategy;
var User = require('../models/user');
module.exports = function(passport){
passport.use('local-signup', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
},
function(req, email, password, done){
User.findOne({'local.username': email}).exec()
.then(function(user) {
console.log(user);
if(user){
return done(null, false, {message: 'Username already taken!'});
}
else{
var newUser = new User();
newUser.local.username = email;
newUser.local.password = password;
return newUser.save();
}
})
.then(function(user) {
console.log('DEBUG DEBUG DEBUG');
done(null, user);
})
.catch(function(err) {
done(err);
});
}
));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(userid, done) {
User.findById(userid).exec()
.then(function(user) {
done(user);
});
});
};
In my case the debug log always be printed, whether return newUser.save() is called or not.
Is it possible to make the debug log only be printed when return newUser.save() is called?
Btw I'm using passportJS for authentication things and for the promise I'm override mongoose default promise with global es6 promise.
You mean like this? Sorry if there are extra brackets or parenthesis. I edited in the text box. :(
User.findOne({'local.username': email}).exec()
.then(function(user) {
if(user){
return done(null, false, {message: 'Username already taken!'});
}
else{
var newUser = new User();
newUser.local.username = email;
newUser.local.password = password;
return newUser.save()
.then(function(){
console.log('DEBUG DEBUG DEBUG');
done(null, user);
})
}
})
.catch(function(err) {
done(err);
});
}
));
Just put your debug message in a function in newUser.save()
User.findOne({'local.username': email}).exec()
.then(function(user) {
console.log(user);
if(user){
return done(null, false, {message: 'Username already taken!'});
}
else{
var newUser = new User();
newUser.local.username = email;
newUser.local.password = password;
return newUser.save(function() {
console.log('SAVED!');
console.log('DEBUG DEBUG DEBUG');
});
}
})
.then(function(user) {
done(null, user);
})

TypeError: user.verifyPassword is not a function

My model looks like this, but when I try use verifyPassword, it says TypeError: user.verifyPassword is not a function
var passport = require('passport');
var BasicStrategy = require('passport-http').BasicStrategy;
var User = require('../models/user');
passport.use(new BasicStrategy(
function(username, password, callback) {
User.findOne({ username: username }, function (err, user) {
if (err) { return callback(err); }
// No user found with that username
if (!user) { return callback(null, false); }
// Make sure the password is correct
// Error comind at this point
user.verifyPassword(password, function(err, isMatch) {
if (err) { return callback(err); }
// Password did not match
if (!isMatch) { return callback(null, false); }
// Success scenario
return callback(null, user);
});
});
}
));
Try to use User.verifyPassword function
But for more correct answer show your User model
You should have in your User schema something like:
// I am use bctypt, but you need your comparer function
userSchema.methods.verifyPassword = function(password, callback) {
callback(err, bcrypt.compareSync(password, this.password));
};
I know i am late but you can do something like this,
passport.use(
'local',
new LocalStrategy(function(username, password, done) {
User.findOne({ username: username }, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false);
}
if (user.password != password) {
return done(null, false);
}
return done(null, user);
});
})
);
Reference : https://github.com/sikandar114/node-auth-pass-local/blob/master/config/passport.js
Since the data is sent by post method, express is not able to parse data because body-parser middleware has been omitted from express due to some issues. Hence you must add it manually.Import body-parser:
npm install body-parser
Add these lines to your code.
app.use(bodyParser.urlencoded({ extended: false }));
app.use(passport.initialize());
Also define these 2 functions.
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
Try using passport.use(new LocalStrategy(User.authenticate()));

Resources