In my route
router.get("/", verifyAdmin, getUsers);
It should first verify admin then move to the getUsers function.
but instead it is moving towards getUsers function.
Routes:
import express from "express";
import {
updateUser,
deleteUser,
getUser,
getUsers,
} from "../controllers/user.js";
import { verifyAdmin, verifyUser,verifyToken } from "../middlewares/verifytoken.js";
const router = express.Router();
//UPDATE
router.put("/:id", verifyUser, updateUser);
//DELETE
router.delete("/:id", verifyUser, deleteUser);
//GET
router.get("/:id", verifyUser, getUser);
//GET ALL
router.get("/", verifyAdmin, getUsers); // Problem is occouring here
export default router;
Middlewares:
import jwt from "jsonwebtoken";
const JWT_KEY = "jwt"; // for development its here only
export const verifyToken = (req,res,next) => {
const token = req.cookies.access_token;
if(!token) return res.status(404).json("You are not authenticated");
jwt.verify(token,JWT_KEY,(err,user)=>{
if(err) return res.status(403).json("Token is invalid");
req.user =user;
next();
})
};
export const verifyUser = (req, res,next) => {
verifyToken(req, res, next,()=>{ // This section is not working
if(req.user._id === req.params.id || req.user.isAdmin){
next();
}else{
return next(res.status(403).json("You are not allowed to access this."));
}
})
}
export const verifyAdmin = (req, res,next) => {
verifyToken(req,res,next,() => {
if (req.user.isAdmin) {
return next();
} else {
return res.status(403).json("you are not admin");
}
});
};
User controllers:
import User from "../models/User.js";
//Update User
export const updateUser = async(req,res) =>{
try {
const updatedUser = await User.findByIdAndUpdate(req.params.id,{$set:req.body},{new:true});
res.status(200).json(updatedUser);
} catch (error) {
return res.status(500).json(error);
}
}
//Delete User
export const deleteUser = async(req,res) =>{
try {
await User.findByIdAndDelete(req.params.id);
return res.status(200).json("User has been deleted");
} catch (error) {
return res.status(500).json(error);
}
}
// getUser
export const getUser = async(req,res) =>{
try {
const user = await User.findById(req.params.id);
return res.status(200).json(user);
} catch (error) {
return res.status(500).json(error);
}
}
// Get All Users
export const getUsers = async(req,res) =>{
try {
const users = await User.find();
return res.status(200).json(users);
} catch (error) {
return res.status(500).json(error);
}
}
Related
I'm getting the error JsonWebTokenError: jwt must be a string when getting the jwt from the front end (react.js) and using in middleware to verify the token. If I tried to use toString it gives me another error JsonWebTokenError: jwt malformed.
Update
As soon as i pass the accessToken from frontEnd it converted into object in the AuthMiddleware.js. I'm passing middleware on header in file Post.js(attached below)
AuthMiddleware.js
const { verify } = require("jsonwebtoken")
const validateToken = (res, req, next) => {
const accesToken = req.header("accesToken");
const stringAccesToken = accesToken.toString()
console.log(typeof (stringAccesToken), "accesToken type")
if (!stringAccesToken) {
return res.json({ error: "user not logged in" })
}
try {
const validToken = verify(stringAccesToken, "importantSecret");
console.log(validToken, 'validtoken')
if (validToken) {
return next();
}
} catch (err) {
console.log(err, "error")
}
}
module.exports = { validateToken }
User.js (backend for login)
const express = require("express");
const router = express.Router()
const { Users } = require("../models")
const bcrypt = require("bcrypt")
const { sign } = require("jsonwebtoken")
router.post("/login", async (req, res) => {
const { username, password } = req.body;
const user = await Users.findOne({ where: { username: username } });
if (!user) {
return res.json({ error: "User dosen't exist" })
}
bcrypt.compare(password, user.password)
.then((match) => {
if (!match) {
return res.json({ error: "Wrong Username and Password match" })
}
const accessToken = sign({ username: user.username, id: user.id }, "importantSecret")
res.json(accessToken)
})
})
module.exports = router;
Post.js
import React, { useEffect, useState } from 'react'
import { useParams } from 'react-router-dom';
import axios from 'axios';
import './Post.css'
function Post() {
let { id } = useParams();
const [postObject, setPostObject] = useState({})
const [comments, setComments] = useState([]);
const [newComment, setNewComment] = useState("");
// console.log(comments)
const addComment = () => {
const accessToken = sessionStorage.getItem('accessToken')
console.log(typeof (accessToken), 'acces token in comment button')
axios.post(`http://localhost:4000/comments`,
{
commentBody: newComment,
PostId: id
},
{
headers: {
accessToken: sessionStorage.getItem("accessToken"),
}
}
)
.then((res) => {
// console.log(res)
const data = res.data;
console.log(data, 'comments')
setComments([...comments, data])
setNewComment("")
})
.catch((err) => {
alert(err, 'Error:comment')
})
}
useEffect(() => {
axios.get(`http://localhost:4000/posts/byId/${id}`)
.then((res) => {
// console.log(res)
const data = res.data;
// console.log(data)
setPostObject(data)
// setPost(data)
})
// comment api request
axios.get(`http://localhost:4000/comments/${id}`)
.then((res) => {
// console.log(res)
const data = res.data;
// console.log(data)
setComments(data)
})
}, [])
return (
<div className='Post'>
<div className='left__side'>
<div className='left__side__wrapper'>
<div className='title'>{postObject.title}</div>
<div className='text'>{postObject.postText}</div>
<div className='username'>{postObject.username}</div>
</div>
</div>
<div className='right__side'>
<div className='right__side__wrapper'>
<div className='add__comment__container'>
<input type="text"
value={newComment}
placeholder="Comment"
// autoComplete="off"
onChange={(e) => setNewComment(e.target.value)}
/>
<button onClick={addComment}> Submit Comment</button>
</div>
<div className='listOfCommnets'>
{comments.map((item, index) => {
{/* console.log(item, 'item') */ }
return <div className='comments' key={index}>Comments:<br />{item.commentBody}</div>
})}
</div>
</div>
</div>
</div>
)
}
export default Post
Because the jwt token is using an object as an input rather than using the word "verify," it won't work with the object in which you are receiving the error any longer. Instead, you must attempt as follows.
var jwt = require("jsonwebtoken");
const validateToken = (res, req, next) => {
const accesToken = req.header("accesToken");
const stringAccesToken = accesToken;
if (!stringAccesToken) {
return res.json({ error: "user not logged in" });
}
jwt.verify(stringAccesToken, "importantSecret", function (err, decoded) {
if (err)
return console.log(err)
// Next Code
next();
});
};
module.exports = { validateToken };
I found the solution:
As the error said JWT need to be string,
I first tried using accesToken.toString() that gives [object object],
On second try i used JSON.stringy and that was also unsuccessful.
Final scuccesful attemp was to use library name - flatted (to convert json to string and after using it i just used split till i did'nt get the token).
faltted (link) - https://github.com/WebReflection/flatted#flatted
Worked Solution -
AuthMiddleware.js
const { parse, stringify, toJSON, fromJSON } = require('flatted');
const validateToken = (res, req, next) => {
const authToken = req.header("accessToken");
const string = stringify(authToken)
const token = string && string.split(' ')[2];
const tokenPure = token.split('"')[4]
if (!tokenPure) {
return res.json({ error: "user not logged in" })
}
try {
const validToken = verify(tokenPure, "importantSecret");
// console.log(validToken, 'validtoken')
if (validToken) {
return next();
}
} catch (err) {
console.log(err, "error")
}
}
module.exports = { validateToken }
I'm using mern to set up a social media of sorts. I'm trying to do if a post is favourited then it should be true if not false. However the result for favourited is always false regardless of if the post is in the users likes in the DB.
If the post has been favourited the button is meant to be set to show the word removed, that way the user knows they've favourited it. If its not favourited(false) it should show add. However, if the user favourites a post and likes it then refreshes the page instead of showing remove it shows add and if the user adds it again it then adds the same movie to the DB.
favourite routes
const express = require('express');
const router = express.Router();
const { authJwt } = require("../middlewares");
const Favourite = require("../models/favourite.model");
router.use(function(req, res, next) {
res.header(
"Access-Control-Allow-Headers",
"x-access-token, Origin, Content-Type, Accept"
);
next();
});
router.post("/favouriteNumber", [authJwt.verifyToken], (req, res) => {
Favourite.find({"movieId": req.body.movieId})
.exec((err, favourite) => {
if(err) return res.status(400).send(err)
res.status(200).json({success: true, favouriteNumber: favourite.length})
})
})
router.post("/favourited", [authJwt.verifyToken], (req, res) => {
Favourite.find({"movieId": req.body.movieId, "userFrom": req.body.userFrom})
.exec((err, favourite) => {
if(err) return res.status(400).send(err)
let result = false;
if(favourite.length !== 0) {
result = true
}
res.status(200).json({success: true, favourited: result});
})
})
router.post("/addToFavourite", [authJwt.verifyToken], (req, res) => {
const favourite = new Favourite(req.body)
favourite.save((err, doc) => {
if(err) return res.json({success: false, err})
return res.status(200).json({success: true, doc})
})
})
router.post("/removeFavorite", [authJwt.verifyToken], (req, res) => {
Favourite.findOneAndDelete({movieId: req.body.movieId, userFrom: req.body.userFrom})
.exec((err, doc) => {
if(err) return res.json({success: false, err})
return res.status(200).json({success: true, doc})
})
})
router.post("/getFavourites", [authJwt.verifyToken], (req, res) => {
Favourite.find({ userFrom: req.body.data })
.populate('userFrom')
.exec((err, films) => {
if(err) return res.status(400).send(err)
res.status(200).json({success: true, films})
})
})
module.exports = router;
favourite component
import Axios from 'axios';
import React, { useEffect, useState } from 'react'
import styled from "styled-components";
import authService from '../../services/auth.service';
import authHeader from '../../services/auth-header';
const FavouriteButton = styled.button`
height: 30px;
width: 40px;
`;
function FavouriteComp(props) {
const currentUser = authService.getCurrentUser();
const [favourited, setFavourited] = useState(false);
const variable = {
userFrom: currentUser.id,
movieId: props.movieInfo?.id,
movieTitle: props.movieInfo?.title,
movieImg: props.movieInfo?.poster_path
}
const onClickFavourite = () => {
//if user already likes the film - remove it
if(favourited) {
Axios.post('http://localhost:8080/api/favourite/removeFavorite', variable, { headers: authHeader() })
.then(response =>{
if(response.data.success){
setFavourited(!favourited)
console.log("Removed from favourites")
}else {
alert('Failed to remove');
}
})
//if not - add to favourites
}else {
Axios.post('http://localhost:8080/api/favourite/addToFavourite', variable, { headers: authHeader() })
.then(response =>{
if(response.data.success){
setFavourited(!favourited)
console.log("Added to favourites")
}else {
alert('Failed to add');
}
})
}
}
useEffect(() => {
Axios.post('http://localhost:8080/api/favourite/favourited', variable, { headers: authHeader() })
.then(response =>{
if(response.data.success){
// setFavourited(response.data.favourited)
// console.log(response.data)
}else {
alert('Failed to get info');
}
})
}, [])
return (
<div>
<FavouriteButton onClick={onClickFavourite}>{!favourited ? "add" : "remove"}</FavouriteButton>
</div>
)
}
export default FavouriteComp
So I am trying to redirect after I am deleting the page, it get's deleted from the database but it doesn't redirect me to my homepage. It worked fine when I was using json-server locally, but when I started using Mongoose it wasn't working properly and wasn't redirecting.
The code inside .then isn't working, I tried console.log inside the .then but it didn't log
I am using mongoose as my database
Here is my entire component:
import { useParams } from "react-router-dom";
import useFetch from "../useFetch";
import { useHistory } from "react-router-dom";
import moment from "moment";
import profile_image from "../images/picture.jpg";
const BlogDetails = () => {
let blogDate = moment().format('D MMM YYYY');
const { id } = useParams();
const { data: blog, isPending, errorMsg } = useFetch("http://localhost:5000/postsdata/" + id);
const history = useHistory()
const handleDelete = () => {
fetch('http://localhost:5000/postsdata/' + blog._id, { method: 'DELETE' })
.then(() => {
history.push('/');
})
.catch((err) => console.log(err))
}
return (
<div className="blog-details">
<div className="top-profile">
<div className="top-profile-picture">
<img src={profile_image} alt="profile-pic-top" />
</div>
<div className="top-profile-name">
<p>Vishwajeet Deshmukh</p>
</div>
</div>
{isPending && <div>Loading...</div>}
{errorMsg && <div>{errorMsg}</div>}
{blog && (
<article className="blog-main-content" >
<div className="main-content-header">
<div className="content-title-date">
<h2 className="blogdetails-title">{blog.title}</h2>
<p className="blogdetails-date">{blogDate}</p>
</div>
<div className="content-image">
<img src={blog.imgsrc} alt="" />
</div>
</div>
<div className="blogdetails-body"><p>{`${blog.postBody}`}</p></div>
<button className="blogdetails-delete" onClick={handleDelete}>Delete Me</button>
</article>
)}
</div>
);
};
export default BlogDetails;
Here is my router.js which handles my delete
const express = require('express');
const router = express.Router();
const { Posts } = require("./models");
//<----------------------------------- CRUD OPERATIONS ------------------------------------------>
router.get("/", () => {
console.log("Server Connected");
})
//<---------------------------- Get Posts from Database ---------------------------->
router.get("/postsdata", (req, res) => {
Posts.find((err, data) => {
if (err) {
res.status(500).send(err);
} else {
res.status(201).send(data);
}
return null;
})
})
//<------------- Get Specific Posts from Database --------------->
router.get("/postsdata/:_id", (req, res) => {
const id = req.params._id;
Posts.findById(id, (err, data) => {
if (err) {
res.status(500).send(err);
throw new Error(err)
} else {
res.status(201).send(data);
}
return data;
})
})
//<---------------------------- Post On the Posts Database ---------------------------->
router.post("/postsdata", (req, res) => {
const db = req.body;
Posts.create(db, err => {
if (!err) {
console.log("Posted on Server");
} else {
throw new Error(err)
}
return null
})
})
//<---------------------------- Delete Posts from Database ---------------------------->
router.delete("/postsdata/:id", (req, res) => {
const id = req.params._id
Posts.deleteOne(id, (err, data) => {
if (err) {
console.log(err);
throw new Error(err)
} else {
console.log(data);
}
return null
})
})
module.exports = router;
after deleting the postdata, send a response from the API.
router.delete("/postsdata/:id", (req, res) => {
const id = req.params._id
Posts.deleteOne(id, (err, data) => {
if (err) {
console.log(err);
throw new Error(err)
} else {
return res.status(200).json({status: 'success'}); // try with this
}
return null
})
})
Hello try it with async/await sayntax
const handleDelete = async () => {
await fetch('http://localhost:5000/postsdata/' + blog._id, { method: 'DELETE' });
history.push('/');
}
I'm using nuxtjs/axios and Mongoose to write to MongoDB. The POST always works but it takes a few seconds for the insert to get into MongoDB. Problem is that I'm trying to call a GET immediately after a new POST so i can get all the latest records. That doesn't always happen because it takes a few seconds for the data to get into the DB. Here's my index.js file for the server:
if (process.env.NODE_ENV !== 'production') {
require('dotenv').config();
}
const Post = require('./models/post');
const express = require('express');
const { Nuxt, Builder } = require('nuxt');
const app = express();
const mongoose = require('mongoose');
const xss = require('xss-clean');
app.use(
express.urlencoded({
extended: true
})
)
app.use(express.json())
app.use(xss());
const config = require('../nuxt.config.js');
config.dev = process.env.NODE_ENV !== 'production';
const nuxt = new Nuxt(config);
const { host, port } = nuxt.options.server;
const username = process.env.username;
const pwd = process.env.pwd;
const server = process.env.server;
const db = process.env.db;
const dbURI = `mongodb+srv://${username}:${pwd}#${server}/${db}?
retryWrites=true&w=majority`;
async function start() {
if (config.dev) {
const builder = new Builder(nuxt);
await builder.build();
} else {
await nuxt.ready();
}
app.use(nuxt.render);
}
start();
mongoose
.connect(dbURI, {useNewUrlParser: true, useUnifiedTopology: true})
.then((result) => {
app.listen(port, host); // listen
}
)
.catch(err => console.log(err));
app.get('/posts', (req, res) => {
Post
.find()
.sort({createdAt: -1})
.then((result) => {
res.send(result);
})
.catch((err) => console.log(err));
})
app.post(
'/posts',
(req, res) => {
const post = new Post({
body: req.body.post.trim()
});
post
.save()
.then((result) => {
res.send(result);
})
.catch((err) => console.log(err));
}
);
I feel like in app.post the .save() isn't waiting for the insert to complete. Is my implementation wrong? Here's my Store:
export const actions = {
async getPosts() {
let res = await this.$axios.get(`/posts`);
return res;
}
}
export const mutations = {
async savePost(state, data) {
let res = await this.$axios.post('/posts', {post: data});
return res;
}
}
And here's my index.vue file:
export default {
components: {},
data: () => ({
posts:[],
confession: ""
}),
mounted(){
this.getPosts();
},
methods: {
async getPosts() {
let res = await this.$store.dispatch('getPosts');
this.posts = res;
},
async savePost(payload) {
let res = await this.$store.commit('savePost', payload);
return res;
},
clear(){
this.confession = "";
},
focusInput() {
this.$refs.confession.focus();
},
onSubmit() {
this
.savePost(this.confession.trim())
.then((result) => {
this.playSound();
this.getPosts();
this.clear();
this.focusInput();
});
},
playSound: function(){
// sound code
}
}
}
}
Maybe you can try to add w: "majority" option in save method.
Mongoose Documentation of save options
MongoDB Documentation for further explanation of 'writeConcern'
app.post(
'/posts',
(req, res) => {
const post = new Post({
body: req.body.post.trim()
});
post
.save({w: "majority"})
.then((result) => {
res.send(result);
})
.catch((err) => console.log(err));
}
);
I am having a slight problem with authorizing the admin.
The backend code works, but i have got problems with requesting the admin route and authenticating the logged in user. First thing i have tried was to put the isAdmin value in the cookies, but it wasnt secure. Then i tried to verify the admin with cookies, i used cookie.get() to get the token. But it was not a success.
code Authorization:
const isAdmin = async (req, res, next) => {
if (!req.user.isAdmin) {
res.status(401).send({ msg: "Not an authorized admin" });
} else {
res.send(req.user.isAdmin);
// const token = req.header("auth-token");
// const verified = verify(token, process.env.SECRET);
// req.user = verified;
// next();
}
next();
};
code Admin route:
router.get("/adminPanel", isAuth, isAdmin, (req, res) => {});
code Login page:
const handleSubmit = e => {
e.preventDefault();
Axios.post("http://localhost:5000/users/login", {
email,
password,
})
.then(response => {
cookie.set("token", response.data.token, {
expires: 1,
});
setUser({
token: response.data.token,
});
if (response.data.isAdmin) {
alert("admin");
} else {
alert("not an admin");
}
// console.log(response.data.token);
// console.log(response.data.isAdmin);
})
.catch(err => {
console.log(err);
});
};
code Admin page:
import React, { useContext, useEffect, useState } from "react";
import Axios from "axios";
import { userContext } from "../../App";
export default function Home() {
const [user, setUser] = useContext(userContext);
const [content, setContent] = useState("login plz to display the content");
useEffect(() => {
// Axios.get("http://localhost:5000/users/adminPanel").then(response =>
// console.log(response.data),
// );
// async function fetchAdmin() {const result = await
Axios.get("http://localhost:5000/users/adminPanel", {
headers: {
Authorization: `Bearer ${user.isAdmin}`,
},
});
// }
// fetchAdmin();
// async function fetchProtected() {
// const result = await (
// await fetch("http://localhost:5000/users/adminPanel", {
// method: "GET",
// headers: {
// "Content-Type": "application/json",
// authorization: `Bearer ${user.token}`,
// },
// })
// ).json();
// if (result.isAdmin) setContent("Admin");
// }
// fetchProtected();
}, [user]);
return `${content}`;
}
Getting the token from cookies:
const [user, setUser] = useState({});
useEffect(() => {
setUser({ token: cookie.get("token") });
}, []);
console.log(user);
Taking into account your route
router.get("/adminPanel", isAuth, isAdmin, (req, res) => {});
I assume req.user.isAdmin is set in isAuth middleware, so your isAdmin middleware should check that parameter, let it pass if so, or reject it otherwise.
In the isAuth middleware after you validate the user, you should know if is an admin or not, so just set the parameter like this:
const isAuth = async (req, res, next) => {
// other code
req.user.isAdmin = true // put your logic here to reflect the status
next(); // pass the control to next middleware, in your example to isAdmin
}
Finally isAdmin could look like this:
const isAdmin = async (req, res, next) => {
if (!req.user.isAdmin) {
res.status(401).send({ msg: "Not an authorized admin" });
} else {
next();
}
};