I am using axios, express validator and bcryptjs for the function of letting a user change their password in their account area. The field I am checking against is the one where the user needs to type in their existing password. It then hashes their input and checks it against the database hashed password. When doing other validation I am able to get the error message response back via axios but for this will not:
body("currentPass")
.custom((value, { req }) => {
return User.findOne({ _id: req.user }).then(userDoc => {
bcrypt.compare(value, userDoc.password).then(domatch => {
if (!domatch) {
return Promise.reject("no match");
}
});
});
})
The error I get is:
UnhandledPromiseRejectionWarning: no match Unhandled promise
rejection. This error originated either by throwing inside of an async
function without a catch block, or by rejecting a promise which was
not handled with .catch(). (rejection id: 3)
If I add a catch block then when validation fails the catch block fires instead of the Promise.reject
body("currentPass")
.custom((value, { req }) => {
return User.findOne({ _id: req.user })
.then(userDoc => {
bcrypt.compare(value, userDoc.password)
.then(domatch => {
if (!domatch) {
return Promise.reject("no match");
}
})
.catch(err => {
console.log('catch block error');
})
});
})
Related
I have a back-end using firebase-admin and express to allow post requests from the client to the server to make changes to the firestore I have that contains stuff like user data (this is a test and not a real product). I want to check if a document already exists so a user cannot register with that username again. I have first seen instances of doc.exists but that returns undefined for me and I looked into the documentation and found doc.empty which is said to check if a document is empty. I tried it but it returned a promise rejection error. If I changed that line to .exists or to something else, that goes away so I have narrowed down the issue to that line.
index.js (backend)
app.post("/registeruser", function (req, res) {
res.setHeader("Content-Type", "application/json");
try {
const username = req.body.username;
const password = req.body.password;
const passwordEncrypted = HmacSHA1(password, JSON.parse(fs.readFileSync("./keys.json"))["passwordEncryptKey"]).toString();
// console.log(username, password, passwordEncrypted);
try {
firestore.collection("users").get(username).then(function (data) {
if (data.empty == false) {
throw [true, "Already registered user!"];
}
}).catch(function (error) {
throw [true, error];
});
if (username == "") {
firestore.collection("users").add({
username: v4(),
passwordhash: passwordEncrypted,
email: "example#gmail.com",
}).then(function () {
return res.status(200).send(JSON.stringify({
error: false,
message: "Successfully registered user!",
}))
}).catch(function (error) {
throw [true, error];
});
}
else {
firestore.collection("users").doc(username).set({
username: username,
passwordhash: passwordEncrypted,
email: "example#gmail.com",
}).then(function () {
return res.status(200).send(JSON.stringify({
error: false,
message: "Successfully registered user!",
}));
}).catch(function (error) {
throw [true, error];
});
}
}
catch (error) {
throw [true, error];
}
}
catch (error) {
console.log(error);
const [isError, errorMessage] = error;
return res.status(404).send(JSON.stringify({
error: isError,
message: errorMessage,
}));
}
});
Terminal Output
(node:29448) UnhandledPromiseRejectionWarning: [object Array]
(node:29448) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:29448) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
You have multiple concurrent promise chains, and some of those can fail independently. You need to consolidate all your logic into one promise chain.
return firestore.collection("users").get(username)
.then((data) => {
if (data.empty == false) {
throw [true, "Already registered user!"];
}
})
.then(() => {
if (username == '') {
return firestore.collection("users").add({/* Your data */});
}
return firestore.collection("users").doc(username).set({/* Your data */});
})
.then(() => {
return res.status(200);
})
.catch((err) => {
return res.status(500);
});
You can also try using async/await which will significantly simplify logic like this.
I'm doing a RESTful API for a vuejs frontend with authentication and data saving by account.
I get this error when trying to log a user in.
Basically my frontend will send a request with a body containing a username and password.
Here's the log in middleware in express :
exports.post_LogIn = (req, res, next) => {
User.findOne({ username: req.body.username })
.then(foundUser => {
if (!foundUser) {
console.log("no user");
const error = new Error("No user with that username");
error.statusCode = 400;
error.tosend = "No user with that username";
throw error;
} else {
return bcrypt.compare(req.body.password, foundUser.password);
}
})
.then(isEqual => {
if (!isEqual) {
console.log("wrong password");
const error = new Error("Passwords don't match");
error.statusCode = 400;
error.tosend = "Passwords don't match";
throw error;
}
console.log("logged in");
const token = jwt.sign(
{
username: foundUser.username
},
"secretpassword",
{ expiresIn: "24h" }
);
console.log(token);
res.status(200).json({ username: foundUser.username });
})
.catch(err => {
res.status(err.statusCode).json({ message: err.tosend });
});
};
So basically when there's a problem with the credentials (either wrong username or password) I wanna send a response to the front end with a message saying what was wrong, and let the front end deal with it.
This works just fine and as expected.
However when I send valid credentials, my console.log statement goes through, but the token and response part gives me this error
(node:14515) UnhandledPromiseRejectionWarning: RangeError
[ERR_HTTP_INVALID_STATUS_CODE]: Invalid status code: undefined
at ServerResponse.writeHead (_http_server.js:242:11)
at ServerResponse._implicitHeader (_http_server.js:233:8)
at write_ (_http_outgoing.js:579:9)
at ServerResponse.end (_http_outgoing.js:689:5)
at ServerResponse.send (/home/martin/dev/projets/trader-backend/node_modules/express/lib/response.js:221:10)
at ServerResponse.json (/home/martin/dev/projets/trader-backend/node_modules/express/lib/response.js:267:15)
at /home/martin/dev/projets/trader-backend/controllers/UserController.js:97:34
(node:14515) UnhandledPromiseRejectionWarning: Unhandled promise
rejection. This error originated either by throwing inside of an async
function without a catch block, or by rejecting a promise which was
not handled with .catch(). (rejection id: 1) (node:14515) [DEP0018]
DeprecationWarning: Unhandled promise rejections are deprecated. In
the future, promise rejections that are not handled will terminate the
Node.js process with a non-zero exit code.
I tried commenting out the token part, or commenting the response and console logging the token, and I still get the same error.
However if I comment out the token and use res.send instead of res.json it seems to be working fine and my front end gets the response.
Any idea what might be causing this ? I'm kind of lost here
I've rewritten the following function about 6 different times and am still getting a "Cannot set headers after they are sent to the client" error. I have found several posts on the topic of promises but still cant figure it out:
Error: Can't set headers after they are sent to the client
Cannot set headers after they are sent to the client
Error: Setting header after it is sent - Help me understand why?
The following function is for a forum and is triggered when a comment is submitted. It check to see that the forum post exists, than if a parent comment exists (in the case it is a subcomment). I am using firestore.
index.js
const functions = require('firebase-functions');
const app = require('express')();
const {postOneForumComment,
} = require('./handlers/forumPosts');
app.post('/forumPost/:forumPostId/:parentId/comment', FBAuth, postOneForumComment);
exports.api = functions.https.onRequest(app);
forumPosts.js
// submit a new comment
exports.postOneForumComment = (req, res) => {
if (req.body.body.trim() === '')
return res.status(400).json({ comment: 'Must not be empty' });
const newComment = {
body: req.body.body,
forumPostId: req.params.forumPostId,
parentId: req.params.parentId
};
db.doc(`/forumPosts/${req.params.forumPostId}`) //check to see if the post exists
.get()
.then((doc) => {
if (!doc.exists) {
return res.status(404).json({ error: 'Post not found' });
}
else if (req.params.forumPostId !== req.params.parentId) { //check to see if the comment is a subcomment
return db.doc(`/forumComments/${req.params.parentId}`) //check to see if the parent comment exists
.get();
}
return "TopLevelComment";
})
.then((data) => {
if (data === 'TopLevelComment' || data.exists) {
return db.collection('forumComments').add(newComment); //post the comment to the database
}
return res.status(500).json({ error: 'Comment not found' });
})
.then(() => {
res.json(newComment);
})
.catch((err) => {
console.log(err.message);
res.status(500).json({ error: 'somethign went wrong' });
});
};
ERROR:
(node:29820) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting
a promise which was not handled with .catch(). (rejection id: 1)
(node:29820) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with
a non-zero exit code.
There are two ways of using promises. Either you use the then/catch callbacks or you can use async/await to allow you to write them synchronously.
then/catch method
// Some code before promise
somePromise.then(() => {
// Some code after promise action is successful
}).catch(err => {
// Some code if promise action failed
})
// Some code after promise definition you think should run after the above code
// THIS IS WHAT IS HAPPENING WITH YOUR CODE
async/await method
// Some code before promise
await somePromise;
// Some code after promise action is successful
The latter approach was introduces to avoid the callback hell problem and it seems that's where your error is arising from.
When using callback callbacks you must make sure that nothing is defined after the promise definition else it will run before the promise resolves (Which is counter-intuitive since placing code B after code B should make A run before B)
Your error is because your callbacks are probably running AFTER the response has been sent and express does not allow you to send multiple responses for a request.
You should make sure that where ever res.send or res.json is being called exist within the callback.
This article should help you understand promises much better...
Hope this helps...
For anyone who stumbles upon this here is a working solution using Promise.all to make sure all promises are fulfilled before moving on. It is not the prettiest function and I plan on going back and turning it into an async/await ordeal per #kwame and #Ajay's recommendation... but for now it works.
// post a comment
// TODO: turn into async await function
exports.postOneForumComment = (req, res) => {
if (req.body.body.trim() === '') return res.status(400).json({ comment: 'Must not be empty' });
const newComment = {
body: req.body.body,
createdAt: new Date().toISOString(),
forumPostId: req.params.forumPostId,
parentId: req.params.parentId,
username: req.user.username,
userImage: req.user.imageUrl,
likeCount: 0
};
const parentPost =
db.doc(`/forumPosts/${req.params.forumPostId}`).get()
.then((doc) => {
if (!doc.exists) {
res.status(404).json({ error: 'Post not found' });
return false;
}
return true;
})
.catch((err) => {res.status(500).json({ error: 'something went wrong while checking the post' });});
const parentComment =
req.params.forumPostId === req.params.parentId ? true :
db.doc(`/forumComments/${req.params.parentId}`).get()
.then((doc) => {
if (!doc.exists) {
res.status(404).json({ error: 'Comment not found' });
return false;
}
if (doc.forumPostId !== req.params.forumPostId) {
res.status(404).json({ error: 'Comment is not affiliated with this post' });
return false;
}
return true;
})
.catch((err) => {res.status(500).json({ error: 'something went wrong while checking the comment' });});
Promise.all([parentPost, parentComment])
.then((values) => {
if (values[0] && values[1]) {
return db.collection('forumComments')
.add(newComment)
.then(() => {
res.json(newComment);
});
}
return console.log("there was an error");
})
.catch((err) => {
res.status(500).json({ error: 'somethign went wrong with the submission' });
});
};
When trying to authenticate users with Mongoose I get the following warning in my console:
(node:20114) UnhandledPromiseRejectionWarning: undefined
(node:20114) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:20114) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Trying to trace the stack doesn't result in anything, all I get is undefined. Similar questions exist here on Stackoverflow, but they don't apply to my situation. Any idea what this could cause?
My route controller is calling the findByCredentials function inside the Mongoose model:
Controller
static login(req, res) {
User.findByCredentials(req.body.email, req.body.password)
.then(user => {
return user.generateAuthToken().then(token => {
res.header("x-auth", token).json(user);
});
})
.catch(error => {
res.status(400).json({ message: "Invalid credentials." });
});
}
Model
userSchema.statics.findByCredentials = function(email, password) {
return this.findOne({ email }).then(user => {
if (!user) {
Promise.reject();
}
return new Promise((resolve, reject) => {
bcrypt.compare(password, user.password, (err, res) => {
res ? resolve(user) : reject();
});
});
});
};
The error undefined is coming from your Promise.reject() you are not passing any error message to it, so you are literally throwing undefined.
It is not caught in the catch in login, because you are not returning it from your findByCredentials method.
Solution:
userSchema.statics.findByCredentials = function(email, password) {
return this.findOne({ email }).then(user => {
if (!user) {
return Promise.reject('User not available');
}
return new Promise((resolve, reject) => {
bcrypt.compare(password, user.password, (err, res) => {
res ? resolve(user) : reject();
});
});
});
};
I'm using a Firebase function that detects when a Stripe Token is added to a user collection, which then creates a Stripe User and creates a Subscription for that user.
The problem I'm having is that the below code has an error in it, and whilst I need to figure out what that error is, the Promise Chain doesn't seem to catch the actual error.
All that logs out is Function execution took 4375 ms, finished with status: 'connection error'
Does anyone know why this is happening. The only way I can get it to log catch the error is to nest the Promises.
exports.createStripeUser = functions.firestore
// A Stripe Token is created in a user, therefore they have agreed to pay
// Create a User in Stripe, and then attach the subscription to the Stripe customer
.document('users/{userId}/stripe/{stripeCollectionId}')
.onCreate((snap, context) => {
const stripeToken = snap.data().stripeToken;
let customer;
return admin.auth().getUser(`${context.params.userId}`)
.then(userObj => {
const user = userObj.toJSON();
return stripe.customers.create({
email: user.email,
source: stripeToken
})
})
.then(cust => {
customer = cust;
return db.collection('users').doc(context.params.userId).collection('plan').doc(context.params.userId).get()
})
.then(plan => {
return stripe.subscriptions.create({
customer: customer.id,
items: [{plan: plan.data().plan}]
})
})
.catch(e => {
console.log("ERRR", e)
throw new functions.https.HttpsError('unknown', e.message, e);
})
})
Wrap your block in a
try {
}
catch (e) {
// e holds the nested error message
}
block and pick up the error there.