How to Fix Headers Already Sent to Client in NodeAPI with Angular - javascript

I am getting the following error on my node api, which is really just console logging the request at this point.
router.get('/booksByISBN', checkRole, async (req, res) => {
console.log(req.params)
return res.sendStatus(200);
});
node:internal/errors:484
ErrorCaptureStackTrace(err);
^
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
I believe the issue is because of pre-flight CORS data, but no clue how to fix it.
There is one API call in my Angular 15 application, but upon inspection of the Network tab, I see two api calls are actually being made to my endpoint.
I understand this is because of the CORS options request, but I don't know how to fix it to let the API go through.
CheckRole function
var checkRole = async function CheckRoleAuth (req, res, next) {
try {
const token = req.headers.authorization.split(' ')[1];
const decodedToken = jwt.verify(token, envs.jwtSecret);
await User.findById(decodedToken.userId)
.then(foundUser => {
if (foundUser) {
if (foundUser.role != null || foundUser.role != '') {
if (foundUser.role.includes('Admin'))
{
req.userData = {
email: decodedToken.email,
id: decodedToken.id
};
next();
} else {
return res.sendStatus(401);
}
} else {
return res.sendStatus(401);
}
}
})
.catch(err => {
return res.sendStatus(401);
});
} catch (error) {
return res.sendStatus(401);
}
}

You are combining async/await with then/catch in your checkRole middlware, so probably both your checkRole middleware and your endpoint handler try to send back the response.
Refactor your checkRole middleware like this:
const checkRole = async function CheckRoleAuth(req, res, next) {
try {
const token = req.headers.authorization.split(' ')[1];
const decodedToken = jwt.verify(token, envs.jwtSecret);
const user = await User.findById(decodedToken.userId).lean();
if (!user) return res.sendStatus(401);
if (!user?.role?.includes('Admin')) return res.sendStatus(403);
req.userData = { email: decodedToken.email, id: decodedToken.id };
next();
} catch (error) {
return res.sendStatus(401);
}
};

Related

JWT Authorization is failing for all endpoints

So I am creating a social media application.
I used JWT token for verification on all endpoints. It's giving me custom error of "You are not authorized, Error 401"
For example: Create post is not working:
This is my code for JWT
const jwt = require("jsonwebtoken")
const { createError } = require ("../utils/error.js")
const verifyToken = (req, res,next) => {
const token = req.cookies.access_token
if(!token) {
return next(createError(401,"You are not authenticated!"))
}
jwt.verify(token, process.env.JWT_SECRET, (err,user) => {
if(err) return next(createError(401,"Token is not valid!"))
req.user = user
next()
}
)
}
const verifyUser = (req, res, next) => {
verifyToken(req,res, () => {
if(req.user.id === req.params.id || req.user.isAdmin) {
next()
} else {
return next(createError(402,"You are not authorized!"))
}
})
}
const verifyAdmin = (req, res, next) => {
verifyToken(req, res, next, () => {
if (req.user.isAdmin) {
next();
} else {
return next(createError(403, "You are not authorized!"));
}
});
};
module.exports = {verifyToken, verifyUser, verifyAdmin}
This is my createPost API:
const createPost = async (req, res) => {
const newPost = new Post(req.body);
try {
const savedPost = await newPost.save();
res.status(200).json(savedPost);
} catch (err) {
res.status(500).json(err);
}
}
Now, in my routes files, I have attached these functions with every endpoints.
For example: In my post.js (route file)
//create a post
router.post("/", verifyUser, createPost);
When I try to access it, this is the result
But, when I remove this verify User function from my route file, it works okay.
I have tried to re-login (to generate new cookie) and then try to do this but its still giving me error.
What can be the reason?
P.S: my api/index.js file https://codepaste.xyz/posts/JNhIr9W6zNnN26CH9xWT
After debugging, I found out that req.params.id is undefined in posts routes.
It seems to work for user endpoints since it contains req.params.id
const verifyUser = (req, res, next) => {
verifyToken(req,res, () => {
if(req.user.id === req.params.id || req.user.isAdmin) {
next()
} else {
return next(createError(402,"You are not authorized!"))
}
})
}
So I just replaced === with || and its working. (but its not right)
if(req.user.id || req.params.id || req.user.isAdmin) {
Can anyone tell me the how can I truly apply validation here since in my posts routes i dont have user id in params

Node Js pass the role as a string to the JWT verification function

the JWT verification function accepts the req, res and next as its params. I need to pass an additional string 'Admin' so that only admin users may access this API
My jwtVerification.js code:
module.exports = async function (req, res, next) { //I need to be able to add role to this call
try {
const token = req.header("Authorization");
if (!token) return res.status(401).send('Invalid access token.');
const _token = token.substring(7, token.length);
const decoded = jwt.verify(_token, process.env.JWT_PRIVATE_KEY)
const user = await prisma.user.findFirst({ where: { id: decoded.id } });
if (!user) return res.status(401).send('Invalid access token.');
//I need to be able to read the role so that I can do the following verifications
//if(!role) next();
//else{
// if(user.role !== role || decode.role !== role) return res.status(403).send('Forbidden!')
// else next();
//}
next();
} catch (error) {
res.status(401).send(error.message);
}
};
finally, the API call itself:
//use verifyJWT('Admin') for example
router.post('/test', verifyJWT, async (req, res) => {
res.send('hi');
})
You cat use some thing like this:
module.exports = function (myParam) => {
return async function (req, res, next) {
//use myParam here
try {
const token = req.header("Authorization");
if (!token) return res.status(401).send('Invalid access token.');
const _token = token.substring(7, token.length);
const decoded = jwt.verify(_token, process.env.JWT_PRIVATE_KEY)
const user = await prisma.user.findFirst({ where: { id: decoded.id } });
if (!user) return res.status(401).send('Invalid access token.');
//I need to be able to read the role so that I can do the following verifications
//if(!role) next();
//else{
// if(user.role !== role || decode.role !== role) return res.status(403).send('Forbidden!')
//}
next();
} catch (error) {
res.status(401).send(error.message);
}
}
};
And after that use the middleware this way:
router.post('/test', verifyJWT(someParam), async (req, res) => {
res.send('hi');
})

How to fix router.delete() which is not working - Express.js?

I am trying to run a delete request but it is not working, I have used the exact same logic on another project exactly like it and it works there.
Here is the route file which includes the delete request as well as the post request that does indeed work
const express = require("express");
const router = express.Router();
const User = require("../models/users");
const cardSchema = require("../models/card");
//add card request
router.post("/:id/addcard", getUser, async (req, res) => {
try {
if (req.body != null) {
const newCard = new cardSchema({
name: req.body.name,
cardNumber: req.body.cardNumber,
ccv: req.body.ccv,
expiration: req.body.expiration,
});
res.user.cardInfo.push(newCard);
}
const updatedCardInfo = await res.user.save();
return res.status(200).json(updatedCardInfo);
} catch (error) {
return res.status(400).json({ message: error.message });
}
});
//delete card request
router.delete("/:id/deletecard", getUser, async (req, res) => {
if (req.body !== null) {
res.user.cardInfo.remove(req.body);
}
try {
const updatedUser = await res.user.save();
res.status(200).json(updatedUser);
} catch (error) {
res.status(400).json({ message: error.message });
}
});
//get user middleware
async function getUser(req, res, next) {
let user;
try {
user = await User.findById(req.params.id);
if (user == null) {
return res.status(404).json({ message: "Cannot find user" });
}
} catch (error) {
return res.status(500).json({ message: error.message });
}
res.user = user;
next();
}
module.exports = router;
I have triple checked that I am using the correct URL and passing in the correct information in the req.body. I recieved the users information after calling the delete request but just does not remove the card information. I have also checked in my database that it is 'cardInfo' so there is no spelling mistake there either.

Authorizarion on json-server with a JWT

I am trying to make a node app which is using typicode json-server, I want to add authorization to the app, where GET request is open to all public, but PUT, POST & DELETE request require a JWT token and only then they can proceed on the api.
I have tried to make a small app, but I am not able to figure out the next part of authorization and how to use middlewares on node, as I am a frontend developer.
Here is the app that I have written.
const jsonServer = require('json-server')
const app = jsonServer.create()
const router = jsonServer.router('db.json')
const middlewares = jsonServer.defaults()
const morgan = require('morgan');
const jwt = require('jsonwebtoken');
const config = require('./config');
const bodyParser = require('body-parser');
app.set('Secret', config.secret);
app.use(morgan('dev'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(middlewares)
app.use((req, res, next) => {
if (req.method === 'GET') {
console.log(req);
next();
} else {
if (req.path === '/login') {
getToken(req, res);
}
if (isAuthorized(req, res)) {
console.log("I am here");
next();
} else {
console.log("I am in error");
res.sendStatus(401)
}
}
})
app.use(router)
app.listen(3000, () => {
console.log('JSON Server is running on 3000')
})
function isAuthorized(req, res) {
console.log("sadasdasdasd");
var token = req.headers['access-token'];
console.log(token);
// decode token
if (token) {
console.log("Inside token");
jwt.verify(token, app.get('Secret'), (err, decoded) => {
console.log("Inside JWT fn");
if (err) {
console.log("Inside JWT fn err");
return res.json({ message: 'invalid token' });
} else {
console.log("Inside JWT fn success");
req.decoded = decoded;
return true;
}
});
} else {
// if there is no token
res.send({
message: 'No token provided.'
});
}
}
function getToken(req, res) {
if (req.body.username === "test") {
if (req.body.password === 123) {
const payload = {
check: true
};
var token = jwt.sign(payload, app.get('Secret'), {
expiresIn: 1440 // expires in 24 hours
});
res.json({
message: 'Authentication Successful ',
token: token
});
} else {
res.json({
error: 'Invalid Password',
});
}
} else {
res.json({
error: 'Please provide valid credentials',
});
}
}
You are doing in right way. But, have some issue in your isAuthorized middleware. In the middleware you have a asynchronous action (jwt.verify), then you can not use this function as a "helper function" as the official document of json-server (the function return boolean value).
Make isAuthorized become a middleware and you it like a middleware:
function isAuthorized(req, res, next) { // Pass 3 parmas to a express middleware
console.log("sadasdasdasd");
var token = req.headers['access-token'];
console.log(token);
// decode token
if (token) {
console.log("Inside token");
jwt.verify(token, app.get('Secret'), (err, decoded) => {
console.log("Inside JWT fn");
if (err) {
console.log("Inside JWT fn err");
return res
.status(401) // I think will be better if you throw http status is 401 to client
.json({ message: 'invalid token' });
} else {
console.log("Inside JWT fn success");
req.decoded = decoded;
return next(); // Only call "next" if everything is good, continue next jobs - handle secured requests
}
});
} else {
// if there is no token
return res
.status(401)
.send({
message: 'No token provided.'
});
}
}
Use the middleware
app.use((req, res, next) => {
if (req.method === 'GET') {
console.log(req);
next();
} else {
if (req.path === '/login') {
getToken(req, res);
}
isAuthorized(req, res, next); // Here
}
})

How to write middleware for graphql which will be call before every resolver

In every request I send token, and check it in express middleware
app.use(async (req, res, next) => {
const authorization = req.headers.authorization;
let token = null;
let user;
if (authorization) {
try {
token = jwt.verify(authorization, config.secret);
} catch (e) {
// dont work
throw new GraphQLError({ message: 'token damaged' });
}
if (token) {
const { _id } = token;
user = await User.findOne({ _id });
}
if (user) {
req.user = user;
}
}
next();
});
Token can be damaged, and I do the check:
try {
token = jwt.verify(authorization, config.secret);
} catch (e) {
throw new GraphQLError({ message: 'token damaged' });
}
So I need to send to client application Express Error, but it dont work, as expected,
are there any options to create graphql middlewares which take request arguments before calling every resolver? Now if I want throw error of damaged token I need write check in every resolver?
You can simply respond and return, without calling the next middleware:
try {
token = jwt.verify(authorization, config.secret);
} catch (e) {
res.statusCode = 401;
return res.end('{"errors": [{"message": "token damaged"}]}');
}

Categories

Resources