Mongoose returning string instead of object - node.js

I am experiencing a slight issue while trying to query mongodb using mongoose.
Here is a sample of my code:
...
resolve: (root, params) => {
var user = User.findOne({email: params.data.email}).exec()
return user;
}
...
This returns the following:
{
"data": {
"login": "{ _id: 596f4cc4f51fa12bf0f5a001,\n updatedAt: 2017-07-19T12:12:52.736Z,\n createdAt: 2017-07-19T12:12:52.736Z,\n email: 'myemail#gmail.com',\n password: '$2a$12$bhPG4TPGR6by/UBTeAnzq.lyxhfMAJnBymDbkFDIHWl5.XF2JG62O',\n __v: 0 }"
}
}
I have no idea why this is happening. Any help would be appreciated.
EDIT
Here is the full code :
var bcrypt = require('bcryptjs');
var _ = require('lodash');
var { GraphQLNonNull, GraphQLString } = require('graphql');
var jwt = require('jsonwebtoken');
var { UserInputType } = require('./UserSchema');
var User = require('./UserModel');
var login = {
type: new GraphQLNonNull(GraphQLString),
args: {
data: {
name: 'Data',
type: new GraphQLNonNull(UserInputType)
}
},
resolve: (root, params, { SECRET }) => {
var user = User.findOne({email: params.data.email}).exec();
var authorized = bcrypt.compareSync(params.data.password, user.password);
if (!authorized) throw new Error('Invalid password');
var token = jwt.sign({
user: _.pick(user, ['_id', 'name'])
}, SECRET, {expiresIn: '1y'});
return token;
}
};

This might be in the function which calls resolve and calls .then of this user promise. Can't tell for sure without more code...

resolve: (root, params, { SECRET }) => {
return new Promise(resolve => {
User.findOne({email: params.data.email}).exec().then(user => {
bcrypt.compare(params.data.password, user.password, (err, authorized) => {
if (!authorized) throw new Error('Invalid password');
var token = jwt.sign({
user: _.pick(user, ['_id', 'name'])
}, SECRET, {expiresIn: '1y'});
resolve(token);
});
});
}).then(token => token);
}
Decided to do this instead and voila! Solved my issue. Thanks for the help.

Related

Nodemailer verification using jwt

I'm trying to make e-mail verification using nodemailer and jwt key. The problem is that when I'm trying to verify jwt key, id of user is always undefined.
What am I doing wrong?
app.post("/api/createAccount", async (req, res) => {
const { login, password } = req.body;
const newUser = await user.create({
login: login,
password: password,
});
jwt.sign(
{ userId: newUser.id },
"SECRETKEY",
{
expiresIn: "7d",
},
(err, token) => {
const url = `http://localhost:5000/api/confirmMail/${token}`;
const options = {
from: "xxx",
to: login,
subject: "verifyacc",
html: `${url} `,
};
transporter.sendMail(options, function (err, info) {
if (err) {
console.log(err);
} else {
console.log(info);
}
});
}
);
});
app.get("/api/confirmMail/:token", async (req, res) => {
try {
const {
userId: { id },
} = jwt.verify(req.params.token, "SECRETKEY");
await user.update({ confirmed: 1 }, { where: { id: id } });
} catch (err) {
console.log(err);
}
return res.redirect("http://localhost:3000/login");
});
The error :
err: Error: WHERE parameter "id" has invalid "undefined" value
The payload of the token you are creating is
{
userId: "foobar"
}
But in the deconstruction during verification
const { userId: { id } } = jwt.verify(...);
you expect it to be
{
userId: {
id: "foobar"
}
}
because the deconstruction you used, roughly translates to
const tmp = jwt.verify(...);
const id = tmp.userId.id;
Thus id is of course undefined, as tmp.userId is (probably) a string or a number, which doesn't have an id property.
Use
const { userId: id} = jwt.verify(...);
await user.update({ confirmed: 1 }, { where: { id } });
which roughly translates to
const tmp = jwt.verify(...);
const id = tmp.userId;
or alternatively
const { userId} = jwt.verify(...);
await user.update({ confirmed: 1 }, { where: { id: userId } });

Service Oriented Architecture in node.js

I am building a Node.js project which has an advanced folder structure for future purposes.
user.register controller:
exports.register = async (req, res) => {
try {
var isValidated = await userService.validateInDatabase(req);
if (!isValidated)
return res
.status(409)
.json({ error: "Phone number or email is already registered" });
var user = await userService.create(req);
return user
} catch (e) {
console.trace(e);
return res.status(400).json({ message: e.message });
}
};
The services file code:
exports.create = async (user) => {
const hashedPassword = passwordHash.generate(user.password);
let new_user = new User({
phoneNumber,
email,
password: hashedPassword,
});
const payload = {
id: new_user._id,
};
let token = jwt.sign(payload, keys.JWToken, { expiresIn: 31556926 });
const userData = await new_user.save();
return userData;
};
exports.validateInDatabase = async (req) => {
let check_user = await User.findOne({
$or: [{ email: req.body.email }, { phoneNumber: req.body.phoneNumber }],
});
if (check_user) return false;
return true;
};
Now, whenever I send the request from the postman it says invalid password Why is that?

Userschema and controller

I am building a user signup and login api and admin signup and login using express and currently I am testing in the postman, but somehow postman keeps return "error": "firstName is not defined" even though I posted firstname etc. here is my code, can anyone help me to explain it what is wrong? I saw so many videos using all different kinds of method, like generateAuthtakoken in the user.model or joi password library, it is just so overwhelming, can you help me to point to a direction as to how to use express to create ?
this is my user.model file:
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const validator = require("validator");
const userSchema = new mongoose.Schema(
{
firstName: {
type: String,
required: true,
trim: true,
},
lastName: {
type: String,
required: true,
trim: true,
},
email: {
type: String,
required: true,
trim: true,
unique: true,
},
password: {
type: String,
required: true,
},
role: {
type: String,
enum: ["user", "admin"],
default: "user",
},
contactNumber: { type: String },
profilePicture: { type: String },
},
{ timestamps: true }
);
//static signup method
userSchema.statics.signup = async function (email, password) {
//validation
if (!firstName || !lastName || !email || !password) {
throw Error("All fields must be filled");
}
if (!validator.isEmail(email)) {
throw Error("Email is not valid");
}
if (!validator.isStrongPassword(password)) {
throw Error("Password is not strong enough");
}
const exists = await this.findOne({ email });
if (exists) {
throw Error("Email already in use");
}
const salt = await bcrypt.genSalt(10);
const hash = await bcrypt.hash(password, salt);
const user = await this.create({ email, password: bcrypt.hash });
return user;
};
//static login method
userSchema.statics.login = async function (email, password) {
if (!firstName || !lastName || !email || !password) {
throw Error("All fields must be filled");
}
const user = await this.findOne({ email });
if (!user) {
throw Error("Incorrect Email");
}
const match = await bcrypt.compare(password, user.password);
if (!match) {
throw Error("Incorrect password");
}
return user;
};
module.exports = mongoose.model("User", userSchema);
this is my controller file:
const User = require("../models/user");
const jwt = require("jsonwebtoken");
const createToken = (_id) => {
jwt.sign({ _id }, process.env.JWT_SECRET, { expiresIn: "3d" });
};
//login user
const loginUser = async (req, res) => {
const { email, password } = req.body;
try {
const user = await User.login(email, password);
// create token
const token = createToken(user._id);
res.status(200).json({ email, token });
} catch (error) {
res.status(400).json({ error: error.message });
}
res.json({ msg: "login user" });
};
//signup user
const signupUser = async (req, res) => {
const { email, password } = req.body;
try {
const user = await User.signup(email, password);
// create token
const token = createToken(user._id);
res.status(200).json({ email, token });
} catch (error) {
res.status(400).json({ error: error.message });
}
res.json({ msg: "login user" });
};
module.exports = { signupUser, loginUser };
and my router file:
const express = require("express");
const router = express.Router();
const { signupUser, loginUser } = require("../controller/auth");
//login route
router.post("/login", loginUser);
//signup route
router.post("/signup", signupUser);
module.exports = router;
where exactly do you get this error. Please provide full details to regenerate this error.
But as i could guess
In your static login method you do not need firstName and LastName.
In your signup user method you should be passing those missing required db fields as in your model.

TypeError: Cannot read property 'authenticate' of null

I am working in user signin authentication in backend. Whenever I hit send request from postman it shows error as
TypeError: Cannot read property 'authenticate' of null
at /home/saru/mernbootcamp/projbackend/controllers/auth.js
I had check this error in stackoverflow but the solution doesn't match my case
controllers/auth.js
`const User = require("../models/user");
//express-validator
const { check, validationResult } = require('express-validator');
var jwt = require('jsonwebtoken');
var expressJwt = require('express-jwt');
const dotenv = require("dotenv")
const config = dotenv.config({ path: './routes/.env' });
//user object creation for class/model User
const user = new User(req.body);
exports.signin = (req, res) => {
const errors = validationResult(req);
const { email, password } = req.body;
if (!errors.isEmpty()) {
return res.status(422).json({
error: errors.array()[0].msg
});
}
User.findOne({ email }, (err, user) => {
if (err) {
return res.status(400).json({
error: "USER email does not exists"
});
}
console.log(password);
if (!user.authenticate(password)) {
return res.status(401).json({
error: "Email and password do not match"
});
}
//create token
const token = jwt.sign({ _id: user._id }, process.env.SECRET);
//put token in cookie
res.cookie("token", token, { expire: new Date() + 9999 });
//send response to front end
const { _id, name, email, role } = user;
return res.json({ token, user: { _id, name, email, role } });
});
};
`
models/user.js
`var mongoose = require("mongoose");
const crypto = require("crypto");
const uuidv1 = require("uuid/v1");
var userSchema = new mongoose.Schema(
{
email: {
type: String,
trim: true,
required: true,
unique: true
},
encry_password: {
type: String,
required: true
},
salt: String,
},
{ timestamps: true }
);
userSchema
.virtual("password")
.set(function (password) {
this._password = password;
this.salt = uuidv1();
this.encry_password = this.securePassword(password);
})
.get(function () {
return this._password;
});
userSchema.method = {
authenticate: function (plainpassword) {
return this.securePassword(plainpassword) === this.encry_password;
},
securePassword: function (plainpassword) {
if (!password) return "";
try {
return crypto
.createHmac("sha256", this.salt)
.update(plainpassword)
.digest("hex");
} catch (err) {
return "";
}
}
};
module.exports = mongoose.model("User", userSchema);
`
In controllers/auth.js
Replace the if(err) with if(err || !user) in the findOne() method
You can use this code:
User.findOne({email}, (err, user) => {
if (err || !user) {
return res.status(400).json({
error: "USER email does not exists"
})
}
});
Replace if (err) with if (err || !user) and use return before res.status

Graphql mutation sequelize not returning data

am trying to do login with graphql, i want to first of all check if the email exist in the database, then if the email exist, then i will compare the password with the bcrypt hash in the password, if it returns true, then i will update the token in the users table in the database, but when i tested it with this
mutation {
loginUser(user: {email: "kmd#vh.id", password: "1345968"}) {
password
}
}
it returned this instead of te token in the database
mutation {
loginUser(user: {email: "kmd#vh.id", password: "1345968"}) {
token
}
}
here is my code
import models from '../../../models/index.js';
import User from '../../types/user.js';
import UserInput from '../../inputs/user.js';
const jwt = require('jsonwebtoken')
const bcrypt = require('bcryptjs')
require('dotenv').config()
const fs = require('fs');
const path = require('path');
var privateKey = fs.readFileSync(path.join(__dirname, '../../key/private.key'), 'utf8');
export default {
type: User,
args: {
user: {
type: UserInput
}
},
resolve (_,args) {
return models.user.findOne({
where: {email: args.user.email}
}).then((fUser)=> {
bcrypt.compare(args.user.password, fUser.password, function (err, res) {
if (res) {
return models.user.findById(fUser.id).then((user) => {
return user.update({ token: jwt.sign({id:fUser.id, role:fUser.role, email:fUser.email}, privateKey, { expiresIn: '1h' }) });
});
} else {
console.log(err+" error")
}
})
});
}
};
pls ow can i make it return the token
Try using findByIdAndUpdate with {new: true} for updated document. So your code will be something like this
return models.user.findOne({
where: {email: args.user.email}
}).then((fUser)=> {
bcrypt.compare(args.user.password, fUser.password, function (err, res) {
if (res) {
return models.user.findByIdAndUpdate(fUser.id, {
$set: {
token: jwt.sign(
{ id:fUser.id, role:fUser.role, email:fUser.email },
privateKey,
{ expiresIn: '1h' }
)}}, { new: true })
} else {
console.log(err+" error")
}
})
});

Resources