Mongoose schema validation err - express

i m applying mongoose to my todolist app on node js and to make sure a person doesn't add an empty new item i m using the required field in mongoose schema for new item and it gives no err untill some i checked to see if its working and try adding an empty item it gave me the err message i put into the required field but also crashed the whole app
here's the node js code
//jshint esversion:6
const express = require("express");
const bodyParser = require("body-parser");
const mongoose = require("mongoose")
const app = express();
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static("public"));
mongoose.connect("mongodb://localhost:27017/todolistDB")
const itemsSchema = {
name: String
// {
// type: String,
// required: [true, "naam dal na ba!$^&y"]
// }
}
const Item = mongoose.model("(Item", itemsSchema)
const welcome = new Item({ name: "welcome to list" })
const add = new Item({ name: "+ will add new item" })
const del = new Item({ name: "nuke the mofo" })
const defaultItems = [welcome, add, del]
// Item.insertMany(defaultItems, (err) => {
// if (err) { console.log("err") } else { console.log("success in default") }
// })
app.get("/", function(req, res) {
Item.find({}, (err, foundItems) => {
if (foundItems.length === 0) {
Item.insertMany(defaultItems, (err) => {
if (err) { console.log("err") } else { console.log("success in default") }
})
res.redirect("/")
} else { res.render("list", { listTitle: "today", newListItems: foundItems }); }
})
});
app.post("/", function(req, res) {
const itemName = req.body.newItem;
const newItem = new Item({ name: itemName })
newItem.save()
res.redirect("/")
// if (req.body.list === "Work") {
// workItems.push(item);
// res.redirect("/work");
// } else {
// items.push(item);
// res.redirect("/");
// }
});
app.post("/delete", function(req, res) {
const itemId = req.body.cbox
Item.findByIdAndRemove(itemId, (err) => {
if (!err) {
console.log("deleted item")
res.redirect("/")
} else { console.log("err") }
})
})
app.get("/:userList", (req, res) => {
const nameOFList = req.params.userList
})
app.get("/work", function(req, res) {
res.render("list", { listTitle: "Work List", newListItems: workItems });
});
app.get("/about", function(req, res) {
res.render("about");
});
app.listen(3000, function() {
console.log("Server started on port 3000");
});
this is the error i m getting
Server started on port 3000
C:\Users\zahab\Desktop\todolist-v2-starting-files\node_modules\mongoose\lib\document.js:2980
this.$__.validationError = new ValidationError(this);
^
ValidationError: (Item validation failed: name: naam dal na ba!$^&y
at model.Document.invalidate (C:\Users\zahab\Desktop\todolist-v2-starting-files\node_modules\mongoose\lib\document.js:2980:32)
at C:\Users\zahab\Desktop\todolist-v2-starting-files\node_modules\mongoose\lib\document.js:2769:17
at C:\Users\zahab\Desktop\todolist-v2-starting-files\node_modules\mongoose\lib\schematype.js:1333:9
at processTicksAndRejections (node:internal/process/task_queues:78:11) {
errors: {
name: ValidatorError: naam dal na ba!$^&y
at validate (C:\Users\zahab\Desktop\todolist-v2-starting-files\node_modules\mongoose\lib\schematype.js:1330:13)
at SchemaString.SchemaType.doValidate (C:\Users\zahab\Desktop\todolist-v2-starting-files\node_modules\mongoose\lib\schematype.js:1314:7)
at C:\Users\zahab\Desktop\todolist-v2-starting-files\node_modules\mongoose\lib\document.js:2761:18
at processTicksAndRejections (node:internal/process/task_queues:78:11) {
properties: {
validator: [Function (anonymous)],
message: 'naam dal na ba!$^&y',
type: 'required',
path: 'name',
value: ''
},
kind: 'required',
path: 'name',
value: '',
reason: undefined,
[Symbol(mongoose:validatorError)]: true
}
},
_message: '(Item validation failed'
}
[nodemon] app crashed - waiting for file changes before starting...

Related

How do I save the state of a page in Express JS?

I'm creating a website that allows users to create a video-board and display YouTube videos, and they can drag/resize these videos. I want users to be able to save their video board page in a unique URL so they can return later, and have multiple different pages.
To do this I've created a unique user id with UUID, and added this to the URL when users create a video board. Then, I connected my website to a MySQL database and used sequelize to create a table using a MVC Pattern. I want to store the state of their video board (positions, videos URL) and assign it to their url. The tables have been created, however, the issue I'm having is nothing is being sent to the database.
GitHub: https://github.com/RyanOliverV/MultiViewer
Controller index:
const controllers = {};
controllers.video = require('./video-board');
module.exports = controllers;
Controller video board:
const { models: { Video } } = require('../models');
module.exports = {
create: (req, res) => {
const { video_url, user_id, position } = req.body;
Video.create({ video_url, user_id, position })
.then(video => res.status(201).json(video))
.catch(error => res.status(400).json({ error }));
},
getAllVideos: (req, res) => {
Video.findAll()
.then(videos => res.status(200).json(videos))
.catch(error => res.status(400).json({ error }));
},
getVideoById: (req, res) => {
const { id } = req.params;
Video.findByPk(id)
.then(video => {
if (!video) {
return res.status(404).json({ error: 'Video not found' });
}
return res.status(200).json(video);
})
.catch(error => res.status(400).json({ error }));
},
update: (req, res) => {
const { id } = req.params;
const { video_url, user_id, position } = req.body;
Video.update({ video_url, user_id, position }, { where: { id } })
.then(() => res.status(200).json({ message: 'Video updated' }))
.catch(error => res.status(400).json({ error }));
},
delete: (req, res) => {
const { id } = req.params;
Video.destroy({ where: { id } })
.then(() => res.status(200).json({ message: 'Video deleted' }))
.catch(error => res.status(400).json({ error }));
},
}
Model index:
const dbConfig = require('../config/db-config');
const Sequelize = require('sequelize');
const sequelize = new Sequelize(dbConfig.DATABASE, dbConfig.USER, dbConfig.PASSWORD, {
host: dbConfig.HOST,
dialect: dbConfig.DIALECT
});
const db = {};
db.sequelize = sequelize;
db.models = {};
db.models.Video = require('./video-board') (sequelize, Sequelize.DataTypes);
module.exports = db;
Model video board:
module.exports = (sequelize, DataTypes) => {
const Video = sequelize.define('video', {
video_url: {
type: DataTypes.STRING,
allowNull: false
},
user_id: {
type: DataTypes.STRING,
allowNull: false
},
position: {
type: DataTypes.JSON,
allowNull: false
}
});
return Video;
}
Route:
const express = require('express');
const router = express.Router();
const { v4: uuidv4 } = require('uuid');
const { video } = require('../../controllers');
router.get('/', (req, res) => {
const user_id = uuidv4();
res.redirect(`/video-board/${user_id}`);
});
router.post('/', (req, res) => {
const { video_url, user_id, position } = req.body;
video.create(req, res, { video_url, user_id, position })
});
router.get('/:id', (req, res) => {
const user_id = req.params.id;
res.render('video-board', { user_id });
});
module.exports = router;
When the user clicks the 'create-video-board' button it creates a unique url, and I'm expecting this to be stored in the database with the page state of that url.

I changed nothing to my code and now I have " TypeError: Cannot destructure property 'type' of 'vnode' as it is null. " when I launch my web app

I have a web application which is linked to an API. Usually I launch the API and it works. And now, for no reason ( I change nothing in my code and in the API ), it does not work anymore and I can a ton of error like the one I shared on my web application. What can I do ?
`
at callWithErrorHandling (vue.runtime.esm-bundler.js?ebac:123)
at setupStatefulComponent (vue.runtime.esm-bundler.js?ebac:1242)
at setupComponent (vue.runtime.esm-bundler.js?ebac:1238)
at mountComponent (vue.runtime.esm-bundler.js?ebac:838)
at processComponent (vue.runtime.esm-bundler.js?ebac:834)
at patch (vue.runtime.esm-bundler.js?ebac:755)
at ReactiveEffect.componentUpdateFn [as fn] (vue.runtime.esm-bundler.js?ebac:856)
at ReactiveEffect.run (vue.runtime.esm-bundler.js?ebac:67)
at setupRenderEffect (vue.runtime.esm-bundler.js?ebac:881)
`
I tried to relaunch the web application but same problem.
I also have a really long answer so I will try to shorten it but it can come from the store in src/stores/yourstore.js
First you will have to import what you need and do it like if it was a state
import { defineStore } from "pinia";
export const useGlobalStateStore = defineStore("global", {
state: () => ({
globalSell: 0,
whateverarray: [...],
}),
Then, you have the getter and actions ( not getter and setter be carful )
getters: {
doubleCount(state) {
return state.globalSell * 2;
},
},
actions: {
incrementGlobalSell() {
this.globalSell++;
},
deleteCategory(id) {
this.categories = this.categories.filter((element) => {
return element.id != id;
});
},
And if you want to import it on you file, it will be first an import pn indexPage.js for example or whatever you want
<script>
-> import {useGlobalStateStore} from "stores/globalState";
import NavComponent from "components/NavComponent";
In the data you get the store
data() {
return {
-> store : useGlobalStateStore(),
email: "",
And to use it it will be
this.store.whatyouwant = whatyouwanttostore
Now for the potential API problem, Make sure to do the good configuration.
this is for the db.config.js in app/config/
module.exports = {
HOST:"sql12.freemysqlhosting.net",
USER:"user",
PASSWORD:"pass",
DB:"nameOfDB"
}
Other configuration you may need is the token config but it's a little bit complicated so no problem with it, tell me if you need it later.
Example of my file from customer.controller.js
const Customer = require("../models/customer.model.js");
const getAllCustomer = (req, res) => {
Customer.getAllRecords((err, data) => {
if (err) {
res.status(500).send({
message: err.message || "Some error occured while
retriveing data.",
});
} else res.send(data);
});
};
const createNewCustomer = (req, res) => {
if (!req.body) {
res.status(400).send({
message: "Content can not be empty.",
});
}
const customerObj = new Customer({
name: req.body.name,
mail: req.body.mail,
password: req.body.password,
address: req.body.address,
postCode: req.body.postCode,
city: req.body.city
});
Customer.create(customerObj, (err, data) => {
console.log(req.body)
if (err) {
res.status(500).send({
message: err.message || "Some error occured while
creating.",
});
} else {
res.send(data);
}
});
};
const updateCustomer = (req, res) =>{
if(!req.body){
res.status(400).send({ message: "Content can not be
empty."});
}
const data = {
name: req.body.name,
mail: req.body.mail,
password: req.body.password,
address: req.body.address,
postCode: req.body.postCode,
city: req.body.city
};
Customer.updateByID(req.params.id, data, (err, result)=>{
if(err){
if(err.kind == "not_found"){
res.status(401).send({
message: "Not found Customer id: " +
req.params.id
});
} else{
res.status(500).send({
message: "Error update Customer id: " +
req.params.id
});
}
} else res.send(result);
});
};
const deleteCustomer = (req, res) =>{
Customer.delete(req.params.id, (err, result)=>{
if(err){
if(err.kind == "not_found"){
res.status(401).send({
message: "Not found Customer id: " +
req.params.id
});
}else{
res.status(500).send({
message: "Error delete Customer id: " +
req.params.id
});
}
}
else res.send(result);
});
};
const loginCustomer = (req, res) => {
if (!req.body) {
res.status(400).send({
message: "Content can not be empty.",
});
}
const account = new Customer({
mail: req.body.mail,
password: req.body.password
});
Customer.login(account, (err, data)=>{
if(err){
if(err.kind == "not_found"){
res.status(401).send({
message: "Not found " + req.body.mail
});
} else if (err.kind == "invalid_pass"){
res.status(401).send({
message: "Invalid Password"
});
} else{
res.status(500).send({
message: "Error retriveing " + req.body.mail
});
}
}else res.send(data);
});
};
module.exports = {
getAllCustomer,
createNewCustomer,
updateCustomer,
deleteCustomer,
loginCustomer
};
The problem you have could come from the route also in src/router/routes.js
const routes = [
{
path: '/',
component: () => import('layouts/MainLayout.vue'),
children: [
{ path: '', component: () => import('pages/IndexPage.vue') },
{ path: 'signin', component: () => import('pages/SigninPage.vue')
},
{ path: 'signup', component: () => import('pages/SignupPage.vue')
},
]
},
{
path: '/:catchAll(.*)*',
component: () => import('pages/ErrorNotFound.vue')
}
]
export default routes
This error could come from the route, try this an give me update!
And if we follow this priciple, the API route would be
module.exports = (app) => {
const customer_controller =
require("../controllers/customer.controller")
var router = require("express").Router();
router.post("/add", customer_controller.createNewCustomer);
router.get("/all", customer_controller.getAllCustomer);
router.put("/:id", customer_controller.updateCustomer);
router.delete("/:id", customer_controller.deleteCustomer);
router.post("/login", customer_controller.loginCustomer);
app.use("/api/customer", router);
};
Maybe there is a problem with the axios.js file in src/boot/ folder
import { boot } from 'quasar/wrappers'
import axios from 'axios'
// example: const RESTURL = "http://172.26.117.16:3000/api"
const RESTURL = "http://localhost:3000/api"
const api = axios.create({
baseURL: RESTURL,
headers:{ "Content-type" : "application/json" }
})
export default boot(({ app }) => {
app.config.globalProperties.$axios = axios
app.config.globalProperties.$api = api
app.config.globalProperties.$RESTURL = RESTURL
})
export { api, RESTURL }
You can try this !
And to use you new formatted axios in javascript page
this.$api.post("/customer/login", data)
.then(res => {
if (res.status == 200){
this.errorMessage = ""
this.store.loggedUser = res.data
this.$router.push('/')
}
})
.catch((err) => {
this.errorMessage = "Wrong Mail / Password"
})
Example of my customer.model.js
const sql = require("./db");
//Constructor
const Customer = function (customer) {
this.name = customer.name;
this.mail = customer.mail;
this.password = customer.password;
this.address = customer.address;
this.postCode = customer.postCode;
this.city = customer.city;
};
Customer.getAllRecords = (result) => {
sql.query("SELECT * FROM Customer", (err, res) => {
if (err) {
console.log("Query error: " + err);
result(err, null);
return;
}
result(null, res);
});
};
Customer.create = ( newCustomer, result ) => {
sql.query("INSERT INTO Customer SET ?", newCustomer, (err, res)
=> {
if (err) {
console.log("Query error: " + err);
result(err, null);
return;
}
console.log("Created Customer: ", {
id: res.insertId,
...newCustomer
});
result(null, {
id: res.insertId,
...newCustomer
});
})
}
Customer.updateByID = (id, data, result) => {
sql.query(
"UPDATE Customer SET name=?, mail=?, password=?, address=?,
postCode=?, city=? WHERE id=?",
[data.name, data.mail, data.password, data.address,
data.postCode, data.city, id],
(err, res) => {
if (err) {
console.log("Query error: " + err);
result(err, null);
return;
}
if (res.affectedRows == 0) {
//this id not found
result({ kind: "not_found" }, null);
return;
}
console.log("Updated Customer: ", { id: id, ...data });
result(null, { id: id, ...data });
}
);
};
Customer.delete = ( id, result ) => {
sql.query("DELETE FROM Customer WHERE id = ?", id, (err, res)
=> {
if (err) {
console.log("Query error: " + err);
result(err, null);
return;
} if(res.affectedRows == 0){
result({kind: "not_found"}, null)
return;
}
console.log("Deleted Customer id: ", id)
result(null, {id: id})
});
}
Customer.login = (account, result) => {
sql.query(
"SELECT * FROM Customer WHERE mail = ?", account.mail,
(err, res) => {
if (err) {
console.log("Query error: " + err);
result(err, null);
return;
}
if (res.length) {
const validPassword = account.password ==
res[0].password
if (validPassword) {
result(null, res[0]);
return;
} else {
console.log("Password invalid.");
result({ kind: "invalid_pass" }, null);
return;
}
}
result({ kind: "not_found" }, null);
}
);
};
module.exports = Customer
I had the same problem and I solved it.
You can try to relaunch the API every 15min. It works at the second time for me. But I don't know why.

No 'Access-Control-Allow-Origin' header is present on the requested resource (MERN)

This error is never ending, I keep getting it and it's been days I've been trying to find a solution for this annoying error.
Here is what happens when I try to log in.
My app works perfectly fine in localhost but there are alot of issue when I uploaded it to heroku and it is really annoying.
Im using
Axios.defaults.withCredentials = true;
code on my every front end.
My backend
const express = require("express");
const cors = require("cors");
const mongoose = require("mongoose")
const app = express();
const bcrypt = require("bcryptjs")
const saltRounds = 10;
const bodyParser = require("body-parser")
const cookieParser = require("cookie-parser")
const session = require("express-session")
const voterModel = require('./modules/voters.js')
const presidentModel = require('./modules/president.js')
const viceModel = require('./modules/vice.js')
const treasurerModel = require('./modules/treasurer.js')
var MongoDBStore = require('connect-mongodb-session')(session);
app.use(express.json());
const corsOptions = {
origin: 'https://incomparable-speculoos-abdd5f.netlify.app',
//update: or "origin: true," if you don't wanna add a specific one
credentials: true,
};
app.use(cors(corsOptions));
app.options('*', cors());
app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: true }))
mongoose.connect("hidden",
{
useNewUrlParser: true,
useUnifiedTopology: true
}
)
var store = new MongoDBStore({
uri: 'hidden',
collection: 'sessions'
});
// Catch errors
store.on('error', function(error) {
console.log(error);
});
app.use(session({
secret: "hidden",
resave: false,
store: store,
saveUninitialized: false,
cookie: {
maxAge: 1000 * 60 * 60 * 24
}
}))
app.post('/login', async (req, res) => {
const email = req.body.email;
const password = req.body.password;
voterModel.find({email: email}, {"email":1}, async (err, result) => {
if (err) {
console.log(err)
} else {
if(result.length > 0) {
const user = await voterModel.findOne({email: email})
const pass = await user.comparePassword(password)
if (pass) {
req.session.user = user
} else {
console.log("NOT LOGGED IN")
res.send({ message: 'Invalid email or password!'})
}
} else {
console.log("NOT LOGGED IN")
res.send({ message: 'Invalid email or password!'})
}
}
})
})
app.post('/register', async (req, res) => {
const username = req.body.username;
const email = req.body.email;
const password = req.body.password;
// HASING PASSWORD
bcrypt.hash(password, saltRounds, async (err, hash) => {
if (err) {
console.log(err)
}
// INSERTING VALUES
const voters = await voterModel({email: email, username: username, password: hash, status: false})
// CHECKS IF EMAIL IS IN USE
const isNewEmail = await voterModel.isThisEmailInUse(email)
if (!isNewEmail) return res.send({ message: 'This email is already taken!'})
// SAVES THE INSERT DATA FOR VOTERS
await voters.save()
res.send({success: true})
})
})
app.post('/voted', async (req, res) => {
// FOR UPDATING THE VOTING STATUS
const email = req.body.email
// VARIABLES FOR CHOSEN CANDIDATES OF USER
const president = req.body.president
const vice = req.body.vice
const treasurer = req.body.treasurer
// SETS THE STATUS OF VOTER TO TRUE SO HE/SHE CAN ONLY VOTE ONCE
voterModel.updateOne({email: email}, {$set : {status: true}}, (err, result) => {
if (err) {
console.log(err)
} else {
console.log(result)
}
})
// BELOW ARE THE COMMANDS FOR INCREMENTING THE VOTE COUNT OF SELECTED CANDIDATES OF THE VOTER
presidentModel.updateOne({nickname: president}, {$inc : {votes: 1}}, (err, result) => {
if (err) {
console.log(err)
} else {
console.log(result)
}
})
viceModel.updateOne({nickname: vice}, {$inc : {votes: 1}}, (err, result) => {
if (err) {
console.log(err)
} else {
console.log(result)
}
})
treasurerModel.updateOne({nickname: treasurer}, {$inc : {votes: 1}}, (err, result) => {
if (err) {
console.log(err)
} else {
console.log(result)
}
})
})
app.get('/login', (req, res) => {
if (req.session.user) {
res.send({loggedIn: true, user: req.session.user})
} else {
res.send({loggedIn: false})
}
})
app.post('/checkVote', (req, res) => {
const email = req.body.email
const num = true;
voterModel.find({ $and : [{email: email}, {status : num}]},(err, result) => {
if (err) {
console.log(err)
} else {
console.log(result)
if (result.length > 0) {
res.send( {voted: true } )
} else {
res.send( {voted: false } )
}
}
})
})
app.get("/logout", (req, res) => {
req.session.destroy(err => {
if (err) return next(err)
res.status(200).send('logged out')
})
res.status(200).send('User has been logged out');
});
const PORT = process.env.PORT || 3001
app.listen(PORT, () => {
console.log('running on port 3001')
})

TypeError: User.comparePassword is not a function. Comes up with Postman

I'm trying to get my login component on the server side to work, but it keeps giving me this one error over and over. "TypeError: User.comparePassword is not a function" I had it working in postman before, but I can't figure out why it broke now. I'm making an ecommerce site, so I'm excluding code that has stuff to do with the rest of the site.
[user.js - my routing and where the error is being presented.]
const { User, validateUser } = require("../models/user.js");
const bcrypt = require("bcrypt");
const config = require("config");
const jwt = require("jsonwebtoken");
const auth = require("../middleware/auth");
const express = require("express");
const router = express.Router();
//get users
router.get("/", async (req, res) => {
try {
const users = await User.find();
return res.send(users);
} catch (ex) {
return res.status(500).send(`Internal server Error: ${ex}`);
}
});
//get a user
router.get("/:userId", async (req, res) => {
try {
const user = await User.findById(req.params.userId);
return res.send(user);
} catch (ex) {
return res.status(500).send(`Internal server Error: ${ex}`);
}
});
//new user
router.post("/register", async (req, res) => {
try {
const { error } = validateUser(req.body);
if (error) return res.status(500).send(error.details[0].message);
let user = await User.findOne({ email: req.body.email });
if (user) return res.status(400).send("User already registered.");
const salt = await bcrypt.genSalt(10);
user = new User({
name: req.body.name,
email: req.body.email,
password: await bcrypt.hash(req.body.password, salt),
});
await user.save();
const token = jwt.sign(
{ _id: user._id, name: user.name },
config.get("jwtSecret")
);
return res
.header("x-auth-token", token)
.header("access-control-expose-headers", "x-auth-token")
.send({ _id: user._id, name: user.name, email: user.email });
} catch (ex) {
return res.status(500).send(`InternalServerError:${ex}`);
}
});
router.get("/auth", auth, (req, res) => {
res.status(200).json({
_id: req.user._id,
isAdmin: req.user.role === 0 ? false : true,
isAuth: true,
email: req.user.email,
name: req.user.name,
role: req.user.role,
image: req.user.image,
cart: req.user.cart,
history: req.user.history,
});
});
router.post("/login", (req, res) => {
User.findOne({ email: req.body.email }, (err, user) => {
if (!User)
return res.json({
loginSuccess: false,
message: "Auth failed, email not found",
});
});
User.comparePassword(req.body.password, (err, isMatch) => {
if (!isMatch)
return res.json({ loginSuccess: false, message: "Wrong password" });
user.generateToken((err, user) => {
if (err) return res.status(400).send(err);
res.cookie("w_authExp", User.tokenExp);
res.cookie("w_auth", User.token).status(200).json({
loginSuccess: true,
userId: user._id,
});
});
});
router.get("/logout", auth, (req, res) => {
User.findOneAndUpdate(
{ _id: req.user._id },
{ token: "", tokenExp: "" },
(err, doc) => {
if (err) return res.json({ success: false, err });
return res.status(200).send({
success: true,
});
}
);
});
});
[User.js - Userschema]
const mongoose = require('mongoose');
const Joi = require('joi');
const cors = require('cors');
const config = require('config');
const jwt = require('jsonwebtoken');
const { productSchema } = require('./Product');
const { reviewSchema } = require('./review');
const userSchema = new mongoose.Schema({
name: { type: String, required: true, minlength: 5, maxlength: 50},
email: {type: String, unique: true, required: true, minlength: 5, maxlength: 255},
password: {type: String, required: true, maxlength: 1024, minlength: 5},
timestamp: { type: Date, default: Date.now() },
cart: {type: [productSchema], default: []},
newSalePost: {type: [productSchema], default: []},
review: {type: [reviewSchema], default: []},
image: {type: String, required: true}
});
const User = mongoose.model('User', userSchema);
userSchema.methods.generateAuthToken = function () {
return jwt.sign({_id: this._id, name: this.name, isAdmin: this.isAdmin}, config.get('jwtSecret'));
};
function validateUser(user){
const schema = Joi.object({
name: Joi.string().min(5).max(50).required(),
email: Joi.string().min(5).max(255).required().email(),
password: Joi.string().min(5).max(1024).required(),
});
return schema.validate(user);
}
userSchema.statics.findByToken = function (token, cb) {
var user = this;
jwt.verify(token, 'secret', function (err, decode) {
user.findOne({ "_id": decode, "token": token }, function (err, user) {
if (err) return cb(err);
cb(null, user);
})
})
}
exports.User = User;
exports.validateUser = validateUser;
exports.userSchema = userSchema;

How to properly logout with JWT using Passport Strategies?

I am new to JWT and Passport so I started following a MERN tutorial on Youtube by NoobCoder that deals with authentication and authorization using JWT. I reached the part where the route deals with '/logout' and I get Unauthorized as a reply from Postman. The code so far is exactly the same by the looks of it. Can someone help me understand what is wrong here?
I have attached the code in the bottom. Please let me know if more information is required.
Here is the code:
app.js
const express = require('express');
const app = express();
const cookieParser = require('cookie-parser');
const mongoose = require('mongoose');
app.use(cookieParser());
app.use(express.json());
mongoose.connect('mongodb://localhost:27017/mernauth', {useNewUrlParser: true, useUnifiedTopology: true}, () => {
console.log('Successfully connected to DB');
});
const userRouter = require('./routes/User');
app.use('/user', userRouter);
app.listen(5000, () => {
console.log('express server started');
});
passport.js
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const JwtStrategy = require('passport-jwt').Strategy;
const User = require('./models/User');
const cookieExtractor = req => {
let token = null;
if(req && req.cookies) {
token = req.cookies['access_token'];
}
return token;
}
// Authorization
passport.use(new JwtStrategy({
jwtFromRequest: cookieExtractor,
secretOrKey: 'NoobCoder'
}, (payload, done) => {
User.findById({_id: payload.sub}, (err, user) => {
if(err) {
return done(err, false);
}
if(user) {
return done(null, user);
}
else {
return done(null, false);
}
})
}));
// Authenticated local strategy using username and password
passport.use(new LocalStrategy((username, password, done) => {
User.findOne({username}, (err, user) => {
// Something went wrong with DB
if(err) {
return done(err);
}
// If no user exists; null = no error; false = user does not exist
if(!user) {
return done(null, false);
}
// Check if password is correct; callback cb = done
user.comparePassword(password, done);
});
}));
User.js (route)
const express = require('express');
const userRouter = express.Router();
const passport = require('passport');
const passportConfig = require('../passport');
const JWT = require('jsonwebtoken');
const User = require('../models/User');
const Todo = require('../models/Todo');
const signToken = userID => {
return JWT.sign({
iss: "NoobCoder",
sub: userID
}, "NoobCoder", {expiresIn: "1h"});
}
userRouter.post('/register', (req, res) => {
const {username, password, role} = req.body;
User.findOne({username}, (err, user) => {
if(err) {
res.status(500).json({message: {msgBody: "Error has occured", msgError: true}})
}
if(user) {
res.status(400).json({message: {msgBody: "Username is already taken", msgError: true}})
}
else {
const newUser = new User({username, password, role});
newUser.save(err => {
if(err) {
res.status(500).json({message: {msgBody: "Error has occured", msgError: true}})
}
else {
res.status(201).json({message: {msgBody: "Account Successfully Created", msgError: false}})
}
})
}
})
});
userRouter.post('/login', passport.authenticate('local', {session: false}), (req, res) => {
if(req.isAuthenticated()) {
const {_id, username, role} = req.user;
const token = signToken(_id);
res.cookie('access_token', token, {httpOnly: true, sameSite: true});
res.status(200).json({isAuthenticated: true, user: {username, role}})
}
});
userRouter.get('/logout', passport.authenticate('jwt', {session: false}), (req, res) => {
res.clearCookie('access_token');
res.json({user: {username: '', role: ''}, success: true});
});
module.exports = userRouter;
User.js (model)
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const UserSchema = new mongoose.Schema({
username: {
type: String,
required: true,
min: 6,
max: 15
},
password: {
type: String,
required: true,
},
role: {
type: String,
enum: ['user', 'admin'],
required: true
},
todos: [{type: mongoose.Schema.Types.ObjectId, ref: 'Todo'}]
});
UserSchema.pre('save', function(next) {
if(!this.isModified('password')) {
return next()
}
bcrypt.hash(this.password, 10, (err, passwordHash) => {
if(err) {
return next(err);
}
this.password = passwordHash;
next();
});
});
UserSchema.methods.comparePassword = function(password, cb) {
bcrypt.compare(password, this.password, (err, isMatch) => {
if(err) {
return cb(err);
}
else {
if(!isMatch) {
return cb(null, isMatch)
}
return cb(null, this);
}
})
};
module.exports = mongoose.model('User', UserSchema);
Perhaps the /logout route is unauthorised because the JWT token is not present?
JWT token presence can be verified by ensuring that the cookieExtractor function is returning a token
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
Source