Having trouble with Multer and JWT - node.js

I'm currently trying to wrap my head around this:
Basically I am trying to verify the users token before uploading the file with multer.
The problem is the JWT from the cookie is "undefined".
This is the route snippet:
router.post("/files/user/:userId", verifyJWT, upload.single("file"), controllerFunction);
In my verifyJWT method I'm doing:
exports.verifyJWT = async (req, res, next) => {
let accesToken = req.cookies.jwt;
if (!accesToken) {
return res.status(401).json({
message: "Acces is unauthorized.",
});
}
let payload;
try {
payload = jwt.verify(accesToken, process.env.JWT_KEY);
req.name = payload.name;
req.type = payload.type;
req._id = payload._id;
next();
} catch (err) {
res.status(403).json({ message: "Token could not be verified!" });
}
};
If my route has the verifyJWT middleware for whatever reason when I'm calling console.log(accesToken) the acces token is sent as undefined.
If i remove the middleware verifyJWT from the "/files/user/:userId" route, and console.log(accesToken) I'm receiving the token.
This only happens on the route which has the multer upload callback in it.
I have already tried the suggestions from this thread but for whatever reason it does not seem to work

Related

Express Middleware not rendering the view

I have written an auth middleware , which when fails , I want to render a page instead of sending response to my xhr request . How to do it ?
const jwt = require('jsonwebtoken')
const User = require('../models/users')
const express = require('express')
const auth = async (req, res ,next) => {
try {
//res.render("404")
const token = req.header('Authorization').replace('Bearer ' , '') //token comes from client in the header section named Auth(here)
const data = jwt.verify(token , 'itistechvaulttoken') //this gives back the id of the user and also iat :- issued at , a callback can be attached to
// .verify :-- see jsonwebtoken documentation for details .
const user = await User.findOne({ _id : data._id})
if(!user) {
throw new Error()
}
req.token = token //this is being use mainly for logout route handler
req.user = user
next()
} catch(e) {
res.status(401).render("404")
}
}
module.exports = auth
I have create a form and sending that data to my backend using xhr request to route and from there i am redirecting it to another route where I want to render the view .
My routes :-
router.post('/users' ,async (req, res) => {
console.log("Request Recieved")
const user = new User(req.body)
try {
await user.save()
const token = await user.generateAuthToken()
//console.log(token)
//res.status(201).send({ user , token})
req.accesstoken = token
res.redirect(302 , "/users/me" )
}
catch(e) {
console.log(e)
res.status(400).send("User not created" + e)
}
})
/user/me :-
router.get('/users/me', auth, async (req, res) => {
console.log("in users/me")
res.send(req.user)
})
I know here authentication has to fail, and code under the catch of middleware should run ,where I am trying to render a view but failing .
I guess you want your xhr endpoint code on your server to make your user's browser display an error page of some kind upon auth failure.
To do that you need the Javascript code in your browser to detect an error message in the response to the xhr request and navigate to the error page. Your nodejs app can't force any kind of redirect when responding to an xhr request without cooperation from the browser.

Using both JWT and Passportjs

I am working on mern stack application where I use both jwt for registration and passportjs for social login. I am currently facing issue of authenticating routes.
Here is the Auth middleware of jwt and passportjs:
const jwt = require("jsonwebtoken")
const Auth = async (req,res,next)=>{
//if(req,headers)
try {
const token = req.headers.authorization.split(" ")[1]
if(!token){
return res.status(404).json({msg:"no token authorization"})
}
let decodeData
decodeData = jwt.verify(token, process.env.JWT_SECRET)
req.userId = decodeData.user.id
next()
} catch (error) {
console.error(error.message);
res.status(500).send("Server Error");
}
}
const passportAuth = (req,res,next)=>{
if(!req.user){
return res.status(404).json({msg:"user not loggged in"})
}else{
next()
}
}
module.exports = Auth
module.exports = passportAuth
logged in user can create post but here I have jwt auth and passportauth.
if use both [Auth, passportAuth] in the route it wont be working becuase either user have to logged in with google or simple signup registration. Here is the authenticated route code
route.post("/",[auth,passportAuth], async(req,res)=>{
const post = req.body
try {
const createpost = new Post({
tags:post.tags,
creator:post.creator,
title: post.title,
message: post.message,
selectedFile: post.selectedFile,
user:req.userId
})
await createpost.save()
res.json(createpost)
} catch (error) {
console.error(error.message);
res.status(500).send("Server Error");
}
})
So what should I do here?
Maybe instead of returning error responses set some property on the response (something like res.authenticated = true / res.authenticated = false). Then have another middleware which would check if this property is set and only return error response if it's false or not present. You would just have to make sure that this middleware is called always as the last one (I don't remember now how it works in express).
As a side note - 404 response is not found. For the case of missing/bad authentication you should rather use 401.

How to fix the "cannot set headers after they are sent to the client" error in node?

I know this question has been asked several times, but the solution provided didn't work for me.
I have a protected route to find a user. The request is validated by an authenticate middleware, which basically checks if the user's cookie is valid, and then the route getUser is called. The route works well when I don't use the middleware, so the issue may come from authenticate.
I have used return res.status(200).json({}) as suggested.
When testing the route with the cookie, chai makes two calls. The first one succeeds, but as soon as the route is hit, another call is made, without any cookie. Weird. In Postman, same thing. It works wonderfully without authenticate, but returns unauthorized with it. In the terminal, the error is:
[ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
Here is the authenticate middleware and the route:
// router
const router = express.Router();
router.get("/user/:id", authenticate, getUser);
// authenticate
const authenticate = (req: Authenticate, res: Response, next: NextFunction) => {
const { mycookie } = req.cookies;
if (mycookie) {
jwt.verify(mycookie, process.env.JWT_TOKEN_KEY, (error, parsedToken) => {
if (error) {
return res.sendStatus(403);
}
req.cookie = { _id: parsedToken._id, locale: parsedToken.locale };
return next();
});
}
return res.sendStatus(401);
};
export default authenticate;
// the get - user/:id route
const getUser = async (
req: GetUser,
res: IResponse
): Promise<IResponse> => {
try {
const user = await UserControler.findUserById(req.params.id);
return res.status(200).json({ user });
} catch (err) {
throw new Error("error.unknown");
}
};
export default getUser;
How to fix this?
In your authenticate functions, you forgot to add an else statement to if (mycookie).
Unauthorized will therefore always be sent, which is clearly the reason why you get unauthorized with a good cookie, and then as unauthorized as already been sent, the error you describe will be throw when getUser tries to sent the http response.
Wrap return res.sendStatus(401); in an else statement and it should work fine.

Authorization middleware JWT confusion

I have a suspicion about the relative security of some authentication middleware code I came across in a course im enrolled in.
So I used postman to send a request to a protected route(see route code below) and found that I was able retrieve an order for one user with a token generated for another user.
const protected = asyncHandler(async (req, res, next) => {
let token;
if (
req.headers.authorization &&
req.headers.authorization.startsWith("Bearer")
) {
try {
token = req.headers.authorization.split(" ")[1];
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = await User.findById(decoded.id).select("-password");
next();
} catch (error) {
console.error(error);
res.status(401);
throw new Error("Not authorized, token failed");
}
}
if (!token) {
res.status(401);
throw new Error("Not authorized, No token found");
}
});
export protected
It seems evident to me that this middleware code will only verify if a user from decoded token exists in the DB and but will not limit access to resources based on the user/token.
import {addOrderItems, getOrderbyId} from "../controllers/orderController.js";
import { protected } from "../middleware/authMiddleware.js";
const router = express.Router();
router.route("/").post(protected, addOrderItems);
router.route("/:id").get(protected, getOrderbyId);
//:id is the order id
However, when testing another protected route for updating a user's profile info, I receive an error when using wrong token.
Was hoping for some clarification
jwt.verify will only verify that the given token is generated by the server or not. It doesn't care which user send this token.
For your protected middleware, it just check if the request is authorized. If so, the request will pass to the controller.
As for the updating route. It probably be something like this:
// route
router.route("/:userId", protected, updateController)
const updateController = (req, res) => {
const user = req.user; // this is the one generated by protected middleware
const reqUserId = req.params.userId; // this is the one send by request
if (user.id !== reqUserId) {
// if two ids are not the same, it means someone is trying
// to update the profile with the wrong token
res.status(401);
}
// update profile in database
}

Node.js: Custom header returns undefined when use 'req.header'

I'm fairly new to Express and NodeJS. I'm having trouble accessing my custom created header named auth-token when trying to verify the existing of said user first before allowing them to do any CRUD functionality in the system. It just returned 'undefined' instead of the token I placed in it.
So below is where I created my custom header named auth-token in my home GET router.
// Home GET Router
router.get('/', verifyUser, async (req, res) => {
// get user data by id
const dbData = await All.findById({ _id: req.user._id })
// store token passed as query 'tkn' in 'token' var
const token = req.query.tkn
// create custom header & render 'index.ejs' or homepage
res
.header('auth-token', token)
.render('index', { data: dbData.data })
}
I successfully able to create the custom header auth-token with no problem as shown below in my index or home page:
Right now, I'm trying to save new data inserted by user in the home page by using Home POST Router as shown below. But it will check first whether the user has the token or not using verifyUser1st function:
// Home POST Router
router.post('/', verifyUser1st, async (req, res) => {
// save new data code here...
}
And this is my verifyUser1st function:
function verifyUser1st(req, res, next) {
// get token from header
const token = req.header('auth-token') // this will return undefined
// if have, then allow/continue next(). If don't have, then return error message
if(!token) return res.status(401).json({ message: 'Accessed Denied!' }) // I got this error since token = undefined
try {
// verify the exist token
const varified = jwt.verify(token, process.env.TOKEN_4LOGINUSER)
req.user = varified
next()
} catch(err) {
res.status(400).json({ message: 'Invalid token!' })
}
}
But unfortunately it returns Accessed Denied since the token is undefined.
Should the auth-token be in Request Headers section (in blue circle image above) instead of Response Header section (in red circle image above) in order for it to work?
If yes, then how can I do that? If not, then can you help enlighten me of what things or topics should I learn first in order for me to make this work since I'm kinda new to this HTTP, Express and NodeJS environment?
to answer your question briefly - if you want to pass the auth token in the header then it should be passed in the request header.
However, if you want some middleware to check a token value that you can use later on in the processing chain, then just set it as a custom property on the req object and access it from there. There is no reason to try to jam something into the headers and then parse it out again later.
const auth = (req, res, next) => {
let { token } = req.body;
try {
console.log(token)
if (!token)
return res.status(401).json({ msg: 'no authentication token, authorisation denied' })
const verified = jwt.verify(token, process.env.JWT_KEY);
if (!verified)
return res.status(401).json({ msg: 'no authentication token, authorisation denied' })
req.user = verified.id;
next();
}
catch (err) {
res.status(500).json({ error: err.message });
}
}
So instead of sending the token in a Header we can either grab it from somewhere in the state, or in your case from the local storage.
In the front end we would do something like const token = localStorage.getItem("auth-token");
And then we would pass this token to the API request.

Resources