I would like to use sailsJS with an existing database.
This database already contains a user table (which is named caver) and already has email, username and password (which are named contact, login, password).
I cannot change the schema of this database!
I am currently trying to use sails-generate-auth to setup a local authentication.
Is it possible to link the passport authentication with an existing model (Caver) which is not the automatically created model (User)?
Can I use a custom hash function (the one used for the password in my current database) with the passport service created by sails-generate-auth?
I already succeeded to make my authentication work with the following code but now I would like to use sails-generate-auth instead.
api/services/passport.js (without using sails-generate-auth) :
var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy;
// helper functions
function findById(id, fn) {
Caver.findOne(id).done( function(err, user){
if (err){
return fn(null, null);
}else{
return fn(null, user);
}
});
}
function findByEmail(e, fn) {
Caver.findOne({
Contact: e
}).done(function(err, user) {
// Error handling
if (err) {
return fn(null, null);
// The User was found successfully!
}else{
return fn(null, user);
}
});
}
function md5(string) {
var crypto = require('crypto');
return crypto.createHash('md5').update(string).digest('hex');
}
function getOldGCpassword(login, password) {
return addslashes(md5(login + "*" + password));
}
function addslashes(str) {
// From: http://phpjs.org/functions
// * example 1: addslashes("kevin's birthday");
// * returns 1: 'kevin\'s birthday'
return (str + '').replace(/[\\"']/g, '\\$&').replace(/\u0000/g, '\\0');
}
// Passport session setup.
// To support persistent login sessions, Passport needs to be able to
// serialize users into and deserialize users out of the session. Typically,
// this will be as simple as storing the user ID when serializing, and finding
// the user by ID when deserializing.
passport.serializeUser(function(sessionUser, done) {
done(null, sessionUser.id);
});
passport.deserializeUser(function(id, done) {
findById(id, function (err, user) {
done(err, user);
});
});
// Use the LocalStrategy within Passport.
// Strategies in passport require a `verify` function, which accept
// credentials (in this case, a email and password), and invoke a callback
// with a user object.
passport.use(new LocalStrategy({
usernameField: 'email'
},
function(email, password, done) {
// asynchronous verification, for effect...
process.nextTick(function () {
// Find the user by email. If there is no user with the given
// email, or the password is not correct, set the user to `false` to
// indicate failure and set a flash message. Otherwise, return the
// authenticated `user`.
findByEmail(email, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, { message: 'Unknown user ' + email });
}
var hash = getOldGCpassword(user.Login, password);
if (hash.localeCompare(user.Password) == 0) {
var returnUser = { email: user.Contact, createdAt: user.Date_inscription, id: user.Id };
return done(null, returnUser, { message: 'Logged In Successfully'} );
} else {
return done(null, false, { message: 'Invalid Password'});
}
})
});
}
));
Related
I am trying to use passportjs in my nodejs app with express4. For testing purpose, I am using session-file-store to keep session.
The way I set up the session and passportjs :
this.app.use(session({
secret: process.env.SESSION_SECRET || configuration.session_secret,
store: new fileStore(),
resave: false,
saveUninitialized: false
}));
// config authentication
this.app.use(passport.initialize());
this.app.use(passport.session());
// Configure Passport
passport.use('local-login', new localStrategy.Strategy(
(username, password, done) => {
Services.users.authenticatePromise(username, password).then(
function(token) {
done(null, token);
},
function(err) {
done(null, false, err);
},
).done();
},
));
// Serialize user in session
passport.serializeUser((token: AccessToken, done) => {
let user = {
_token: token.accessToken,
_expiryInAt: token.expiryInMs + new Date().getTime(),
};
done(null, user);
});
passport.deserializeUser((sessionUser, done) => {
done(null, sessionUser);
});
However, the issue I am having is that if I don't write anything into the session before user is logged in, passportjs works fine. However if I am trying to write some data into session like shopping cart details, then passportjs would not be able to serialize the user token into the session store any more. so login cannot be successful.
What am I missing?
you have a couple of things wrong with your code that I can see.
first, serializeUser should take an object and return a string (typically a unique id or database key). Your function currently takes a string and returns an object; that's backwards.
second, deserializeUser should take a string and return a user object. Your function currently takes an object and returns that same object.
finally ,serializeUser and deserializeUser must be inverses of each other; meaning, passport can call deserializeUser(serializeUser(user)) and get back the original user object.
So you're going to want something like this:
passport.serializeUser((user, done) => {
return user.id;
});
passport.deserializeUser((id, done) => {
getUserFromDatabase({id: id}, function(err, user) {
if (err) {
done(err, null); // something went wrong; return error
} else if (!user) {
done(null, false); // no user found; return `false`
} else {
done(null, user); // return user
}
});
});
for more info see here: Understanding passport serialize deserialize
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: '' }
I am using passportJS, in my callback I would like to carry the following operations.
1) generate a random string
2) check the database if that id exists, if it exist then regenrate id untill it is unique.
3) Save the user model (with a unique id).
I tried writing multiple functions but it seems that the newUser object is undefined inside the functions!
Here is my functions I am using from my mongoose model.
userSchema.methods.generateVci = function(length, characters){
var string = '';
for(var i = length; i > 0; --i){
string += characters[Math.round(Math.random() * (characters.length - 1))];
}
return string;
};
userSchema.statics.validateVci = function(uniquekey){
this.find({}, function(err,user){
for(var i = 0; i < user.length; ++i){
var uservci = user[i].local.vci;
if(uservci == uniquekey){
console.log('false');
return false;
}
}
console.log('true');
return true;
});
};
passport.use('local-signup', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) {
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ 'local.email' : email }, function(err, user) {
// if there are any errors, return the error
if (err)
return done(err);
// check to see if theres already a user with that email
if (user) {
return done(null, false, req.flash('signupMessage', 'That email is already taken.'));
} else {
var generatedVciKey = newUser.generateVci(32, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
var isvalidvci = User.validateVci(generatedVciKey);
if(isvalidvci){
var newUser = new User();
newUser.local.email = email;
newUser.local.password = newUser.generateHash(password);
newUser.local.vci = generatedVciKey;
}
// save the user
newUser.save(function(err) {
if (err)
throw err;
return done(null, newUser);
});
}
});
}));
If you could please show me a way to write some sort of recursive function in that passportjs would only save the user model when a unique ( checked with the database) id has been generated. The unique id must be regenerated again and again until it is truly unique from any other ids in the database. I have no clue how to do this as it seems that when I write a function the variables in the passportjs callback becomes undefined.
I rewrote this generateVci but you should really use node-uuid like someone already suggested
userSchema.methods.generateVci = function(length, characters) {
return characters.split('').map(function() {
var randomKey = Math.round(Math.random() * (characters.length - 1));
return characters[randomKey];
}).join('').substring(0, length);
};
Your validateVCI is async so you have to pass a callback or other way is to use promises
userSchema.statics.validateVci = function(uniquekey, cb){
this.find({}, function(err, users){
if (err) {
return cb(err);
}
var isInvalid = users.reduce(function(invalid, user) {
if (invalid) {
return true;
}
return user.local.vci === uniquekey;
}, false);
if (isInvalid) {
return cb(null, false);
}
console.log('true');
return cb(null, true);
});
};
You should make vci field unique in your database ...
So when you try to create a user with same vci it would fail
function createUser(email, password, done) {
var generatedVciKey = newUser.generateVci(32, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
User.validateVci(generatedVciKey, function(isInvalid) {
if (isInvalid) {
return createUser(email, password, done)
}
var newUser = new User();
newUser.local.email = email;
newUser.local.password = newUser.generateHash(password);
newUser.local.vci = generatedVciKey;
// save the user, or try again on error
newUser.save(function(err) {
if (err) {
return createUser(email, password, done);
}
done(null, newUser);
});
});
}
passport.use('local-signup', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // allows us to pass back the entire request to the callback
}, function(req, email, password, done) {
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ 'local.email' : email }, function(err, user) {
// if there are any errors, return the error
if (err)
return done(err);
if (user) {
return done(null, false, req.flash('signupMessage', 'That email is already taken.'));
}
createUser(email, password, done);
});
}));
Do you realy need to do this? You can use node-uuid to generate unique identifier. So the probability that you will generate non-uniqe id negligible low
I have a user table in which i have an Admin account and some other normal users accounts.
I want to do all activities which for a particular user. It should act like that activity done by same user.
Can somebody tell me how can i switch to another account from Admin account
without login to that account.
Currently i use passport authentication.(passport-local)
Here is my code
app.get('/secure/group/login', function(req,res,next) {
passport.authenticate('local',function(err,user,info) {
console.log("error is "+err);
req.logIn('tessAccount',function(err) {
console.log("Weer" +err);
});
console.log("dd");
})(req,res,next);
});
});
and passport code
var LocalStrategy = require('passport-local').Strategy;
module.exports = function(passport) {
// used to serialize the user for the session
passport.serializeUser(function(user, done) {
done(null, user.token);
});
passport.use(new BearerStrategy(
function(token, done) {
user.tokenExist(token, function(err, user) {
if (err) {
return done(err);
}
else {
return done(null, user, { scope: 'all' });
}
});
}
));
// used to deserialize the user
passport.deserializeUser(function(accessToken, done) {
user.getUserByAccessToken(accessToken, function(err, dbUser) {
if (err) {
done(err);
} else {
done(null, dbUser[0]);
}
});
});
// we are using named strategies since we have one for login and one for signup
// by default, if there was no name, it would just be called 'local'
passport.use('local-signup', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) {
user.emailExists(email, function(err, exists) {
if (err)
return done(err);
else if (exists) {
return done(null, false, {
message: req.flash('loginMessage')
});
} else {
req.userDetails.status = 0;
req.userDetails.token = md5.digest_s(req.userDetails.email + new Date());
req.userDetails.userImage = config.user.image;
user.register(req.userDetails, function(err, newUser) {
if (err)
return done(err);
else {
/*Get user Shared article if exist start*/
getSharedArticlesOfnewlyuserIfExist(email, newUser.insertId);
/*Get user Shared article if exist end*/
req.userDetails.id = newUser.insertId;
return done(err, req.userDetails);
}
});
}
});
}));
passport.use('local-login', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) {
// callback with email and password from our form
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
user.matchPassword({
email: email,
password: password
}, function(err, newUser) {
// if there are any errors, return the error before anything else
if (err)
return done(err);
// if no user is found, return the message
if (newUser.length > 0) {
var user = {
id: newUser[0].user_id,
email: newUser[0].email_id,
token: newUser[0].user_access_token
};
return done(null, user);
} else {
return done(null, false, {
message: 'Incorrect username or password.'
}); // req.flash is the way to set flashdata using connect-flash
}
});
}));
User.findOne({ username: 'Myusername' }, function(err, user) {
req.logIn(user, function(err){});
});
This worked for me to log into an account without using password, it switches from my admin account to an user account.
Actually I'm using passport.js and mongoose
I have setup Passport with Facebook strategy (just facebook strategy), And set up the check if user exists function to save user in Mongodb within a users collection.
I throw console.log for successful save in DB and I can see that the user has successfully saved in the collection (I can see it also in the DB itself with Terminal.
But the issue is that after successful registration the passport.authenticate function takes me to the login page and not to the 'getusergames' page.
Any Idea why is that?
function(accessToken, refreshToken, profile, done) {
// Checking if this user is already in DB and if no, Add this user to DB
insertUser.findOne({facebook_id: profile.id}, function(err, user) {
// Print Error
if (err) {
return done(err);
}
// Save new user if there is no user exists
if (!user) {
var userId = profile.id;
var userToken = accessToken;
var user_name = profile.displayName;
var newuser = new insertUser({
username: user_name,
facebook_id: userId,
facebook_token: userToken
});
newuser.save(function(err) {
if (err) {
console.log(err);
} else {
console.log('New User has Saved!');
done(null, user);
}
});
}
// If there is a user, Return his object and done.
else if (user) {
done(null, user);
}
});
}
So, the problem was that I didn't put return before the done(null, user);