token not found in a resetPassword function in nodejs - node.js

I'm junior software developer which main task is develop a auth nodejs app. When I took the part of reset_password functionality, return me error message that I'm not able to solve.
This is my env file:
TOKEN_SECRET: T0K€N
RESET_PASSWORD_TOKEN: T0K€N
This is my user's model:
../models/user.js
const mongoose = require('mongoose');
const userSchema = mongoose.Schema({
name: {
type: String,
required: true,
min: 6,
max: 255
},
email: {
type: String,
required: true,
min: 6,
max: 1024
},
password: {
type: String,
required: true,
minlength: 6
},
subscribe: {
type: Boolean,
default: false
},
date: {
type: Date,
default: Date.now
},
role: {
type: String,
required: false,
minlength: 4
},
address: {
type: String,
required: false,
minlength: 4,
defaultValue: ""
},
nonce: {
type: String,
required: false,
minlength: 4,
defaultValue: ""
},
})
module.exports = mongoose.model('User', userSchema);
This is my token's model:
../models/token.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema({
token: {
type: String,
required: true,
min: 6
},
id_user:{
type: String,
required: true,
min: 1
}
})
module.exports = mongoose.model('expired_tokens', Schema);
And finally, my user's controller:
const Joi = require('#hapi/joi');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const nodemailer = require("nodemailer");
//models
const User = require('../models/user');
const expired_token = require('../models/expired_token');
//nodemailer
const sendMail = require('../nodemailer');
ResetPassword: async (req, res) => {
const user = await User.findOne({ email: req.body.email });
if (!user) {
return res.status(400).json({
data: "",
message: "User with this email doesn't exist",
status: "error"
})
}
const token = jwt.sign({ id: user._id, email: user.email }, process.env.RESET_PASSWORD_TOKEN );
const data = {
from: 'noreply#hello.com',
to: email,
subject: 'Recuperacion de contraseña',
template: "reset password",
context: {
url: 'http://localhosy:3005/auth/reset_password?token=' + token,
name: user.fullName.split('').split('')[0]
}
}
const link = `${process.env.BASE_URL}/reset_password/${user._id}/${token.token} `;
await sendEmail(user.email, "Password Reset", link);
},
//routes
router.route('/reset_password')
.post(verifyToken, controller.ResetPassword);
Nevertheless, when I tested in postman, return me the error message:
Probably, the error has to do with the enviroment variable in env. file. So, I'd be really grateful if someone could bring me his help or advice in order to solve this situation.
Thanks and have a nice day!

Related

Log a user in and get their profile

I am attempting to log a user in to my DB. When I log the user in, it returns the first userId in the DB and not the user who logged in. I have been struggling with this for a while and really am at a dead end.
This is my POST route to log the user in:
// login
router.post("/login", async (req, res) => {
const user = await User.findOne({
email: req.body.email,
});
const secret = process.env.SECRET;
if (!user) {
return res.status(400).send("the user not found!");
}
if (user && bcrypt.compareSync(req.body.password, user.passwordHash)) {
const token = jwt.sign(
{
userId: user.id,
isAdmin: user.isAdmin,
},
secret,
{ expiresIn: "1d" }
);
res.status(200).send({ user: user.email, token: token });
} else {
res.status(400).send("password is wrong!");
}
});
The const user = await User.findOne({ email: req.body.email, }); this returns the wrong user.
When I query the endpoint get a users profile with the userId it gets the right information. So its got nothing to do with the DB.
This is the call in the app.
const handleSubmit = () => {
axios
.post(`${baseURL}users/login`, {
email: email,
passwordHash: password,
})
.then(res => {
console.log('USER ID TOKEN', res.data.token);
setbearerToken(res.data.token);
AsyncStorage.setItem('bearerToken', res.data.token);
const decoded = decode(res.data.token);
setTokenID(decoded.userId);
dispatch(setUser(res.data));
});
};
user.js model
const userSchema = mongoose.Schema({
contactName: {
type: String,
required: true,
minlength: 5,
maxlength: 50
},
phone: {
type: String,
required: true,
minlength: 5,
maxlength: 50
},
passwordHash: {
type: String,
required: true,
minlength: 5,
maxlength: 1024
},
token: {
type: String,
},
isAdmin: {
type: Boolean,
default: false
},
clubName: {
type: String,
required: true,
},
clubAddress: {
type: String,
required: true,
},
clubEmail: {
type: String,
required: true,
},
clubPhone: {
type: String,
required: true,
},
clubWebsite: {
type: String,
required: true,
},
clubContact: {
type: String,
required: true,
},
})
Your schema doesn't have a field email to filter on.
const user = await User.findOne({
email: req.body.email,
});
Maybe you try clubEmail field. I reproduced the behavior and it looks like that mongoose ignores the filter if the field does not exist in the Schema an just returns the first document in the collection.
E.g.
const userSchema = new Schema(
{
name: String,
age: Number
}
)
const User = mongoose.model('User', userSchema);
User.findOne({name: "Superman"}, ...
Returns the user with name "Superman".
const userSchema = new Schema(
{
name: String,
age: Number
}
)
const User = mongoose.model('User', userSchema);
User.findOne({xname: "Superman"}, ...
But when using xname in the filter document which does not exist in my schema neither in the collection as field the query returns the first document in my test collection (its not Superman).
Also look here similar issue: Model.find Mongoose 6.012 always return all documents even though having filter
Issue reported: https://github.com/Automattic/mongoose/issues/10763
Migration Guide to Mongoose 6:
https://mongoosejs.com/docs/migrating_to_6.html#strictquery-is-removed-and-replaced-by-strict

How to make options (public or private) on Post? in reactjs, nodejs

I'm creating Social Network app using MERN. I have made it so far that users can create posts and have followers and followings. My task is to make an option on the post so it can be public or private. I don't know how to do that. Has anybody idea or example of code how to do it? Thanks!
This is my post model:
const mongoose = require("mongoose");
const postSchema = new mongoose.Schema({
caption: String,
image: {
public_id: String,
url: String,
},
owner: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
createdAt: {
type: Date,
default: Date.now,
},
likes: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
],
comments: [
{
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
comment: {
type: String,
required: true,
},
},
],
visibility: {
type: String,
enum : ["public", "private"],
default: "public"
},
});
module.exports = mongoose.model("Post", postSchema);
This is my user model:
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const crypto = require("crypto");
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, "Please enter a name"],
},
avatar: {
public_id: String,
url: String,
},
email: {
type: String,
required: [true, "Please enter an email"],
unique: [true, "Email already exists"],
},
password: {
type: String,
required: [true, "Please enter a password"],
minlength: [6, "Password must be at least 6 characters"],
select: false,
},
posts: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Post",
},
],
followers: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
],
following: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
],
resetPasswordToken: String,
resetPasswordExpire: Date,
});
userSchema.pre("save", async function (next) {
if (this.isModified("password")) {
this.password = await bcrypt.hash(this.password, 10);
}
next();
});
userSchema.methods.matchPassword = async function (password) {
return await bcrypt.compare(password, this.password);
};
userSchema.methods.generateToken = function () {
return jwt.sign({ _id: this._id }, process.env.JWT_SECRET);
};
userSchema.methods.getResetPasswordToken = function () {
const resetToken = crypto.randomBytes(20).toString("hex");
this.resetPasswordToken = crypto
.createHash("sha256")
.update(resetToken)
.digest("hex");
this.resetPasswordExpire = Date.now() + 10 * 60 * 1000;
return resetToken;
};
module.exports = mongoose.model("User", userSchema);
and here is the logic how of creating post:
exports.createPost = async (req, res) => {
try {
const myCloud = await cloudinary.v2.uploader.upload(req.body.image, {
folder: "posts",
});
const newPostData = {
caption: req.body.caption,
image: {
public_id: myCloud.public_id,
url: myCloud.secure_url,
},
owner: req.user._id,
};
const post = await Post.create(newPostData);
const user = await User.findById(req.user._id);
user.posts.unshift(post._id);
await user.save();
res.status(201).json({
success: true,
message: "Post created",
});
} catch (error) {
res.status(500).json({
success: false,
message: error.message,
});
}
};
In your post's model, you can add a "visibility" field with private & public as possible values (one of them default, obviously). Then every time you are querying for that post or list of posts, use that visibility column to decide if the user should be served that post or a 403 response page.
Check this for enum validation.

Mongoose insertion order

I am a very beginner at APIs. I am trying to create a test API for my upcoming website but I am stuck at some point. I used SQL, and I am used to orders when inserting data into the database. While using MongoDB and the mongoose library at insertion the pre-made Schema's order lost after inserting data.
This is my model file for the user.
const mongoose = require("mongoose")
const UserSchema = mongoose.Schema({
username: {
type: String,
require: true,
minlength: 6,
maxlength: 64
},
password: {
type: String,
require: true,
minlength: 6,
maxlength: 255
},
email: {
verified: {type: Boolean, default: false},
type: String,
require: true,
minlength: 11,
maxlength: 64
},
description: {
type: String,
require: true,
minlength: 20,
maxlength: 5000
},
permissions: {
verified: {type: Boolean, default: false, require:true},
admin: {type: Boolean, default: false, require:true},
server: {type: Number, default: 0, require:true}
},
image: {
type: String,
require: true,
default: ""
},
reg_date: {
type: Date,
default: Date.now(),
require: true
}
});
module.exports = mongoose.model("User", UserSchema);
The body of the POST request:
{
"username": "nameblabla",
"email": "email#email.com",
"description": "akjfhajkléfhakaklsfhak-sfn-ak.sfnkl-asfnkléas-f",
"password": "pasfgasgasdg"
}
And the file of the POST request:
const express = require('express')
const router = express.Router();
const userModel = require('../models/User')
//VALIDATION
const joi = require('#hapi/joi');
const schema = joi.object({
username: joi.string().min(6).required(),
email: joi.string().min(6).required().email(),
description: joi.string().min(20).required(),
password: joi.string().min(6).required()
});
router.post('/signup', async (req,res) => {
//VALIDATE
const {error} = schema.validate(req.body, {abortEarly: false});
if(error){
let msg = ""
error.details.forEach(e => {
msg += e.message + "\n";
});
return res.status(400).send(msg)
}else{
const user = new userModel({
username: req.body.username,
email: req.body.email,
description: req.body.description,
password: req.body.password
});
try{
const savedUser = await user.save();
res.send(savedUser)
}catch(err) {
res.status(400).send(err);
}
}
})
module.exports = router
The inserted data looks like:
{
"permissions": {
"verified": false,
"admin": false,
"server": 0
},
"image": "",
"reg_date": "2020-04-06T14:50:02.910Z",
"_id": "5e8b41a0cddf023104955793",
"username": "Miraglia1",
"email": "email#email.com",
"description": "akjfhajkléfhakaklsfhak-sfn-ak.sfnkl-asfnkléas-f",
"password": "pasfgasgasdg",
"__v": 0
}
So when I save the data the default key:value pairs placed above the submitted data. But is there any solution to keep the Schema order? Please help me.
Thanks in advance. :)

How get user name from the userId to display in a post?

Hi everyone I am trying to add the user's name in the post that is being created by that user but I'm running into trouble.
This is the part of the post where the user's name should be displayed
<Link style={{ textDecoration: "none", color: "black" }}>
<h4
onClick={() => this.handleShowUserProfile(event.userId)}
className="host-name"
>
{getUser(event.userId).name}
</h4>
</Link>
This is where the user is being grabbed from the database
import http from "./httpService";
const apiEndPoint = "http://localhost:3100/api/users";
export function getUsers() {
return http.get(apiEndPoint);
}
export function getUser(userId) {
return http.get(apiEndPoint + "/" + userId);
}
in the backend this is how the user schema looks like
const jwt = require("jsonwebtoken");
const config = require("config");
const Joi = require("joi");
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true,
minlength: 5,
maxlength: 50
},
bio: {
type: String,
required: true,
minlength: 200,
maxlength: 400
},
interests: {
type: Array
},
email: {
type: String,
required: true,
minlength: 5,
maxlength: 255,
unique: true
},
password: {
type: String,
required: true,
minlength: 5,
maxlength: 1024
},
isAdmin: Boolean
});
userSchema.methods.generateAuthToken = function() {
const token = jwt.sign(
{ _id: this._id, isAdmin: this.isAdmin },
config.get("jwtPrivateKey")
);
return token;
};
const User = mongoose.model("User", userSchema);
function validateUser(user) {
const schema = {
name: Joi.string()
.min(5)
.max(50)
.required(),
bio: Joi.string()
.required()
.min(200)
.max(400),
interests: Joi.array().required(),
email: Joi.string()
.min(5)
.max(255)
.required()
.email(),
password: Joi.string()
.min(5)
.max(255)
.required()
};
return Joi.validate(user, schema);
}
module.exports.User = User;
module.exports.validate = validateUser;
this is the event schema...
const Joi = require("joi");
const mongoose = require("mongoose");
const { categorySchema } = require("./category");
const { userSchema } = require("./user");
const Event = mongoose.model(
"Events",
new mongoose.Schema({
image: {
type: String
},
title: {
type: String,
required: true,
minlength: 5,
maxlength: 50,
trim: true
},
user: {
type: userSchema,
required: true
},
details: {
type: String,
required: true,
minlength: 200,
maxlength: 300,
trim: true
},
category: {
type: categorySchema,
required: true
},
numOfAttendies: {
type: Number,
required: true,
min: 3,
max: 10000
}
})
);
this is the handleShowUserProfile
handleShowUserProfile = id => {
this.setState({
showUserProfile: true,
shownUserID: id,
user: getUser(id)
});
};
handleHideUserProfile = () => {
this.setState({
showUserProfile: false
});
};
If I had to guess, getUser() likely returns a promise, not a value. You'll need to look into reading the name from props(state) and updating state instead of trying to do it all right there.
Try logging out that get request. See if the results it returns match up with what you're trying to get from it.
export function getUser(userId) {
const results = http.get(apiEndPoint + "/" + userId);
console.log("results of getUser ::: ", results)
return results
}
The getUser function is an asynchronous function so it will return a promise in most cases you need to use state and the lifecycle methods to fetch and set the username.
Can you update your handleShowUserProfile method like the following?
handleShowUserProfile = async (id) => {
const user = await getUser(id);
this.setState({
showUserProfile: true,
shownUserID: id,
user: user
});
};
I assume your api returns data for http://localhost:3100/api/users/:userId endpoint.
After you check your api and sure that it returns data, you need to check if you send a valid userId in this line.
onClick={() => this.handleShowUserProfile(event.userId)}
Lastly you should also change the following line since the user data is in the state.
{getUser(event.userId).name} => {this.state.user && this.state.user.name }

Having an issue getting user id

So I am working on building a web app and I have a lot of it working. However I am trying to link my user._id to my items database. But when I try to post an item using postman the app crashes saying it cant read property '_id' of null. I know I am missing something but I honestly can't figure out what other code I need to implement. Any help would be great. Thanks
Here is the code for the UserSchema:
const mongoose = require('mongoose');
const passportLocalMongoose = require("passport-local-mongoose");
const UserSchema = new mongoose.Schema({
username: {
type: String,
trim: true,
unique: true,
required: true,
minlength: 3,
maxlength: 15
},
firstName: {
type: String,
required: true,
minlength: 3,
maxlength: 15
},
lastName: {
type: String,
required: true,
minlength: 3,
maxlength: 15
},
email: {
type: String,
unique: true,
required: true
},
resetPasswordToken: String,
resetPasswordExpires: Date,
isAdmin: {
type: Boolean,
default: false
}
});
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("user", UserSchema);
Here is the code for the ItemSchema:
const mongoose = require('mongoose');
const User = require('./user');
const ItemSchema = new mongoose.Schema({
name: {
type: String,
required: true,
minlength: 3,
maxlength: 20
},
description: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
createdBy: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
},
createdAt: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model("items", ItemSchema);
And here is the code for the route thats throwing the error:
const express = require("express");
const router = express.Router();
const User = require("../models/user");
router.route("/item/add")
.post(function(req, res) {
User.findById(req.user._id, function(user, err) {
if (err) {
console.log(err);
}
var item = new Item();
item.name = req.body.name;
item.description = req.body.description;
item.price = req.body.price;
item.createdBy = { id: req.user._id, username: req.user.username };
item.save(function(err) {
if (err) {
res.send(err);
}
res.json({ message: "Item was successfully saved" });
console.log(item);
});
});
});
You need to send your data in json format in postman for example:
{'id':1, 'name':'jhon doe', 'email':'jhondoe#example.com'}
in your backend file you need to call
req.body.id not req.user._id

Resources