I'm working on the backend with nodejs and mongodb . After coding authentication part i'm having unknown issue and i think it's from bcrypt :(
please have a look at my code.
USER MODEL CODE -
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const validator = require("validator");
require("dotenv").config();
const userSchema = mongoose.Schema(
{
email: {
type: String,
required: true,
unique: true,
trim: true,
lowercase: true,
validate(value) {
if (!validator.isEmail) {
throw new Error("Invalid Email");
}
},
},
password: {
type: String,
required: true,
trim: true,
},
role: {
type: String,
enum: ["user", "admin"],
default: "user",
},
name: {
type: String,
required: true,
maxlength: 21,
},
phone: {
required: true,
type: Number,
maxlength: 12,
},
},
{
timestamps: true,
},
);
userSchema.pre("save", async function (next) {
if (this.isModified("password")) {
// hash the password
const salt = await bcrypt.genSalt(10);
const hash = await bcrypt.hash(this.password, salt);
this.password = hash;
}
next();
});
userSchema.methods.generateToken = function () {
const userObj = { id: this._id.toHexString(), email: this.email };
const token = jwt.sign(userObj, process.env.DB_SECRET, {
expiresIn: "3d",
});
return token;
};
userSchema.statics.emailTaken = async function (email) {
const user = await this.findOne({ email });
return !!user;
};
const User = mongoose.model("User", userSchema);
module.exports = {
User,
};
USER API CODE (Please avoid those bunch of console log statements as i had added them for debugging purpose) -
const express = require("express");
const router = express.Router();
require("dotenv").config();
const { User } = require("../../models/userModel");
const bcrypt = require("bcrypt");
// const comparePassword = async (userPassword) => {
// console.log("Comparing password with bcrypt");
// const match = await bcrypt.compare(userPassword, this.password);
// console.log(match);
// return match;
// };
router.route("/signup").post(async (req, res) => {
const { email, password, name, phone } = req.body;
console.log(req.body);
try {
// Check if user email exists
if (await User.emailTaken(email)) {
return res.status(400).json({ message: "Email already exists" });
}
// create user instance and hash password
const user = new User({
email: email,
password: password,
name: name,
phone: phone,
});
// generate jwt token
console.log("user is saving");
const token = user.generateToken();
const userDoc = await user.save();
// send email
// save....send token with cookie
res.cookie("access-token", token).status(200).send(returnUserDoc(userDoc));
} catch (error) {
res
.status(400)
.json({ message: "Error while creating user", error: error });
}
});
router.route("/login").post(async (req, res) => {
try {
// Find user
console.log("Finding User........");
let user = await User.find({ email: req.body.email });
if (!user) {
return res.status(400).json({ message: "Invalid Credentials" });
}
// Compare Password
console.log("Comparing password with bcrypt");
const match = await bcrypt.compare(req.body.password, user.password);
console.log("password compared");
console.log(match);
if (match == false) {
return res.status(400).json({ message: "Invalid Credentials" });
}
console.log("Password Matched");
// Generate Token
console.log("Generating Token........");
const token = user.generateToken();
// Response
console.log("Sending Response........");
res.cookie("access-token", token).status(200).send(returnUserDoc(user));
} catch (error) {
console.log("Error");
res.status(400).json({ message: "Error while loggin in", error: error });
}
});
// functions
const returnUserDoc = (userDoc) => {
return {
_id: userDoc._id,
name: userDoc.name,
email: userDoc.email,
phone: userDoc.phone,
};
};
module.exports = router;
CONSOLE LOGS -
listening on port 3001
Database Connected
Finding User........
Comparing password with bcrypt
Error
I have found that code is executing successfully just before const match = await bcrypt.compare(req.body.password, user.password); this line
On console.log(error.message); i'm getting this -
Error: data and hash arguments required
at Object.compare (D:\CovidHelpers\CovidHelpers\node_modules\bcrypt\bcrypt.js:208:17)
at D:\CovidHelpers\CovidHelpers\node_modules\bcrypt\promises.js:29:12
at new Promise (<anonymous>)
at Object.module.exports.promise (D:\CovidHelpers\CovidHelpers\node_modules\bcrypt\promises.js:20:12)
at Object.compare (D:\CovidHelpers\CovidHelpers\node_modules\bcrypt\bcrypt.js:204:25)
at D:\CovidHelpers\CovidHelpers\server\routes\api\users.js:60:32
at processTicksAndRejections (internal/process/task_queues.js:93:5)
data and hash arguments required
Please help me fix this :)
Thank You
Related
I am trying to show the page only if the Jsonwebtoken is verified and the user is logged on to the website, else show him the sign-in page.
However, I can see the token is generated in MongoDB, and also when I console log I can see that it is all good. But the issue is when I try to verify it using an already generated jwt token i.e.
req.cookies.signinToken
it shows an error.
Please the detail code below:
On app.js
const dotenv = require("dotenv");
const mongoose = require("mongoose");
const express = require("express");
const app = express();
const jwt = require("jsonwebtoken");
const cookieParser = require("cookie-parser");
dotenv.config({ path: "./config.env" });
require("./db/connection");
app.use(express.json());
app.use(cookieParser());
app.use(require("./router/route"));
const PORT = process.env.PORT;
app.listen(5000, () => {
console.log(`server running on ${PORT}`);
});
On route.js
const express = require("express");
const bcrypt = require("bcrypt");
const router = express.Router();
const jwt = require("jsonwebtoken");
require("../db/connection");
const User = require("../model/newUserSchema");
const auth = require("../middleware/auth");
// router.get("/", (req, res) => {
// res.send("hello am backend sever");
// });
//Signup or Register Part
router.post("/signup", async (req, res) => {
const { username, email, cpassword, retypePassword } = req.body;
if (!username || !email || !cpassword || !retypePassword) {
return res.status(422).json({ error: "please enter valid details" });
}
try {
const UserExist = await User.findOne({ email: email });
if (UserExist) {
return res.status(422).json({ error: "email already exist" });
} else if (cpassword !== retypePassword) {
return res.status(422).json({ error: "password incorrect" });
} else {
const user = new User({
username,
email,
cpassword,
retypePassword,
});
const userResgister = await user.save();
if (userResgister) {
return res.status(201).json({ message: "signup successfully" });
}
}
} catch (error) {
console.log(error);
}
});
//Login Part
router.post("/signin", async (req, res) => {
try {
const { email, cpassword } = req.body;
if (!email || !cpassword) {
return res.status(400).json({ error: " please enter valid credentials" });
}
const userLogin = await User.findOne({ email: email });
const token = userLogin.generateAuthToken();
res.cookie("signinToken", token, {
expires: new Date(Date.now() + 25892000000),
httpOnly: true,
});
if (userLogin) {
const isMatch = await bcrypt.compare(cpassword, userLogin.cpassword);
if (isMatch) {
return res.status(200).json({ message: "sigin in scuccessfully" });
} else {
return res.status(400).json({ error: " Invalid credentials" });
}
} else {
return res.status(400).json({ error: " Invalid credentials " });
}
} catch (error) {
console.log(error);
}
});
//watchlistpage
router.get("/watchlist", auth, (req, res) => {
console.log(" this is jwt token test " + req.cookies.signinToken);
res.send(req.rootuser);
console.log(req.rootuser);
});
module.exports = router;
On newUserSchema.js:
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const newUserSchema = new mongoose.Schema({
username: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
cpassword: {
type: String,
required: true,
},
retypePassword: {
type: String,
required: true,
},
tokens: [
{
token: {
type: String,
required: true,
},
},
],
});
newUserSchema.pre("save", async function (next) {
if (this.isModified("cpassword")) {
this.cpassword = await bcrypt.hash(this.cpassword, 12);
this.retypePassword = await bcrypt.hash(this.retypePassword, 12);
}
next();
});
newUserSchema.methods.generateAuthToken = async function () {
try {
let token = jwt.sign({ _id: this._id }, process.env.SECRETKEY);
this.tokens = this.tokens.concat({ token: token });
await this.save();
return token;
} catch (error) {
console.log(error);
}
};
const User = mongoose.model("newUser", newUserSchema);
module.exports = User;
On auth.js (this is also my middleware)
const jwt = require("jsonwebtoken");
const User = require("../model/newUserSchema");
const Auth = async (req, res, next) => {
try {
console.log(JSON.stringify(req.cookies.signinToken) + " this is jwt token test");
const token = req.cookies.signinToken;
const verifytoken = jwt.verify(token, process.env.SECRETKEY);
const rootuser = await User.findOne({ _id: verifytoken._id, "tokens.token": token });
if (!rootuser) {
throw new Error("user not found");
}
req.token = token;
req.rootuser = rootuser;
req.UserID = rootuser._id;
next();
} catch (error) {
res.status(401).send("Unauthorized access");
console.log(error);
}
};
module.exports = Auth;
The API call result in postman
The terminal error :
when I try to console log on route.js inside Signin page i see promise pending
const token = userLogin.generateAuthToken();
console.log(token);
res.cookie("signinToken", token, {
expires: new Date(Date.now() + 25892000000),
httpOnly: true,
});
Could you please help to correct the error also please let me know why is this error coming?
Thanks a million in advance for any tips or suggestions.
Hi Thanks for the help
I just saw that my token was returning a promise, I did not add the keyword await before the token other thing was I was trying to access it before the validation, hence it was showing me nodata and error.
Please see the correct code below:
//Login Part
router.post("/signin", async (req, res) => {
try {
const { email, cpassword } = req.body;
if (!email || !cpassword) {
return res.status(400).json({ error: " please enter valid credentials" });
}
const userLogin = await User.findOne({ email: email });
if (userLogin) {
const isMatch = await bcrypt.compare(cpassword, userLogin.cpassword);
const token = await userLogin.generateAuthToken();
console.log(token);
res.cookie("signinToken", token, {
expires: new Date(Date.now() + 25892000000),
httpOnly: true,
});
if (isMatch) {
return res.status(200).json({ message: "sigin in scuccessfully" });
} else {
return res.status(400).json({ error: " Invalid credentials" });
}
} else {
return res.status(400).json({ error: " Invalid credentials " });
}
} catch (error) {
console.log(error);
}
});
I hope this might help other learners too.
Thanks.
I have created a model and the name of the table is users. In the Model, i have a method generateToken which is used to generate the web token.
I have used sequelized ORM.
module.exports = (sequelize, Sequelize) => {
const Tutorial = sequelize.define("users", {
age: {
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING
},
password: {
type: Sequelize.STRING
}
});
Tutorial.generateToken = async function () {
try {
const token = jwt.sign({ _id: this.id }, "ThisIsTaskApp")
console.log(token)
}
catch (error) {
response.send('there is an error' + error)
console.log('there is an error' + error)
}
}
return Tutorial;
};
I want to create a web token when my email id and password matches, so for that i have used the generateToken but i am getting an error
TypeError: user.generateToken is not a function
I believe i have error with javascript importing the generateToken function.
const jwt = require('jsonwebtoken')
const user = db.users;
const generateToken = require('./models/users')
app.post('/login', async (req, res) => {
try {
var email = req.body.email;
var password = req.body.password;
await user.findOne({ where: { email: email, password: password } })
.then(user => {
if (user === null) {
res.status(404).json({
message: 'Auth Failed'
})
}
else {
const token = user.generateToken()
res.status(200).json(user)
}
})
}
catch (error) {
console.log(error)
return res.json({ 'status': 400 })
}
})
Please help me fix this issue and generating web token.
Try using
generateToken.generateToken()
there instead of
user.generateToken()
Because you are basically exporting the model of users in generate token variable, so that function is accessible from that variable not from user variable.
There is some issue with your code related to async, please try this one
const user = db.users;
app.post("/login", async (req, res) => {
try {
var email = req.body.email;
var password = req.body.password;
const userdata = await user.findOne({ where: { email: email, password: password } });
if (userdata === null) {
return res.status(404).json({
message: "Auth Failed",
});
}
const token = await userdata.generateToken();
console.log("🚀 ~ token", token)
return res.status(200).json(userdata);
} catch (error) {
console.log(error);
return res.json({ status: 400 });
}
});
I think you need to require jsonwebtoken in /models/users as well as in the route handler file
I´m trying to set up a register and login server with node.js and mongoose. So I have create an user model and an user route. Can someone find the mistake why I can´t create an user. I connect to Postman under POST : localhost:3000/users/register
my user model:
const mongoose = require('mongoose');
const uniqueValidator = require('mongoose-unique-validator');
const bcrypt = require('bcryptjs');
const UserSchema = new mongoose.Schema({
email: {
type: String,
required: true,
minlength: 1,
trim: true, //calls .trim() on the value to get rid of whitespace
unique: true, //note that the unique option is not a validator; we use mongoose-unique-validator to enforce it
},
password: {
type: String,
required: true,
minlength: 8,
},
});
//this enforces emails to be unique!
UserSchema.plugin(uniqueValidator);
//this function will be called before a document is saved
UserSchema.pre('save', function(next) {
let user = this;
if (!user.isModified('password')) {
return next();
}
//we generate the salt using 12 rounds and then use that salt with the received password string to generate our hash
bcrypt
.genSalt(12)
.then((salt) => {
return bcrypt.hash(user.password, salt);
})
.then((hash) => {
user.password = hash;
next();
})
.catch((err) => next(err));
});
module.exports = mongoose.model('User', UserSchema);
my routes user:
const express = require('express');
const bcrypt = require('bcryptjs');
const User = require('../models/user');
const router = express.Router();
//util function to check if a string is a valid email address
const isEmail = (email) => {
if (typeof email !== 'string') {
return false;
}
const emailRegex = /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")#(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/;
return emailRegex.test(email);
};
router.post('/register', async (req, res) => {
try {
const { email, password } = req.body;
if (!isEmail(email)) {
throw new Error('Email must be a valid email address.');
}
if (typeof password !== 'string') {
throw new Error('Password must be a string.');
}
const user = new User({ email, password });
const persistedUser = await user.save();
res.status(201).json({
title: 'User Registration Successful',
detail: 'Successfully registered new user',
});
} catch (err) {
res.status(400).json({
errors: [
{
title: 'Registration Error',
detail: 'Something went wrong during registration process.',
errorMessage: err.message,
},
],
});
}
});
router.post('/login', async (req, res) => {
try {
const { email, password } = req.body;
if (!isEmail(email)) {
return res.status(400).json({
errors: [
{
title: 'Bad Request',
detail: 'Email must be a valid email address',
},
],
});
}
if (typeof password !== 'string') {
return res.status(400).json({
errors: [
{
title: 'Bad Request',
detail: 'Password must be a string',
},
],
});
}
//queries database to find a user with the received email
const user = await User.findOne({ email });
if (!user) {
throw new Error();
}
//using bcrypt to compare passwords
const passwordValidated = await bcrypt.compare(password, user.password);
if (!passwordValidated) {
throw new Error();
}
res.json({
title: 'Login Successful',
detail: 'Successfully validated user credentials',
});
} catch (err) {
res.status(401).json({
errors: [
{
title: 'Invalid Credentials',
detail: 'Check email and password combination',
errorMessage: err.message,
},
],
});
}
});
module.exports = router;
my server :
const express = require('express');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const mongoose = require('mongoose');
const dotenv = require("dotenv");
const app = express();
dotenv.config();
//other imports
const usersRoute = require('./routes/users');
//other app.use statements
//connect to db
mongoose.connect(
process.env.DB_CONNECTION,
{ useNewUrlParser: true,
useUnifiedTopology: true},
() => console.log('Database connected')
);
mongoose.Promise = global.Promise;
const port = process.env.PORT || 3000;
//sets up the middleware for parsing the bodies and cookies off of the requests
app.use(bodyParser.json());
app.use(cookieParser());
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
module.exports = { app };
what I only get is this.:
Cannot POST /users/register
You didn't specify the path prefix in your server file. You should define:
app.use("/users", usersRoute );
I have creating one ecommerce application, Inside this i have facing some issue regarding req.gravatar() is not a function.
Whenever I have send data through postman give me error, those error I have defined above.
account.js file code
const router = require('express').Router();
const jwt = require('jsonwebtoken');
const User = require('../models/user');
const config = require('../config');
var gravatar = require('gravatar');
router.post('/signup', (req, res, next) => {
let user = new User();
user.name = req.body.name;
user.email = req.body.email;
user.password = req.body.password;
user.picture = req.gravatar();
user.isSeller = req.body.isSeller;
User.findOne({ email: req.body.email }, (err, existingUser) => {
if(existingUser) {
res.json({
success: false,
message: 'Account with that email is already exist'
});
}
else{
user.save();
var token = jwt.sign({
user: user
}, config.secret, {
expiresIn: '7d'
});
res.json({
success: true,
message: 'Enjoy your token',
token: token
});
}
});
});
module.exports = router;
User.js file code
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const bcrypt = require('bcrypt-nodejs');
const crypto = require('crypto');
const UserSchema = new Schema({
email: { type: String, unique: true, lowercase: true },
name: String,
password: String,
picture: String,
isSeller: { type: Boolean, default: false },
address: {
add1: String,
add2: String,
city: String,
state: String,
country: String,
postalCode: String
},
created: { type: Date, default: Date.now }
});
UserSchema.pre('save', function(next) {
var user = this;
if(!user.isModified('password')) return next();
bcrypt.hash(user.password, null, null, function(err, hash) {
if(err) return next(err);
user.password = hash;
next();
});
});
UserSchema.methods.comparePassword = function(password) {
return bcrypt.compareSync(password, this.password);
}
UserSchema.methods.gravatar = function(size) {
if(!this.size) size = 200;
if(!this.email) {
return 'https://gravatar.com/avatar/?s' + size + '&d=retro';
}
else{
var md5 = crypto.createHash('md5').update(this.email).digest('hex');
return 'https://gravatar.com/avatar/' + md5 + '?s' + size + '&d=retro';
}
}
module.exports = mongoose.model('User', UserSchema);
please help me to solve this question as fast as possible.
because i am Amateur developer in node.js technology.
In the following line, gravatar is not an attribute of req and therefore cannot be invoked as a function
user.picture = req.gravatar();
I suppose that what you want to do, is something like:
user.picture = gravatar.url(user.email);
With this change, user.picture will contain the URL of the gravatar user profile picture for that email.
I have three schemas.
User.js:
const mongoose = require("mongoose");
const bcrypt = require("bcryptjs");
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true,
},
email: {
type: String,
unique: true,
required: true,
},
password: {
type: String,
required: true,
},
});
userSchema.pre("save", function (next) {
const user = this;
if (!user.isModified("password")) {
return next();
}
bcrypt.genSalt(10, (err, salt) => {
if (err) {
return next(err);
}
bcrypt.hash(user.password, salt, (err, hash) => {
if (err) {
return next(err);
}
user.password = hash;
next();
});
});
});
userSchema.methods.comparePassword = function (candidatePassword) {
const user = this;
return new Promise((resolve, reject) => {
bcrypt.compare(candidatePassword, user.password, (err, isMatch) => {
if (err) {
return reject(err);
}
if (!isMatch) {
return reject(false);
}
resolve(true);
});
});
};
mongoose.model("User", userSchema);
Project.js:
const mongoose = require("mongoose");
const diamondSchema = new mongoose.Schema({
criteria: {
novelty: String,
technology: String,
complexity: String,
pace: String,
},
});
const projectSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
projectName: {
type: String,
default: "",
},
projectBudget: {
type: Number,
},
projectDuration: {
type: Number,
},
industry: {
type: String,
},
companyName: {
type: String,
},
numberOfEmployees: {
type: Number,
},
diamond: [diamondSchema],
});
mongoose.model("Project", projectSchema);
Recommendation.js:
const mongoose = require("mongoose");
const diamondSchema = new mongoose.Schema({
criteria: {
novelty: String,
technology: String,
complexity: String,
pace: String,
},
});
const recommendationSchema = new mongoose.Schema({
diamond: [diamondSchema],
description: {
type: String,
},
});
mongoose.model("Recommendation", recommendationSchema);
And two route files.
authRoutes.js:
const express = require("express");
const mongoose = require("mongoose");
const User = mongoose.model("User");
const jwt = require("jsonwebtoken");
const router = express.Router();
router.post("/signup", async (req, res) => {
const { name, email, password } = req.body;
try {
const user = new User({ name, email, password });
await user.save();
const token =
//token has payload-->user id
jwt.sign({ userId: user._id }, "MY_SECRET_KEY");
res.send({ token });
} catch (err) {
//invalid data
return res.status(422).send(err.message);
}
});
router.post("/signin", async (req, res) => {
const { email, password } = req.body;
if (!email || !password) {
return res.status(422).send({ error: "Must provide email and password" });
}
const user = await User.findOne({ email });
if (!user) {
return res.status(404).send({ error: "Invalid email or password" });
}
try {
await user.comparePassword(password);
const token = jwt.sign({ userId: user._id }, "MY_SECRET_KEY");
res.send({ token });
} catch (err) {
return res.status(422).send({ error: "Invalid email or password" });
}
});
module.exports = router;
projectRoutes.js:
const express = require("express");
const mongoose = require("mongoose");
const requireAuth = require("../middlewares/requireAuth");
const Project = mongoose.model("Project");
const Recommendation = mongoose.model("Recommendation");
const router = express.Router();
router.use(requireAuth);
router.get("/projects", async (req, res) => {
const projects = await Project.find({ userId: req.user._id });
res.send(projects);
});
router.post("/projects", async (req, res) => {
const {
projectName,
projectBudget,
projectDuration,
industry,
companyName,
numberOfEmployees,
diamond,
} = req.body;
if (
!projectName ||
!projectBudget ||
!projectDuration ||
!industry ||
!companyName ||
!numberOfEmployees ||
!diamond
) {
return res.status(422).send({ error: "Must provide all project details" });
}
try {
const project = new Project({
projectName,
projectBudget,
projectDuration,
industry,
companyName,
numberOfEmployees,
diamond,
userId: req.user._id,
});
await project.save();
//res.send(project);
} catch (err) {
res.status(422).send({ error: err.message });
}
try {
const rec = await Recommendation.find({ diamond });
//console.log(diamond);
console.log(description);
res.send(rec);
} catch (err1) {
res.status(422).send({ error: err1.message });
}
});
module.exports = router;
Using postman, in the projectRoutes.js file, when I try to send the post request on
localhost:3000/projects, I am trying to create a new project and in response I want description. My logic is that, after I save the new project in projects collection, I am trying to find the document with the SAME DIAMOND OBJECT criteria in recommendations collection which is also present in projects collection. Meaning, I have pre-defined records in recommendations collection and projects collection::
So I need some way so that when I try to add a new project for a user, the criteria object in diamond array I set matches to the criteria object in diamond array in pre-defined one of recommendations documents and in the post request localhost:3000/projects I can return description in response. As I'm doing console.log(description) in projectRoutes.js, it's showing as undefined. I don't know why. Hope it makes sense.
Basically, the idea is there will be limited number of recommendations with unique criterias. So whenever a new project is created based on the criteria, a recommendation is displayed to the user.
Assuming you have only one array element in a project and in an a recommendation
const {
projectName,
projectBudget,
projectDuration,
industry,
companyName,
numberOfEmployees,
diamond,
} = req.body;
const [projectDiamond] = diamond // get the first object in the diamond array
const { criteria } = projectDiamond // extract criteria
const recommendation = await Recommendation.find({ 'diamond.criteria': criteria });
Please note that the order of criteria fields must match, since we are looking up a matching object in an array.
Reference: https://docs.mongodb.com/manual/tutorial/query-arrays/#query-an-array