There are a few vulnerabilities and repetitions in your code that I can help resolve. Here's my approach:
Your application does not follow the "Thin controller fat model" approach. To resolve this issue the password hashing inside singin middleware can be applied on the model with the help of pre save middleware.
For abstraction moving the jwt creation into a separate function will be a good idea at the top level of your controller.
Like this : const token = signToken(user._id);
Also, there is repetitive use of try catch block, which can be replace by a catchAsync wrapper method with this method you only need to call the global error handler for custom error handling and for other general case catchAsync will work as your catch block
exports.signIn = catchAsync(async function (req, res, next) {}
There is another major problem with security i.e. the data sanitization problem, anyone with an existing email address can access into your application with the help of NoSQL query injection to prevent this i can implement mongo sanitize and xss-clean.
There are a few vulnerabilities and repetitions in your code that I can help resolve. Here's my approach:
const token = signToken(user._id);
exports.signIn = catchAsync(async function (req, res, next) {}