._doc in Mongoose - javascript

I have this js code
app.post('/auth', async (req, res) => {
try {
const user = UserModel.findOne({email: req.body.email}).exec()
if (!user) return res.status(404).json({
message: 'Not find user'
})
const isValidPassword = bcrypt.compare(req.body.password,user._doc.passwordHash)
if (!isValidPassword) return res.status(404).json({
message: 'Incorrect password'
})
}
catch (err) {
console.log(err)
res.status(500).json({
message: 'error'
})
}
})
And I have this Schema
import mongoose from 'mongoose'
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
surname: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
passwordHash: {
type: String,
required: true
},
telegramUrl: {
type: String,
required: true
},
avatarUrl: String
},
{
timestamps: true
}
)
export default mongoose.model('User', userSchema)
In this line
const isValidPassword = bcrypt.compare(req.body.password,user._doc.passwordHash)
I have error: Cannot read properties of undefined (reading 'passwordHash'). Why am I getting an error? He writes to me that ._doc undefined but why? Help me please

use these two methods in your schema
const bcrypt = require("bcrypt");
// Create Hash Salt Password ..
userSchema.pre("save", async function (next) {
if (!this.isModified("passwordHash")) return next();
this.passwordHash = await bcrypt.hash(this.passwordHash, 12);
next();
});
// Compare Password ...
userSchema.methods.comparePassword = function (passwordHash) {
return bcrypt.compareSync(passwordHash, this.passwordHash);
};
And in your auth code
app.post('/auth', async (req, res) => {
try {
const user = await UserModel.findOne({email: req.body.email}).exec()
if (user) {
res.status(400).json(
{ message: 'User already register'})
}
else{
const newuser = New User({
name: req.body.name,
// also write other schema fields
}
const res = await newuser.save();
console.log(res)
catch (err) {
console.log(err)
res.status(500).json({
message: 'error'
})
}
})
I hope this should resolve your problem

Related

Why does bcrypt always return false?

I've been trying to figure out why bcrypt always return false. I've pretty much tried everything and it's pretty much the same results. I've also tried using third party websites to check if the hash and plain password matches but it also returns false. I'm thinking there's an issue with what I HATCHED but I can't seem to figure it out.
Stack: Express, mongoose, mongodb atlas
api calls
login = (req, res, next) => {
let userData = req.body;
User.findOne({
email: userData.email
},function(error,user){
if(error){
console.log(error)
} else {
if(!user){
res.status(401).send('email not found')
} else if(user){
bcrypt.compare(userData.password, user.password).then(function(resp) {
if(resp === false){
res.status(401).send('Invalid password')
} else {
let payload = {
subject: user._id
}
let token = jwt.sign(payload,"secretKey")
res.status(200).send({user,token})
}});
}
}
})
}
register = async (req, res) => {
let userData = req.body;
User.find({
email: userData.email
},function(err,result){
if(err){
console.log(err)
} else if(under._.isEmpty(result)){
bcrypt.genSalt(saltRounds, function(err, salt) {
bcrypt.hash(userData.password, salt, function(err, hash) {
userData.password = hash;
let user = new User(userData)
user.save((error,registerUser)=>{
if(error){
console.log(error)
} else {
let payload = {
subject: registerUser._id
}
let token = jwt.sign(payload,"secretKey")
res.status(200).send({registerUser,token})
}
})
});
})
} else {
res.status(401).send('Email is already in use')
}
})
};
My schema
import mongoose from 'mongoose';
import {
saveUser,
toAuthJSON,
validatePassword,
} from '../helpers/model.helpers';
import { generateAccessToken } from '../helpers/auth.helpers';
const { Schema } = mongoose;
const userSchema = new Schema({
firstName: {
type: String,
required: [true, 'first name is required'],
},
lastName: {
type: String,
required: [true, 'last name is required'],
},
phoneNumber: {
type: String,
required: [true, 'Phone number is required'],
min: [10, 'A phone number can have at-least 10 digits'],
max: [13, 'Please enter a valid phone number, this is too long'],
},
email: {
type: String,
required: [true, 'An Email is required'],
},
password: {
type: String,
required: [true, 'A password is required'],
},
profileImage: {
type: String,
required: false,
},
title: {
type: String,
required: false,
},
companyName: {
type: String,
required: [false, 'Company name is required'],
},
companyAddress: {
type: String,
required: false,
},
companyPhoneNumber: {
type: String,
required: [false, 'Company phone number is required'],
},
companyLogo: {
type: String,
required: false,
},
},{
versionKey: false
});
userSchema.pre('save', saveUser);
userSchema.methods.validatePassword = validatePassword;
userSchema.methods.generateAccessToken = generateAccessToken;
userSchema.methods.toAuthJSON = toAuthJSON;
export default mongoose.model('User', userSchema);
my helper
import bcrypt from 'bcrypt';
export async function saveUser(next) {
const SALT_WORK_FACTOR = 10;
if (!this.isModified('password')) return next();
try {
const salt = await bcrypt.genSalt(SALT_WORK_FACTOR);
this.password = await bcrypt.hash(this.password, salt);
return next();
} catch (error) {
return next(error);
}
}
export function validatePassword(password) {
return bcrypt.compare(password, this.password);
}
export function toAuthJSON() {
const profile = this.toJSON();
delete profile.password;
// eslint-disable-next-line no-underscore-dangle
delete profile.__v;
return {
profile,
token: this.generateAccessToken(this.id),
};
}

bcrypt compare method is returning false even if I used the same string when I hashed it

When I use the same password I hashed and stored in database as a parameter in compare method I get false. Really don't know whats the problem here:
router:
router.post("/users/login", async (req, res) => {
try {
const user = await User.findByCredentials(
req.body.email,
req.body.password
);
const token = await user.generateAuthToken();
console.log("Route token -->", token);
res.send({ user, token });
} catch (e) {
res.status(400).send(errors.e400);
}
});
Method for email and password validation ( calling compare method here):
userSchema.statics.findByCredentials = async (email, password) => {
const user = await User.findOne({ email });
if (!user) throw new Error("User does not exist.");
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) throw new Error("Invalid password.");
return user;
};
password hashing:
userSchema.pre("save", async function () {
try {
this.password = await bcrypt.hash(this.password, 8);
} catch (e) {
console.log(e);
}
});
user schema:
const userSchema = new mongoose.Schema({
username: {
type: String,
trim: true,
required: true,
unique: true,
},
email: {
type: String,
trim: true,
required: true,
unique: true,
validate(value) {
if (!validator.isEmail(value))
throw new Error("Email address is not valid.");
},
},
password: {
type: String,
trim: true,
required: true,
minLength: 7,
},
tokens: [
{
token: {
type: String,
required: true,
},
},
],
});

mongoose schema method returning undefined

I want to create a method that validates the user's password by using bcrypt.compare()
here is the code below.
UserSchema.methods.validatePassword = async (data) => {
console.log(this.email); // returns undefined
console.log(this.first_name); // returns undefined
return await bcrypt.compare(data, this.password);
};
here is the UserSchema I created
const UserSchema = mongoose.Schema(
{
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
},
{ timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' } }
);
when getting this.password in my schema .pre('save', ..) it works but shows undefined when I use schema methods. :(
here is the implementation of the method
const verifySignIn = async (req, res, next) => {
const { email, password } = req.body;
try {
const user = await User.findOne({ email });
if (!user) {
return res.status(404).json({
status: 'failed',
message: 'User Not found.',
});
}
const isValid = await user.validatePassword(password);
if (!isValid) {
return res.status(401).send({
message: 'Invalid Password!',
data: {
user: null,
},
});
}
next();
} catch (err) {
Server.serverError(res, err);
}
};
In the guide it says:
Do not declare methods using ES6 arrow functions (=>). Arrow functions explicitly prevent binding this, so your method will not have access to the document ...
So in this case, you just need to change UserSchema.methods.validatePassword = async (data) => {... to UserSchema.methods.validatePassword = async function(data) {...

Check if ID exist in related mongodb collection with mongoose

I have a "Drinkers" model and a "Sodas" model which is "related" - a drinker can have drunk X amount of sodas.
The route to get the data is this
router.get('/all/:drinkerId', sodasController.getAllSodasFromDrinker)
In my sodasController, is there a way to check if :drinkerId exists in the "Drinkers" collection and if not return an error that the drinker doesn't exist, without having to require the drinkersController in the sodasController.
Right now getAllSodasFromDrinker looks like this
const Sodas = require("../models/sodas.model");
exports.getAllSodasFromDrinker = async (req, res, next) => {
try {
const id = req.params.drinkerId;
if (id.match(/^[0-9a-fA-F]{24}$/)) {
await Sodas.find({ drinker: id }).exec((err, drinkerItem) => {
if (err) {
return next(err);
}
res.json({ data: drinkerItem });
});
} else {
return next("ID is in the wrong format");
}
} catch (error) {
return next(error);
}
};
In that function, I want to check if a user exists with the applied ID.
I want to avoid having to
const Drinkers = require("../models/drinkers.model") in the sodasController
The Drinkers model:
const Schema = mongoose.Schema;
const drinkersSchema = new Schema(
{
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
sodas: {
type: Schema.Types.ObjectId,
ref: "Sodas",
},
},
{ timestamps: true }
);
The Sodas model
const Schema = mongoose.Schema;
const sodaSchema = new Schema(
{
name: {
type: String,
required: true,
},
drinker: {
type: Schema.Types.ObjectId,
ref: "Drinkers",
},
},
{ timestamps: true }
);
I would add a middleware function which validates if the drinkerId exists. If it exists, you can continue to the controller. If not, then you should throw a 404 error.
Your route:
router.get(
'/all/:drinkerId',
drinkerMiddleware.exists,
sodasController.getAllSodasFromDrinker
);
drinkerMiddleware:
exports.exists = async (req, res, next) => {
try {
const drinker await Drinker.find({ drinker: req.params.drinkerId }).exec();
if (!drinker) {
return next("Drinker not found.");
}
return next();
} catch (error) {
return next(error);
}
};

Why do I get a 400 error when logging a user using bcrypt?

I'm trying to create login authentication, but I keep getting an 400 error in Postman saying that my syntax is bad when testing things out. I'm pretty sure my entire User model is solid, but for good measure, I've attached the whole thing in case something's off there. Otherwise, I'm really not sure what the problem is or where to go from here.
This is the data that I'm sending that triggers the 400 Bad Request (the request cannot be fulfilled due to bad syntax) and logs the invalid password to the console:
{
"email": "andrew#example.com",
"password": "Red12345!"
}
Here's my entire user model code:
const mongoose = require('mongoose')
const validator = require('validator')
const bcrypt = require('bcryptjs')
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true
},
email: {
type: String,
unique: true,
require: true,
trim: true,
lowercase: true,
validate(value) {
if(!validator.isEmail(value)) {
throw new Error('Email is invalid')
}
}
},
age: {
type: Number,
default: 0,
validate(value) {
if(value < 0) {
throw new Error('Age must be a positive number.')
}
}
},
password: {
type: String,
trim: true,
lowercase: true,
required: true,
minlength: 7,
validate(value) {
if(value.toLowerCase().includes("password")) {
throw new Error("Password can't be 'password'.")
}
}
}
})
userSchema.statics.findByCredentials = async (email, password) => {
const user = await User.findOne({ email })
if (!user) {
throw new Error('User not found')
}
const isMatch = await bcrypt.compare(password, user.password)
if (!isMatch) {
throw new Error('Invalid password')
}
return user
}
//Hash the plain text password before saving
userSchema.pre('save', async function (next) {
const user = this
if(user.isModified('password')) {
user.password = await bcrypt.hash(user.password, 8)
}
next()
})
const User = mongoose.model('User', userSchema)
module.exports = User
And here's the user login router:
router.post('/users/login', async (req, res) => {
try {
const user = await User.findByCredentials(req.body.email, req.body.password)
res.send(user)
} catch (e) {
console.log(e.message)
res.status(400).send()
}
})

Categories

Resources