problem with understanding passport local strategy functionality nodejs - node.js

I am kinda new in node. recently I decided to create a blog system and it has an authorization system which is created by passport module and local strategy.
I used passport documentation and some video tutorials to design it but I can't understand how does it work, I don't understand the logic.
I have a login form which has two fields (username,password) and a submit button.
you can see my login.jade code here. it is written in jade templating language and used semantic-UI(something like bootstrap).
form.ui.form(method="POST",action="")
div.field
label Username
div.ui.left.icon.input
i.user.icon
input(type="text",name="username",placeholder="username")
div.field
label Password
div.ui.left.icon.input
i.lock.icon
input(type="password",name="password",placeholder="password")
button.ui.primary.button(type="submit") log-in
and here is my passport local strategy
passport.use(new localStrategy(function(username,password,done){
User.checkUserName(username,function(err,user){
if (err) throw err;
if (!user) {
console.log('unknown user');
return done(null,false,{message:'user not found'});
}
if(user.password!=password){
return done(null,false , {message : 'password is wrong'});
}
return done (null,user);
});
}));
checkUserName is a function in my models (user.js) which finds the username in the database.
module.exports.checkUserName= function(username,callback){
User.findOne({username:username},callback);
}
now I don't understand how does the localstrategy work. how does it understand which field in my login form is for username and which field is for the password? it only accepts two arguments (username, password) but I don't know how it specifies where are these arguments come from and how it understands that these must be my login form credentials.
I would be very thankful if someone explains to me what is happening here.

If you're using username / password authentication, by default localStrategy() uses input fields with name="username" and name="password". So, your form is correct.
If you want to use other field names, you can change the defaults. Read this. http://www.passportjs.org/docs/username-password/#parameters
I'd like to point out that you should use a hashing scheme for your passwords, rather than storing them in plain text in your database. Because, Ashley Madison.
This is a well-designed robust hashing scheme. There are others.

Looking at the implementation of the strategy in github it looks at both query of the request and its body and by default will look for username and password keys for their respective values.
This matches the name values in the login form.
If you want to use different name values for your inputs you can specify what values you want it to use by providing a usernameField or passwordField value like follows
passport.use(new localStrategy(function(username,password,done){
User.checkUserName(username,function(err,user){
if (err) throw err;
if (!user) {
console.log('unknown user');
return done(null,false,{message:'user not found'});
}
if(user.password!=password){
return done(null,false , {message : 'password is wrong'});
}
return done (null,user);
});
},
{
usernameField: 'user',
passwordField: 'pass'
}
));
Then you can update the login form name values to match
form.ui.form(method="POST",action="")
div.field
label Username
div.ui.left.icon.input
i.user.icon
input(type="text",name="user",placeholder="username")
div.field
label Password
div.ui.left.icon.input
i.lock.icon
input(type="password",name="pass",placeholder="password")
button.ui.primary.button(type="submit") log-in

Related

How is getCompany defined on req.user in passportjs?

I was just going through a model code HERE. and i see certain the following in a route file (line of code can be found HERE) :
router.get('/departments/', function(req, res){
// Add JS that is specific only to current page
res.locals.custom_java_script.push('/js/departments.js');
var company_for_template,
model = req.app.get('db_model');
req.user.getCompany({
scope : ['with_active_users', 'order_by_active_users'],
})
.then(function(company){
company_for_template = company;
return company.getDepartments({
scope : ['with_simple_users', 'with_boss'],
order : [[ model.Department.default_order_field() ]],
});
})
.then(function(departments){
res.render('departments_overview', {
title : 'Departments settings',
departments : departments,
allowance_options : generate_all_department_allowances(),
company : company_for_template,
});
});
});
What i don't understand is a single line of code I.E. req.user.getCompany. Where is the getCompany method being attached to the user object. I have a bit of a fuzzy understanding. I'll list out my understanding below.
So basically the user object is being attached by passport.js is what i believe, but where and how exactly is the getCompany being attached to the user object ?
The only partial clue i have is this answer HERE I.E. the deserializeUser, when i see that i only see the below (code can be found HERE, on the repo.) :
passport.deserializeUser(function(id, done) {
model.User.find({where : {id : id}}).then(function(user){
return user.reload_with_session_details();
})
.then(function(user){
done(null, user);
})
.catch(function(error){
console.error('Failed to fetch session user '+id+' with error: '+error);
done(null, false, { message : 'Failed to fetch session user' });
});
});
And from here on i am a bit lost for clues. Any suggestions on where and how the getCompany method is being attached would be really appreciated.
This timeoff-management lib is using Sequelize for the User model and if you look at here https://github.com/timeoff-management/timeoff-management-application/blob/master/lib/model/db/user.js#L561, the User model has an association to Company model.
When the association is defined between two models, Sequelize will create a couple of mixins which is available on an instance.
With the as option is presented, these mixins will be generated on the User instance. (Some plural forms will be also generated but I couldn't confirm.)
userInstance.getCompany()
userInstance.countCompany()
userInstance.hasCompany()
userInstance.setCompany()
userInstance.addCompany()
userInstance.removeCompany()
userInstance.createCompany()
For more detail about Sequelize mixins, please check it out here https://sequelize.org/v6/manual/assocs.html#special-methods-mixins-added-to-instances.

How to return back parameter values when validation is failed in Express.JS

After validation and some fields are not satisfied with the validation rule it will redirect to the registration page, now after the direction I would like to retain the values inputted by the user and assign it to the corresponding input field. I haven't seen this in expressjs but with PHP, Ruby they do have this feature.
This part here I want to return back the params value to front-end so that the user wont re-enter the values.
if ( req.session.flash_has_errors() ) {
return res.redirect_with_session('/register/');
}
What about declaring variables for username and other stuff. You save it after post.
After validation you can render new registrationpage with this values.
router.post('/signup', function (req, res, next) {
let username = req.body.username;
if (validation){
}else{
res.render('./signup', { username: username });
}
});

Unable to change the user password after cognito authentication

I created the user using adminCreateUser. On trying to authenticate using adminInitiateAuth, a challenge NEW_PASSWORD_REQUIRED is returned. How do I change the password?
I am trying to reset the user password for a user with status FORCE_CHANGE_PASSWORD, using the following snippet:
cognitoidentityserviceprovider.respondToAuthChallenge({
ChallengeName: 'NEW_PASSWORD_REQUIRED',
ClientId:'XXXXXXXXXXXXXXXXXXXX',
ChallengeResponses: {
NEW_PASSWORD: 'T.YpkKu487',
USERNAME: 'XXXXX'
},
Session: "cMcKC5ymFvZG2D10lJMRArpfmLZvnQE25P1XjwkHtl47g4kW-LAjOgB4CAjIgv1CurDmfRHpxzvYgYE61BBrp5-TNyVQcXXX"
}, (err, data) => {
if(err) console.log(err);
else console.log(data);
});
When I run the above code, I get the following error:
InvalidParameterException: Invalid attributes given, name is missing
I do not understand this. Why am I getting this error? Reference doc
Also, is there a way I could disable this challenge altogether?
Found A solution: When I created the user pool I added name and email as required attributes, creating it without requiring attribute name solved it.
Please add the 'name' attribute in your "userattributes" which you will pass for changing the password
.

Expressjs (node) - How to get a set of data which is filtered according to the user

I am new to express js. I am currently building a todo list web app. I have been following the tutorials and is able to perform basic CRUD operation. I have two models.
Users - (name, email, password hash, password salt, todo_items - which reference to the second model).
Todo_items - (title, description, due_date, user - which reference to the first model).
When a user log in, I am able to read his user_id. However, how can i filter the todo_items that only belongs to the user? My current code looks like this and is returning all the todo_items from all the users.
router.get('/api/todo_items', function(req, res, next){
Todo_items.find(function(err,todo_items){
if(err){return next(err); }
res.json(todo_items);
})
});
Currently, I am using a front end framework, Angularjs to filter the results that get displayed. But I thought this is inefficient as the user base gets big. How do I create a filtering system from the backend?
You can query the collection on the user field, assuming it references the User model on the _id field so you could have something like the following:
router.get('/api/todo_items', function(req, res, next){
Guest.find({ "user": user_id }).exec(function(err, todo_items){
if(err) { return next(err); }
res.json(todo_items);
});
});

passport match username and password in a sub document if the "parent" document doesn't have any matching username and passwords

I was thinking of setting up an like the following. There will be a login page that an admin can access and a worker can access. the admin can make it so that the workers could see certain data(the admin can create workers). The main problem right now is hooking this up with passport. I think the user document look something like this
{username: "user1", password : "pass1", role : "admin",
workers :[
{username : "worker1", password : "workerpass1"},
{username : "worker2", password : "workerpass2"}]
}
Passport does something like the following to check for username and password
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function(err, user) {
........................
I'm wondering how I can get access to the worker level because I think that the passport code above will look at the top username field.
I think I want to do something like this: if on login attempt if username an password is not in the top level go to the workers array and test to see if the username and password matches there
maybe if User.findOne doesn't find the user do User.worker.findOne() how would I do that?
If you are looking for any of the combinations to match the you basically want an $or query condition, with an $elemMatch to get the username and password combination from the specific workers entry:
passport.use(new LocalStrategy(
function(username,password,done) {
User.findOne({
"$or": [
{ "username": username, "password": password },
{
"workers": { "$elemMatch": {
"username": username, "password": password
}}
}
]
},function(err,user) {
// remaining code
});
}
))
So that means "either" of the conditions listed within the array of $or can match a document, and the $elemMatch is used to make sure that both "username" and "password" need to match on the array element, if the main "userame" and "password" was not a match outside of the array.
So its "either/or", and the inspection of the array happens "inside" the query method, and not as a property directly off the model object.
Also noting here that since "username" and "password" are possibly "also" within an array object, then other built in validation methods need to be overriden by the query logic here. So the "query" needs to validate the match, and when an object is returned that is when you return a "valid" result.
That's the cost of customisation. Otherwise, just have separate user accounts, all with the same role you want to apply.

Resources