I've been trying to generate a token to reset a user's password and send the user an email with the link to use that token.
This is the function forgotPassword defined in the class authController:
//forgot password => /api/v1/password/forgot
exports.forgotPassword = catchAsynErrors(async(req, res, next) => {
const user = await User.findOne({ email: req.body.email });
if(!user){
return next(new ErrorHandler('User not found with this email', 404));
}
//get reset token
const resetToken = user.getResetPasswordToken();
await user.save({validateBeforeSave: false});
// create reset password url
const resetUrl = `${req.protocol}://${req.get('host')}/api/v1/password/reset/${resetToken}`;
const message = `Your password reset token:\n\n${resetUrl}\n\n If you have not requested this email, please ignore it.`
try {
await sendEmail({
email: user.email,
subject: 'ShopIt password recovery',
message
})
res.status(200).json({
success: true,
message: `Email sent to: ${user.email}`
})
} catch (error) {
user.resetPasswordToken = undefined;
user.resetPasswordExpire = undefined;
await user.save({validateBeforeSave: false});
return next(new ErrorHandler(error.message, 500))
}
})
Then, the function getResetPasswordToken defined in the model class user:
// generate password reset token
userSchema.methods.getResetPasswordToken = function() {
//generate token
const resetToken = crypto.randomBytes(20).toString('hex');
//hash and set to resetPasswordToken
this.resetPasswordToken = crypto.createHash('sha256').update(resetToken).digest('hex')
// set token expiration date
this.resetPasswordExpire = Date.now() + 30 * 60 * 1000
return resetToken
}
Upon sending a POST request via Postman to the URL http://localhost:4000/api/v1/password/forgot with the following JSON body,
{
"email": "example#email.com"
}
I get this response:
{
"success": false,
error": {
"statusCode": 500
},
"errMessage": "getaddrinfo ENOTFOUND smpt.mailtrap.io",
"stack": "Error: getaddrinfo ENOTFOUND smpt.mailtrap.io\n at D:\\pruebas de programaciĆ³n\\Proyectazo\\backend\\controllers\\authController.js:93:21\n at processTicksAndRejections (node:internal/process/task_queues:96:5)"
}
I have seen suggestions that this could be a DNS error, but I have already tried manually setting up an IPv4 address like 192.168.1.45 and my DNS servers set to 1.1.1.1 and 1.0.0.1, and the internet worked normally but I kept getting the same error, so I set my network configuration back to DHCP. I also tried emailing both real and fake email addresses and it made no difference.
Any ideas?
Related
I have started a new email project. It's very challenging to me because first time I am working with own custom email project. I am using node.js for creating REST full API. Using imap-simple module for accessing Internet message access. Everything is working like send email, receive email, add flag etc from our own server.
But problem is when trying to move an email from Inbox to Drafts folder it's giving an error. Can you please see my code snippet below and guide me for the right way.
const imaps = require('imap-simple');
module.exports.moveEmail = async (req, res)=>{
let user = req.body;
let UID = req.params.uid
try{
const connection = await imaps.connect({
imap: {
user: user.user, // User email
password: user.password, // User email password
host: 'server.XXXX.com',
port: XXXX,
authTimeout: 10000,
tls: true,
tlsOptions: { rejectUnauthorized: false },
},
});
await connection.openBox('INBOX')
const searchCriteria = [['UID', UID], ['TO', user.user]]
const fetchOptions = {
bodies: ['HEADER', ''], // Empty string means full body
markSeen: false
}
const messages = await connection.search(searchCriteria, fetchOptions)
if (messages.length === 0) {
return res.send({message:`ID ${UID} Email not found`})
}
// console.log(messages)
connection.moveMessage(UID, 'Drafts', (err) => {
if (err){
console.log(err)
console.log({status:500, message:'Problem marking email move to Drafts'});
rej(err);
}else{
connection.end();
return res.send({status:200, message:`Email flag removed successfully!`})
}
})
}catch (error){
console.log(error)
}
}
Hope fully some one have a nice solution.
For context, I'm trying to send a one time link to the user's email as a reset password link that will take them to the reset password page if the jwt token is successfully verified. I followed a tutorial and created a dummy version where user info was stored locally and it worked perfectly. But when I try to implement into my main project which pulls user data from mySQL I keep getting a malformed error, I am checking all the values and everything matches including checking the token on the jwt website to see if it return the correct info which it does so I'm very confused as to what I've done wrong. The only thing that changes between the test and main project is where the data is pulled from. Here is my code for this part of the project:
// Create and send link
router.post('/forgot-password', (req, res, next) => {
var email = req.body.email
db.query('SELECT * FROM users_test WHERE email = ?', [ email ], (error, results) => {
if (results.length < 1) {
res.send('no user')
return
}
const user = results[0]
const secret = process.env.JWT_SECRET + user.password
const payload = {
email: email,
id: user.id
}
const token = jwt.sign(payload, secret)
const link = `http://localhost:5000/auth/reset-password/${user.id}/${token}`
console.log(link)
res.send('sent')
})
})
// verify token and display password reset page
router.get('/reset-password/:id/:token')= (req, res, next) => {
const { id, token } = req.params
db.query('SELECT * FROM users_test WHERE id = ?', [ id ], (error, results) => {
if (error) {
console.log(error)
}
const user = results[0]
const secret = process.env.JWT_SECRET + user.password
res.json({secret})
try {
var payload = jwt.verify(token, secret)
res.render('reset-password.hbs')
}
catch (e) {
console.log(e)
}
})
}
The line the error is point at: var payload = jwt.verify(token, secret)
The error I'm getting:
throw err; // Rethrow non-MySQL errors
^
JsonWebTokenError: jwt malformed
at Object.module.exports [as verify] (C:\Users\winba\Desktop\SecureSoftware\Secure-Software-02\node_modules\jsonwebtoken\verify.js:63:17)
at Query.<anonymous> (C:\Users\winba\Desktop\SecureSoftware\Secure-Software-02\controllers\auth.js:497:29)
at Query.<anonymous> (C:\Users\winba\Desktop\SecureSoftware\Secure-Software-02\node_modules\mysql\lib\Connection.js:526:10)
at Query._callback (C:\Users\winba\Desktop\SecureSoftware\Secure-Software-02\node_modules\mysql\lib\Connection.js:488:16)
at Query.Sequence.end (C:\Users\winba\Desktop\SecureSoftware\Secure-Software-02\node_modules\mysql\lib\protocol\sequences\Sequence.js:83:24)
at Query._handleFinalResultPacket (C:\Users\winba\Desktop\SecureSoftware\Secure-Software-02\node_modules\mysql\lib\protocol\sequences\Query.js:149:8)
at Query.EofPacket (C:\Users\winba\Desktop\SecureSoftware\Secure-Software-02\node_modules\mysql\lib\protocol\sequences\Query.js:133:8)
at Protocol._parsePacket (C:\Users\winba\Desktop\SecureSoftware\Secure-Software-02\node_modules\mysql\lib\protocol\Protocol.js:291:23)
at Parser._parsePacket (C:\Users\winba\Desktop\SecureSoftware\Secure-Software-02\node_modules\mysql\lib\protocol\Parser.js:433:10)
at Parser.write (C:\Users\winba\Desktop\SecureSoftware\Secure-Software-02\node_modules\mysql\lib\protocol\Parser.js:43:10)
Any help or ideas as to where the error is coming from would be appreciated, thank you.
Try with following changes
const link = `http://localhost:5000/auth/reset-password/${user.id}/${JSON.stringify(token)}`
var payload = jwt.verify(JSON.parse(token), secret)
Problem
I have two websites. One is production, but a very very old version.
Another is just localhost where I'm currently testing it.
After involving NextAuthJS to the NextJS website, I need to integrate my NextAuthJS session provider to the remote ExpressJS backend.
I make a request to /login ->
on remote backend I generate a JWT token, and get full user details.
then I send data to the NextJS website.
using NextAuthJS, store it in a cookie.
In case I need to get any user specific details, such as saved videos:
I make a request from NextJS website.
On remote ExpressJS backend I decode the token which I receive in the 'authorization' bearer
header, to get user's details...
And boom! Here the error message repeats shouting at me: "JsonWebTokenError: invalid signature"
That doesn't happen on the production website, even though the remote backend is one link for both the production and development.
What I have tried and explored
What I noticed using JWT token debugger is that
In case of the production website's request, which doesn't give any errors, the decoded user consists of { id, role }. As EXPECTED.
But in case of local version's request, which gives error message, it shows that the user consists of the whole user details object stored in the DB.
And debugger also changes the decryption algorithm automatically from HS256 to HS512. And, even though showing the encrypted message, it says 'token verification failed'.
Token from local, gives error:
eyJhbGciOiJIUzUxMiJ9.eyJlbWFpbCI6ImdtYWlsQGdtYWlsLmNvIiwiYWNjZXNzVG9rZW4iOiJCZWFyZXIgZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SmZhV1FpT2lJMk1UZGpNV0V6TjJFd1lqQXpNakV4TnpFMU0yUTBOek1pTENKeWIyeGxJam9pZFhObGNpSXNJbWxoZENJNk1UWXpOakk1TURrME1Td2laWGh3SWpveE5qTTJNemMzTXpReGZRLmI1YTQ3bF9rcVQ5YUhDVGhFM3VtVFZJOTZZSnpkdkJaZHQxN3hMTFJPU1EiLCJ1c2VyIjp7Il9pZCI6IjYxN2MxYTM3YTBiMDMyMTE3MTUzZDQ3MyIsImZpcnN0TmFtZSI6IiIsImxhc3ROYW1lIjoiIiwiZW1haWwiOiJnbWFpbEBnbWFpbC5jbyIsInJvbGUiOiJ1c2VyIiwiam9iUm9sZSI6IiIsInZpZGVvR29hbCI6IiIsInByb2ZpbGVQaWN0dXJlIjoiIiwiYXBwcm92YWwiOnsiaXNBcHByb3ZlZCI6dHJ1ZSwidHJ4SWQiOiIifSwiYWNjZXNzVHlwZSI6eyJicmFuZGluZzEiOnRydWUsImJyYW5kaW5nMiI6dHJ1ZSwiYnJhbmRpbmczIjp0cnVlLCJicmFuZGluZzQiOnRydWUsInNjcmlwdCI6dHJ1ZSwidGVtcGxhdGUiOnRydWUsImZ1bGxBY2Nlc3MiOnRydWV9LCJyZXNldFBhc3NUb2tlbiI6IiIsImJyYW5kaW5nIjp7ImJyYW5kaW5nMSI6IjYxN2MxYTM3YTBiMDMyMTE3MTUzZDQ3NCIsImJyYW5kaW5nMiI6IjYxN2MxYTM3YTBiMDMyMTE3MTUzZDQ3NSIsImJyYW5kaW5nMyI6IjYxN2MxYTM3YTBiMDMyMTE3MTUzZDQ3NiIsImJyYW5kaW5nNCI6IjYxN2MxYTM3YTBiMDMyMTE3MTUzZDQ3NyJ9LCJzdWJfc3RhdHVzIjoyfSwiaWF0IjoxNjM2MjkxNTk0LCJleHAiOjE2Mzg4ODM1OTR9.ZCnnNZ2a7aeO6GCnyhnWYGMX-4kkJX75ZyzG52lSiWxUBIawXrA37882HFwo_3r6-I3JrrYNv270vxfsMwyBlw
Token from production, works as expected :
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MTdjMWEzN2EwYjAzMjExNzE1M2Q0NzMiLCJyb2xlIjoidXNlciIsImlhdCI6MTYzNjI5MTY3MywiZXhwIjoxNjM2Mzc4MDczfQ.bqY6y0_lmX5dBAHemgv_9UFuupwLBBDcyFpAdXgCiH8
JWT Secret : jdgfaiodsugfaileufliaeruy
Code of decoding token and getting user data is:
exports.requireSignin = (req, res, next) => {
if (req.headers.authorization) {
const token = req.headers.authorization.split(" ")[1];
console.log( 'token is', token );
jwt.verify(token, process.env.JWT_SECRET, function (err, decoded) {
if (err) {
console.log( err, 'Token given', token, 'Token secret', process.env.JWT_SECRET );
return res.status(401).json({ error: "Token has been expired!" });
}
req.user = decoded;
console.log( decoded );
});
// const user = jwt.verify(token, process.env.JWT_SECRET);
// console.log(user);
// req.user = user;
} else {
return res.status(401).json({ error: "Authorization required" });
}
next();
//jwt.decode()
};
Code of GENERATING that token:
exports.signin = async (req, res) => {
User.findOne({ email: req.body.email }).exec((error, user) => {
if (error){ console.log( 'login error', error ); return res.status(400).json({ error }); }
if (user) {
bcrypt.compare(req.body.password, user.hash_password, (err, result) => {
if (err) {
console.log( req.body.password, user.hash_password );
return res
.status(400)
.json({ error: "Something's wrong, Please try again" });
}
if (!result) {
return res.status(400).json({ error: "Invalid credentials" });
}
if (user.approval.isApproved === false)
return res
.status(400)
.json({ error: "Please verify your account" });
if (user.isSuspended === true)
return res.status(400).json({ error: "You have been temporarily suspended, please contact support" });
const token = jwt.sign(
{ _id: user._id, role: user.role },
process.env.JWT_SECRET,
{
expiresIn: "1d",
}
);
console.log( process.env.JWT_SECRET, token, user._id, user.role );
// some unnecessary code ...
res.status(200).json({
success: true,
token: "Bearer " + token,
ProfileImageBaseUrl: ProfileImageBaseUrl,
user: {
_id,
firstName,
lastName,
email,
role,
jobRole,
videoGoal,
profilePicture,
createdBy,
approval,
accessType,
resetPassToken,
branding,
sub_status
},
});
})
});
} else {
return res.status(400).json({ error: "Something's wrong, Please try again" });
}
});
};
A) WHEN I GET AN ERROR, The LOGS I receive from my backend are:
Sign in controller, console.log( process.env.JWT_SECRET, token, user._id, user.role ) is
jdgfaiodsugfaileufliaeruy eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MTdjMWEzN2EwYjAzMjExNzE1M2Q0NzMiLCJyb2xlIjoidXNlciIsImlhdCI6MTYzNjI5MTY3MywiZXhwIjoxNjM2Mzc4MDczfQ.bqY6y0_lmX5dBAHemgv_9UFuupwLBBDcyFpAdXgCiH8 617c1a37a0b032117153d473 user
Require sign in controller ,console.log( err, 'Token given', token, 'Token secret', process.env.JWT_SECRET ) is:
Token given eyJhbGciOiJIUzUxMiJ9.eyJlbWFpbCI6ImdtYWlsQGdtYWlsLmNvIiwiYWNjZXNzVG9rZW4iOiJCZWFyZXIgZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SmZhV1FpT2lJMk1UZGpNV0V6TjJFd1lqQXpNakV4TnpFMU0yUTBOek1pTENKeWIyeGxJam9pZFhObGNpSXNJbWxoZENJNk1UWXpOakk1TURrME1Td2laWGh3SWpveE5qTTJNemMzTXpReGZRLmI1YTQ3bF9rcVQ5YUhDVGhFM3VtVFZJOTZZSnpkdkJaZHQxN3hMTFJPU1EiLCJ1c2VyIjp7Il9pZCI6IjYxN2MxYTM3YTBiMDMyMTE3MTUzZDQ3MyIsImZpcnN0TmFtZSI6IiIsImxhc3ROYW1lIjoiIiwiZW1haWwiOiJnbWFpbEBnbWFpbC5jbyIsInJvbGUiOiJ1c2VyIiwiam9iUm9sZSI6IiIsInZpZGVvR29hbCI6IiIsInByb2ZpbGVQaWN0dXJlIjoiIiwiYXBwcm92YWwiOnsiaXNBcHByb3ZlZCI6dHJ1ZSwidHJ4SWQiOiIifSwiYWNjZXNzVHlwZSI6eyJicmFuZGluZzEiOnRydWUsImJyYW5kaW5nMiI6dHJ1ZSwiYnJhbmRpbmczIjp0cnVlLCJicmFuZGluZzQiOnRydWUsInNjcmlwdCI6dHJ1ZSwidGVtcGxhdGUiOnRydWUsImZ1bGxBY2Nlc3MiOnRydWV9LCJyZXNldFBhc3NUb2tlbiI6IiIsImJyYW5kaW5nIjp7ImJyYW5kaW5nMSI6IjYxN2MxYTM3YTBiMDMyMTE3MTUzZDQ3NCIsImJyYW5kaW5nMiI6IjYxN2MxYTM3YTBiMDMyMTE3MTUzZDQ3NSIsImJyYW5kaW5nMyI6IjYxN2MxYTM3YTBiMDMyMTE3MTUzZDQ3NiIsImJyYW5kaW5nNCI6IjYxN2MxYTM3YTBiMDMyMTE3MTUzZDQ3NyJ9LCJzdWJfc3RhdHVzIjoyfSwiaWF0IjoxNjM2MjkxNTk0LCJleHAiOjE2Mzg4ODM1OTR9.ZCnnNZ2a7aeO6GCnyhnWYGMX-4kkJX75ZyzG52lSiWxUBIawXrA37882HFwo_3r6-I3JrrYNv270vxfsMwyBlw Token secret jdgfaiodsugfaileufliaeruy
B) When no error, these are:
Require sign in : token is eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MTdjMWEzN2EwYjAzMjExNzE1M2Q0NzMiLCJyb2xlIjoidXNlciIsImlhdCI6MTYzNjI5MTYyNSwiZXhwIjoxNjM2Mzc4MDI1fQ.K-inQfI77mepKjkG_5g4obr3sfcVnDwCOXeQlDPra00
Sign in :
jdgfaiodsugfaileufliaeruy eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MTdjMWEzN2EwYjAzMjExNzE1M2Q0NzMiLCJyb2xlIjoidXNlciIsImlhdCI6MTYzNjI5MTYyNSwiZXhwIjoxNjM2Mzc4MDI1fQ.K-inQfI77mepKjkG_5g4obr3sfcVnDwCOXeQlDPra00 617c1a37a0b032117153d473 user
My thoughts
I feel like it is just something simple I just don't notice, I've been trying to fix it for 9 hours, but I haven't determined what yet.
Please, help me, guys!
Any help or hints are highly appreciated!
Thank you so much in advance!
Thanks everyone who helped me with this! #Bergi and #eol!
The problem was nextauthjs specific and not conserned about expressjs, as it appeared.
So I don't get token returned by nextauthjs anymore.
Instead, I get token from user object's authToken property, stored in the session.
So it is like:
MyApp.getInitialProps = async ({ router, ctx }) => {
const session = await getSession(ctx);
if (router.asPath === '/login' || router.asPath === '/register') {
return { session: session };
}
if (!session || !session.user) {
ctx.res.writeHead(302, {
Location: '/login',
});
ctx.res.end();
}
console.log('Access token', session.accessToken);
axios.defaults.headers.common = {
authorization: `${session.accessToken}`,
};
return { token: `${session.accessToken}`, session: session };
};
The success function works perfectly fine when it comes to receiving data from the express backend server and manipulating it. I'm unable to handle error thrown by the backend properly. It is possible to get status code (400 or other) and the related text (Bad request, etc) but not the actual message which the express backend sends.
Backend login code
router.post("/users/login", async (req, res) => {
try {
const user = await User.findByCredentails(
req.body.email,
req.body.password
);
const token = await user.generateAuthToken();
res.send({ user, token });
} catch (error) {
res.status(400).json({ error: error.message }); //error.message is thrown by the database (which logs perfectly fine in the backend when some error occurs)
}
});
Database code that sends the message (There is no error in this; putting this here just for the reference)
userSchema.statics.findByCredentails = async (email, password) => {
const user = await User.findOne({ email });
if (!user) throw new Error("Unable to find a user with the entered email");
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) throw new Error("Incorrect password, try again");
return user;
};
Frontend code (which is working fine for success but not error)
function login() {
const data = {
email: $("#email-field").val(),
password: $("#password-field").val()
};
$.ajax({
url: "/users/login",
method: "POST",
data,
success: function(userData) {
console.log(userData); //works just fine
},
error: function(jqXHR) {
//how can I get that error message here. I'm able to get jqXHR.status and .textStatus but not the actual message that I want.
}
});
}
I am using nodejs and sequelize to create simple user registration, I already did the login and register, but when login is a success I don't get any token.
I have tried to console.log the token to see if I get some result, but there is no response in the console related to the token.
Here is my code:
var express = require('express');
var User = require('../../models').User;
var router = express.Router();
var jwt = require('jsonwebtoken');
var app = require('../../app');
/*var token = jwt.sign(user, app.get('superSecret'), {
expiresInMinutes: 1440 // expires in 24 hours
});*/
router.post('/', function (req, res, next) {
if (JSON.stringify(req.body) == "{}") {
return res.status(400).json({ Error: "Login request body is empty" });
}
if (!req.body.username || !req.body.password) {
return res.status(400).json({ Error: "Missing fields for login" });
}
// search a user to login
User.findOne({ where: { username: req.body.username} }) // searching a user with the same username and password sended in req.body
.then(function (user) {
if (user && user.validPassword(req.body.password)) {
//return res.status(200).json({ message: "loged in!" }); // username and password match
// create a token
var token = jwt.sign(user, app.get('superSecret'), {
expiresInMinutes: 1440 // expires in 24 hours
});
// return the information including token as JSON
res.json({
success: true,
message: 'Enjoy your token!',
token: token
});
}
else{
return res.status(401).json({ message: "Unauthorized" }); // if there is no user with specific fields send
}
}).catch(function (err) {
return res.status(500).json({ message: "server issues when trying to login!" }); // server problems
});
});
module.exports = router;
You should use express-jwt library. https://www.npmjs.com/package/express-jwt
It's middleware for express. Example:
var jwt = require('express-jwt');
app.get('/protected',
jwt({secret: 'shhhhhhared-secret',
credentialsRequired: false,
getToken: (req) => req.cookies.id_token // when cookies are using. 'id_token' is cookie's name
}),
function(req, res) {
if (!req.user.admin) return res.sendStatus(401);
res.sendStatus(200);
});
Also you may write as app.use(...) or app.anyRequest(...)