Task of code : Is to delete document from DB using JWT token
error : sending 500 internal error
code of Auth.js
const auth=async(req,res,next)=>{
try{
const token=req.header('Authorization').replace('Bearer ','');
const decoded=jwt.verify(token,'helloworld');
const user=await User.findOne({_id:decoded._id,'tokens.token':token});
if(!user){
throw new Error();
}
// console.log(token);
req.token=token;
req.user=user;
next();
}
catch(err){
// console.log(token);
res.status(401).send({error:"please authenticate"});
}
}
module.exports=auth;
Code To delete document (API endpoint)
router.delete('/users/me',auth,async(req,res)=>{
try{
await req.user.remove();
res.status(201).send(req.user);
}
catch(err){
console.log(err);
console.log(req.user);
res.status(500).send();
}
})
The Problem is even if I am sending incorrect JWT token it should give me {error : please authenticate} but I am not getting this error too instead I am getting 500 internal error and same error when sending correct JWT .
Even I am printing error in console ,its not showing error in console
In your API endpoint, you have to call your collection name then you need to remove with query/params id.
If you are using mongoose Like this and send id from frontend as query/params,
Model.remove({ _id: req.body.id }, function(err) {
if (!err) {
message.type = 'notification!';
} else {
message.type = 'error';
}
});
or
router.delete('/users/me/:id', auth, async(req,res) => {
try {
await Model.deleteOne({ _id: req.params.id})
res.status(201).send(req.user);
} catch(err) {
console.log(err);
res.status(500).send();
}
});
I hope if you follow this way you can solve this problem.
This is the wrong way to remove await req.user.remove(); Because, when code executes it will don't know which collection of data needs to remove.
Related
I am trying to fetch data from one MongoDB collection and with that result, I am passing that value into another function to get the data from GridFS files for same id.
router.get('/employee/:id', async(req,res) =>{
let gID = req.params.id;
var getUser = await User.find({id:gID});
//console.log(res);
console.log(getUser);
if(getUser){
try{
gfs.files.find({objectID:getUser.id}).toArray(async (err, files) => {
if (!files || files.length === 0) {
res.send({
message: "No User Found",
});
} else {
res.send({
message: "data fetched",
data: getUser,
image: files
});
}
});
}catch(err){
res.send({
message:err.code,
data:err
});
}
}else{
res.send({
message:"error in getting the data",
});
}
})
I tried of this but I am not able to get the expected result, I am getting the user information from getUser but I am not able to fetch the file tagged with that user from GridFS. Can anyone tell me where I am lagging and how could I correct this.
I am trying to use passport-jwt strategy for authentication.
Here is my code :-
router.post('/register', async (req, res) => {
const { username, email } = req.body;
try {
const user = await User.findOne({ username });
if (user) {
throw new Error('User with same username already exists !!');
}
const newUser = new User({
username,
email
})
const salt = await bcrypt.genSalt(10);
newUser.password = await bcrypt.hash(req.body.password, salt);
const savedUser = await newUser.save();
res.json({
status: 200,
'Content-Type': 'Application/Json',
'message': `You have successfully regirstered yourself, ${savedUser.username}!`
})
} catch (err) {
err.statusCode = 500;
console.log(err.message);
res.header({
status: '200',
'Content-Type': 'Application/Json',
});
res.json(err);
}
});
Now this route is working just fine, it's doing all the things till now. The only problem is, When i find an existing user, I want to throw a new error with a custom message. Javascript has this Error class which I can use to throw these errors.
The problem occurs when it catches error. When I console.log(err.message), I can see my custom message perfectly. But the err object that I am returning in response via res.json(err) does not have any message but only statusCode.
I want to know why this is happening and what's the solution for this ? Right now, I am doing this by doing something like this :-
res.json({
statusCode: 500,
message : err.message
});
But I would like to return the err object with both statusCode and message fields populated.
You can create your own Error class which can take more than one parameter in the constructor. This class has to extend base Error JavaScript class. For example:
class MyCustomError extends Error {
constructor(msg, statusCode) {
super(msg);
this.statusCode = statusCode;
this.name = MyCustomError.name;
}
}
function throwCustomError() {
throw new MyCustomError('Some custom message', 404);
}
try {
throwCustomError();
} catch (error) {
console.log(error.message);
console.log(error.statusCode);
console.dir(error);
}
Remember that you have to call super on the beginning of the constructor if you are extending another class
You are passing the error object to the json method of the response object. But this only takes JSON parseable string as a parameter. What you can do is use -
res.json(JSON.stringify(err))
and at the place where you are using this response, you need to parse this string as JSON and then convert it into an Error object. You can use the following syntax assuming your front-end also uses javascript
err = new Error(JSON.parse(response.data))
Replace the entire catch block with the following line of code.
res.status(500).json({
message: err.message
})
From the documentation of res.json() : This method sends a response (with the correct content-type) that is the parameter converted to a JSON string using JSON.stringify().
Now running JSON.stringify(new Error('custom msg')) we get "{}"
I have a login route but whenever it's giving me a typeError not a function. I have checked the code too many times but still can't get why it's giving me this error:
Here's the code:
router.post("/login", async (req, res) => {
try {
const { email, password } = req.body;
if (!email || !password) {
return res.status(400).send("Please provide an email and password");
}
const user = await User.find({ email });
if (!user) return res.status(401).send("User not found");
const isMatch = await user.checkHashedPassword(password);
if (!isMatch) return res.status(401).send("Invalid credentials");
sendTokenResponse(user, 200, res);
} catch (ex) {
console.log(ex);
}
});
The error I get is that user.checkHashedPassword is not a function.
Here's the checkHashedPassword method in userSchema:
userSchema.methods.checkHashedPassword = async function (enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password);
};
Here's the complete error that I get:
TypeError: user.checkHashedPassword is not a function
at D:\pythonprogs\todoapp\routes\users.js:46:32
at processTicksAndRejections (internal/process/task_queues.js:93:5)
I have checked the spellings and everything even changed the function name to see if it works but don't know why it's giving this error. Please help
problem is you are using find() method instead of findOne().
find() returns array of collections not object. try this:
const isMatch = await user[0].checkHashedPassword(password)
How can I give a function that gets called in one file a parameter to use in another file. I am trying to create a RestApi with node.js and express.js. The file entry.routes.js contains the following code:
app.get("/energy/api/ActualTotalLoad/:AreaName/:Resolution/date/:Year-:Month-:Day", entry.findTwo);
However in this link there are some parameters inside the header as a token. I decode the token with the following code:
app.use(
jwt({
secret: privateKey,
credentialsRequired: false,
getToken: function fromHeaderOrQuerystring (req) {
if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
return req.headers.authorization.split(' ')[1];
} else if (req.query && req.query.token) {
return req.query.token;
}
return null;
}
}));
In the file entry.controllers.js the code is the following:
exports.findTwo =async function (req, res) {
console.log(req);
const correct =await getResults2(req.user.apikey);
console.log(correct);
if (correct==1){
Entry.findByPars(req.params.AreaName,req.params.Resolution,req.params.Year,req.params.Month,req.params.Day, (err, data) => {
if (err) {
if (err.kind === "not_found") {
res.status(403).send({
message: `No data`
});
} else {
res.status(400).send({
message: "Bad Request"
});
}
} else{
if(req.query.format!==undefined && req.query.format=="csv"){
const {Parser} = require('json2csv');
const json2csvParser=new Parser();
const csv=json2csvParser.parse(data);
res.send(csv);
}
else if (req.query.format==undefined || req.query.format=="json"){
res.send(data);
}
else {
res.status(400).send({
message: "Bad Request"
});
}
}
});
}
else if (correct==2) res.status(402).send({ message: "Out of Quota"});
else res.status(401).send({message: "Not authorized"});
}
In the last code when I do the following command
const correct =await getResults2(req.user.apikey);
where I try to access req.user.apikey . This parameter is available in other app.post commands in the entry.routes file but is not accessible in the other file. It passes as undefined.
For example in the entry.routes file the following code works perfectly:
app.post("/energy/api/Logout",function(req,res){
console.log(req.user.apikey);
var jwt=require('jsonwebtoken');
sql.query(`SELECT apikey FROM users WHERE apikey=?`,[req.user.apikey],(err,res1) => {
if (err) {
console.log("error: ", err);
result(err, null);
return;
}
else if (res1.length){
res.status(200).send(" ");
}
else res.status(400).send("Bad Request");
});
});
Why doesn't the parameter req.user.apikey get passed in the entry.findTwo function on the file entry.controllers?
💡 The One reason why you can only get the value from req.user.apikey inentry.controller when you send request to this endpoint:
app.get("/energy/api/ActualTotalLoad/:AreaName/:Resolution/date/:Year-:Month-:Day", entry.findTwo);
It's because you add authorization to the header, before you send the request.
👨🏫 It should be noted: this req.user.apikey is comming from the access token that you provided in the header before sending the request.
Because you're using express-jwt, so, every token that you include in the header will be decoded directly and added to the req parameter, that's why you can access req.user.apikey.
So, if your question ❓ is why req.user.apikey can only be accessed inentry.controller, then the answer is because on your other route, for example:
When you call this route: energy/fire/logout
You didn't add authorization there.
💡 So, To be able to use req.user.apikey on your other route, make sure in your header you have added authoriazation.
I hope it can help you 🙏.
I am trying to refactor some inherited code. In every endpoint was the same validation code. I want to pull it out into it's own method. I am new to promises, but I think that is what I want to use. The issues is prom seems to be resolved at the User.findOne call and exits with an undefined prom.promise.
cheers
bob
function validateUser(req) {
var prom = q.defer();
var token = getToken(req.headers);
if (token) {
console.log("have a token")
var decoded = jwt.decode(token, config.secret);
console.log("now going to look for the user")
//Problem exit is on next line
User.findOne({
name: decoded.name
}, function (err, user) {
if (err) throw err;
prom.reject(err);
if (!user) {
console.log("no user found")
prom.reject("Authentication failed. User not found.")
} else {
console.log("user found returning true")
prom.resolve(true);
}
})
} else {
console.log("no token found")
prom.reject("No token provided.")
}
return prom.promise;
}
why you are using promises when mongoose itself returns it.
function validateUser(req, callback) {
var token = getToken(req.headers);
if (token) {
var decoded = jwt.decode(token, config.secret);
User.findOne({
name: decoded.name
}, function (err, user) {
if (err) throw err;
callback(err);
if (!user) {
callback("Authentication failed. User not found.")
} else {
console.log("user found returning true")
callback(null, {status:true, userData:user});
}
})
} else {
callback("No token provided.")
}
}
In above code,
if token is not found callback is returned with an error in the first attempt. if token is found then it is decoded in a row and if matched in DB if the result is an error then the callback is called with err parameter else if no user is found or empty match then a custom message is sent in callback error part. But in final is returned as success with status and userData.