How to decode and verify password in feathers js - node.js

I am new to feathersJs and trying to learn how to perform authentication using hooks and services. I am using Couchdb database and cradle.
This is the post method to encrypt password in hashPassword using "users" hooks service. The post method is as below:
app.post('/dev',function(req,res,next){
var username = req.body.username;
var password = req.body.password;
app.service('database').create({username,password}).then(user => {
db.save(user, function (err, docs) {
// Handle response
res.json(docs);
});
console.log('User Created Successfully.', user);
}).catch(console.error);
})
and service is:
app.service('authentication').hooks({
before: {
create: [
// You can chain multiple strategies
auth.hooks.authenticate(['jwt', 'local'])
],
remove: [
auth.hooks.authenticate('jwt')
]
}
});
app.service('database').hooks({
before: {
find: [
auth.hooks.authenticate('jwt')
],
create: [
local.hooks.hashPassword({ passwordField: 'password' })
]
}
});
now i am using this to retrive data :
app.post('/devget',function(req,res,next){
var User = {
username: req.body.username,
password: req.body.password
};
app.service('dataget').find(User).then(user => {
db.view('byuser/user',{key: User.username}, function (err, docs) {
// Handle response
res.json(docs);
});
console.log('User Get Successfully.', user);
}).catch(console.error);
})
this will give me data in response as:
Response [
{ id: '060ab48a4826da7125d8ae45350037ee',
key: 'w',
value:
{ _id: '060ab48a4826da7125d8ae45350037ee',
_rev: '1-ea9a18d3724ce4542019dc5752c1fd4d',
username: 'w',
password: '$2a$10$yBJVJTmVXfTk0V4CCiWkd.GvAZZB9dF2pckKJ9wb/lJcAK8Ou.v06',
id: 0 } } ]
this works fine and password is encrypted but i am not getting how to decrypt password and authenticate user.
Note: i just want o do it with hooks and services or custom service or class but not using passport.

You do not decrypt the password; you compare the encrypted password to a function that will encrypt the password (after you've found the user to make a password comparison to).
const bcrypt = require('bcryptjs');
var hash = bcrypt.hashSync("bacon");
bcrypt.compareSync("bacon", hash); // true
bcrypt.compareSync("veggies", hash); // false

I would use the comparePassword function in this file
https://github.com/feathersjs/feathers/blob/be5d8df3b7afa39852ff1b5643676d9140ba8203/packages/authentication-local/src/strategy.ts#L94
Based on that you could write a hook and consider contributing it to the feathersJS project.

Related

MongoDB and Mongoose Abnormal behavior?

I apologize for the maybe trivial question. I am accessing mongo db to obtain information on a user, verify the password, and save some data in Redis if the validation was successful.
But I'm misbehaving (probably my mistake).
My simple backend is built in express 4.17.2, node 17.2.0, and redis4.0.1.
Server-side MongoDB on cloud MongoDB Atlas (Version 4.4.10) and RedisLab (Redis in cloud version 6.2.3).
Here are some code snippets:
app.post('/identity/login', (req, res) => {
let userHash = '';
let result = false;
const user = req.body;
userHash = crud.login(user.username);
userHash.then(h => {
result = bcrypt.compareSync(user.password, h.password);
console.log('All object ',h);
console.log('Access to roles array',h.roles);
let value = {name:h.name, surname:h.surname, roles:h.roles, username:h.username};
console.log('value', value);
client.set(h.password, JSON.stringify(value), { EX: 60, NX: true });
res.send(result);
});
});
Making the POST call to that endpoint, I get the following (unexpected) results, highlighted by the logs:
All object {
_id: new ObjectId("61d1d38d0f4bac12d8f504ee"),
username: 'jdoe',
password: '$2a$14$5GULZRmiN0DXQXMaLwQiEO9S/OlBll5U4ZwkBn2NYpju0VRDFUqVO',
name: 'John',
surname: 'Doe',
roles: [ 'Admin' ]
}
Access to roles array undefined
value { name: 'John', surname: 'Doe', roles: undefined, username: 'jdoe' }
As you can see, if I log h.roles I get that the array is not defined, while logging the whole object the array property 'roles' is correctly valued. So obviously, when I go to build the object to save in Redis (as h.roles), the array is not created.
Why? Could you please help me and say me what I am doing wrong? My mistake, a mongoose problem (see 6.4.1)?
Below, I show the code to access MongoDB:
const login = async (username) => {
connectDB();
let dbPwd;
const User = mongoose.model('Users', UserSchema);
return await User.findOne({ username: username }, 'password name surname roles username').exec();
}
Thanks so much!

Mongoose-encryption Failure, Page keeps rolling

I am having a problem with an npm package called 'mongoose-encryption'. Here is my code.
main().catch((err) => console.log(err));
async function main() {
await mongoose.connect('mongodb://localhost:27017/userEncryptedDB', { useNewUrlParser: true });
}
const userSchema = new mongoose.Schema({
email: String,
password: String
});
const secret = 'xxs4Ox4nSKSVnJzIxzy+es6ouOmoMcqcarAnEVRP26Q=';
userSchema.plugin(encrypt, {secret: secret, encryptedFields: ['password']});
const User = mongoose.model('User', userSchema);
Now, if I don't use the package, registration and logging in to the website works properly but if I use the plug in, it says "You are never registered!" even though I register for the email and password...
app.post('/login', (req, res) => {
const email = req.body.username;
const password = req.body.password;
User.findOne({email: email, password: password}, (err, user) => {
if(err) {
console.error(err);
} else {
if(user){
console.log('You are already registered!');
res.render('secrets');
} else {
console.error('You are never registered!');
}
}
});
});
What am I doing wrong here? I started computer stopped database, restarted it. Registration is successfully done but when I try to login page keeps rolling... If I dont use mongoose-encryption, login page works fine once I enter password.
I can clearly see that the new username gets added to the database like so:
{ "_id" : ObjectId("616a4aa45dfbf877d9f92468"), "email" : "aras#aras.com", "_ct" : BinData(0,"YbVYW9Pi6dz1yqRD7XgcEyEp48lLGVjp3J40ZgxGt/YuA8+eyiyebMaO9AGdygBj6A=="), "_ac" : BinData(0,"YQiV2QvS9AyrE1hjm81x5U8K6+0lT8h1ruxtCEZv/iwZWyJfaWQiLCJfY3QiXQ=="), "__v" : 0 }
But then when I try to login, it fails to retrieve password. Password I use here in this case is "1".
Try dropping the collection (first save it somewhere for later use) and try again. It will work!

Express - How to exclude a field from the response returned by Model.findOne() and Model.create() mongoose

I want to exclude the user's password field when sending a response to my front end. of course, I don't want anyone to access the user's encrypted password.
I have looked at this SO question, but the solution does not look good for models with many fields.
I'm using .select('-password') with Model.find(), Model.findById(), and Model.findByIdAndUpdate()
but It's not working for Model.findOne() and Model.create()
How can I exclude some fields when returning a response ?
/**
* #desc Authenticate User
* #route /api/acccounts/signin
* #access Public
*/
export const authenticateUser = asyncHandler(async (req, res) => {
const values = await loginSchema.validateAsync(req.body);
const { email, password, rememberMe } = values;
const account = await Account.findOne({ email });
if (account && (await account.matchPassword(password))) {
return res.json({
id: account.id,
firstName: account.firstName,
lastName: account.lastName,
email: account.email,
isAdmin: account.isAdmin,
...other fields,
token: generateToken(account.id, rememberMe),
});
}
return res.status(400).json({ message: 'Invalid email or password' });
});
Here you can use projection to exclude the field in your response like this
db.collection.findOne({
email: "sample#sample.com"
},
{
password: 0
})
Here is like of playground to test it: MongoPlayground
For more details check out findOne official documentation

How to access Mongoose Schema Attributes?

I am fairly new to using Mongoose and MongoDB. I am working on a register / login page. The register function works fine, storing the user account onto the database, but my problem is with logging in. What I am trying to do is get the 'password' attribute from the matching user off of the database, to match against the password that the user enters. This is my login function.
router.post('/logSubmit', function(req, res, next) {
var gusername = req.body.username;
var gpassword = req.body.password;
User.count({
'credentials.username': gusername
}, function(err, count) {
if (err) throw err;
console.log(count);
if (count > 0) {
// Where I need to pull password attribute from the database
} else {
// Wrong username or password
}
});
});
I have looked all over the internet on how to read an attribute from a database entry, but I can't find anything. I feel like it is something very simple, but I guess I don't know the syntax. The name of my model is User. I figured it would be something like:
User.find({ username: gusername }, function(err, user) {
if (err) throw err;
var getpassword = user.password;
console.log(getpassword);
});
I half-thought that would work, but it didn't. How do I access the password attribute from the database?? Thanks.
EDIT:
This is what my user accounts look like stored in my database:
{
"_id": {
"$oid": "569e5344d4355010b63734b7"
},
"credentials": {
"username": "testuser",
"password": "password1234"
},
"__v": 0
}
A find query is sufficient for your purposes. If a non-null user object is retrieved from the find query, you have a guarantee that it is a user with a password.
User.find({ 'credentials.username': gusername }, function(err, users) {
if (err) throw err;
// 'users' is an array of the User objects retrieved.
users.forEach(function(user) {
// Do something with the password.
// The password is stored in user.credentials.password
console.log(user.credentials.password);
});
});

Why is my Node validation failing in this POST?

I'm following along in the MEAN machine Node authentication tutorial.
Here is their source code: https://github.com/scotch-io/mean-machine-code/blob/master/10-node-authentication/server.js I basically have everything except for the apiRouter.post('/authenticate', part
The Express APIs are working:
http://localhost:8615/api/users will return a list of users from scotch.io's MongoDB
The following is the API for /api/users:
apiRouter.route('/users')
// create a user (accessed at POST http://localhost:8615/api/users)
.post(function(req, res) {
// create a new instance of the User model
var user = new User();
// set the users information (comes from the request)
user.name = req.body.name;
user.username = req.body.username;
user.password = req.body.password;
// save the user and check for errors
user.save(function(err) {
if (err) {
// duplicate entry
if (err.code == 11000)
return res.json({ success: false, message: 'A user with that username already exists. '});
else
return res.send(err);
}
// return a message
res.json({ message: 'User created!' });
});
})
// get all users (access at GET http://localhost:8615/api/users)
.get(function(req, res) {
User.find(function(err, users) {
if (err) return res.send(err);
// return the users
res.json(users);
})
});
Here is my user.js User Schema
// SCHEMAS ------------------------------------
// user schema
var UserSchema = new Schema({
name: String,
username: { type: String, required: true, index: { unique: true }},
password: { type: String, required: true, select: false }
// ^ select false will not return passwords
});
// hash the password before the user is saved
UserSchema.pre('save', function(next) {
var user = this;
// PUT username
if (!user.isModified('username')) return next();
// PUT name
if (!user.isModified('name')) return next();
// hash the password only if the password has been changed or user is new
if (!user.isModifited('password')) return next();
// generate the salt
bcrypt.hash(user.password, null, null, function(err, hash) {
if (err) return next(err);
// change the password to the hashed version
user.password = hash;
next();
});
});
FROM THE BOOK:
Create a Sample User
First, we need to make sure that we even have a user to authenticate
since towards the end of last chapter, we deleted everyone. Let’s
create the user using the POST http://localhost:8080/api/users route
we created in our API to add a user to our database.
We will send a POST request with the following information: Name Chris
Username chris Password supersecret
I'm using Postman to add a new user, as you can see I have put in key/value pairs for username and password, however getting an error saying "Validation failed" "username is required" "password is required":
UPDATE, I just tried x-www-form-urlencoded and got the following error
GET /api/users 200 66.141 ms - 655
••• API CRUD hit •••
/Users/leongaban/NodeDallas/projects/awesome-test/app/models/user.js:27
if (!user.isModifited('password')) return next();
^
TypeError: Object { password: 'supersecret',
username: 'Chris',
name: 'chris',
_id: 54c001dc4ee4028c18e61334 } has no method 'isModifited'
at model.UserSchema.methods.comparePassword.user (/Users/leongaban/NodeDallas/projects/awesome-test/app/models/user.js:27:12)
Screenshot of error in Postman: https://s3.amazonaws.com/f.cl.ly/items/1M0M392M0E3b2i430I1n/Image%202015-01-21%20at%201.45.51%20PM.png
try x-www-form-urlencoded in postman, that would do.
You want to push a json to your server. Select raw and set the data type to JSON.
Then you just have to write your user in JSON format with all his fields here.
{
"name": "Chris",
"username": "chris",
"password": "supersecret"
}

Resources