I've created an email authentication system, however there appears to be an issue with how I jwt.verify this token.
I believe there's an issue with my : process.env.PASS_SEC, which is just my Mongo.DB password secret. Is this correct?
I can confirm if I do a res.sent(req.params.token), my token comes through fine, for example in this.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYyZjc0MWU3ZjBkZjZkY2IyZjM0ZDc3ZSIsImlhdCI6MTY2MDM3MTQzMSwiZXhwIjoxNjYwNjMwNjMxfQ.vFtdRzEH2_52Hdhxs84bk7RPdIRDIoZ6Rcd-zZoBhus
As such, I believe it's the SECRET is being passed incorrectly.
My current functioning code is:
router.post("/register", async (req, res, EMAIL_SECRET) => {
const newUser = new User({
fullname: req.body.fullname,
email: req.body.email,
position: req.body.position,
username: req.body.fullname,
password: CryptoJS.AES.encrypt(
req.body.password,
process.env.PASS_SEC
).toString(),
});
const accessToken = jwt.sign(
{
id: newUser._id,
},
process.env.JWT_SEC,
{
expiresIn:"3d"
},
);
const url = `http://localhost:5000/api/auth/confirmation/${accessToken}`;
const mailOptions = {
from: 'nathandrewphone#gmail.com',
to: req.body.email,
subject: 'Confirm Email',
html: `Please click this email to confirm your email: ${url}`
};
transporter.sendMail(mailOptions, function(error, info){
if (error) {
console.log(error);
} else {
console.log('Email sent: ' + info.response);
}
});
try {
const savedUser = await newUser.save();
res.status(201).json(savedUser);
} catch (err) {
res.status(500).json(err);
}
});
Which sends a code fine, however it does not appear to be correct, how would you create an EMAIL_SECRET?
This is how I wish to validate the email.
//User Email Auth Login
//Not yet functioning
router.get('/confirmation/:token', async (req, res) => {
try {
//verify the token with the secret
const { _id: { _id } } = jwt.verify(req.params.token, process.env.PASS_SEC);
await models.User.update({ confirmed: true }, { where: { _id } });
} catch (e) {
res.send('This isnt working');
}
});
However, I cannot get to verify, whats wrong with secret
You signed your token with process.env.JWT_SEC, you should verify it using the same key:
const { _id } = jwt.verify(req.params.token, process.env.JWT_SEC);
Also, you should be able to update your User with findByIdAndUpdate:
await User.findByIdAndUpdate(_id, { confirmed: true });
Related
node js
This is my register method to register a user. I am trying to pass token in headers when a user is registered which will be used in the front end to access the token and store it in the local storage.
module.exports.register = async function (req, res, next) {
try {
const { username, email, password } = req.body;
const profileImage = req.file.path;
const usernameCheck = await User.findOne({ username });
if (usernameCheck)
return res.json({ msg: "Username already used", status: false });
const emailCheck = await User.findOne({ email });
if (emailCheck)
return res.json({ msg: "Email already exists", status: false });
const hashedPassword = await bcrypt.hash(password, 10);
const user = await User.create({
_id: new mongoose.Types.ObjectId(),
username,
email,
profileImage,
password: hashedPassword,
});
delete user.password;
//create jwt token
const token = jwt.sign(
{
username: user.username,
email: user.email,
userId: user._id,
},
process.env.JWT_KEY,
{
expiresIn: "1h",
}
);
res.header("x-auth-token", token); //This is not setting the token in headers
return res.json({
message: "User Created Successfully",
status: true,
user,
});
} catch (ex) {
next(ex);
}
};
react js
This is my front-end react code to register a user. I want to login the user with the jwt token stored in localStorage once the user is registered.
const handleSubmit = async (values) => {
try {
const { username, email, profileImage, password } = values;
const formData = new FormData();
formData.append("username", username);
formData.append("email", email);
formData.append("profileImage", profileImage);
formData.append("password", password);
const response = await register(formData);
console.log(response);
if (response.status === false) return;
else {
loginWithJwt(response.headers["x-auth-token"]);// log the user in using jwt token
console.log(response.headers["x-auth-token"]);
navigate("/chatroom");
}
} catch (ex) {
console.log(ex.message);
}
};
The app crashes whenever I go to Google Sign in screen. I don't even have to click anything, it just crashes.
Error: The verifyIdToken method requires an ID Token at OAuth2Client.verifyIdTokenAsync (/app/node_modules/google-auth-
Google Login Implementation:
exports.googleController = (req, res) => {
try {
const { idToken } = req.body;
client
.verifyIdToken({ idToken, audience: process.env.GOOGLE_CLIENT })
.then((response) => {
console.log("GOOGLE LOGIN RESPONSE", response);
const { email_verified, name, email } = response.payload;
if (email_verified) {
User.findOne({ email }).exec((err, user) => {
if (user) {
const token = jwt.sign({ _id: user._id }, process.env.JWT_SEC, {
expiresIn: "7d",
});
const { _id, email, name, isAdmin } = user;
return res.json({
token,
user: { _id, email, name, isAdmin },
});
} else {
let password = email + process.env.JWT_SEC;
user = new User({ name, email, password });
user.save((err, data) => {
if (err) {
console.log("ERROR GOOGLE LOGIN ON USER SAVE", err);
return res.status(400).json({
error: "User signup failed with google",
});
}
const token = jwt.sign({ _id: data._id }, process.env.JWT_SEC, {
expiresIn: "7d",
});
const { _id, email, name, isAdmin } = data;
return res.json({
token,
user: { _id, email, name, isAdmin },
});
});
}
});
} else {
return res.status(400).json({
error: "Google login failed. Try again",
});
}
});
} catch (err) {
console.log(err);
}
};
I have a NodeJs and ReactJs project, where a user can register and after the user is registered they will get an email to confirm their account.
so now when I register the email is working well. but it works with an email that I set in like this.
function sendMail() {
const msg = {
to: "someoneemail#gmail.com",
from: "myemail#gmail.com",
subject: "a subject",
text: "some text herer",
html: "<strong>and easy to do anywhere, even with Node.js</strong>",
};
sgMail
.send(msg)
.then(() => {
console.log("Email sent");
})
.catch((error) => {
console.error(error);
});
}
module.exports = { sendMail };
I need to remove this to: "someoneemail#gmail.com" a*
nd instead set the user email, the user who to register on this
system
and instead of text: i have to send the token.
so here is the registration part:
router.post("/register", async (req, res) => {
const { fullName, emailAddress, password } = req.body;
const user = await Users.findOne({
where: {
[Op.and]: [{ fullName: fullName }, { emailAddress: emailAddress }],
},
});
if (user) {
res.status(400).send({
error: `some message.`,
});
} else {
bcrypt
.hash(password, 10)
.then((hash) => {
return {
fullName: fullName,
emailAddress: emailAddress,
password: hash,
isVerified: false,
};
})
.then((user) => {
const token = TokenGenerator.generate();
const creator = Verifications.belongsTo(Users, { as: "user" });
return Verifications.create(
{
token,
user: user,
},
{
include: [creator],
}
);
})
.then((verification) => {
console.log("verification", verification);
sendMail();
})
.then(() => res.json("User, Successmessage "));
}
});
but the codes are not in the same file.
Just add the parameters you need to the sendMail function:
function sendMail(user, token) {
const msg = {
to: user.emailAddress,
from: "myemail#gmail.com",
subject: "Sending with SendGrid is Fun",
text: token,
html: `<strong>${token}</strong>`,
};
sgMail
.send(msg)
.then(() => {
console.log("Email sent");
})
.catch((error) => {
console.error(error);
});
}
Also inject the needed parameters in the promises:
.then(async (user) => {
const token = TokenGenerator.generate();
const creator = Verifications.belongsTo(Users, { as: "user" });
await Verifications.create(
{
token,
user: user,
},
{
include: [creator],
}
);
return {user, token};
})
.then(({user, token}) => {
sendMail(user, token);
})
.then(() => res.json("User, Successmessage "));
I am trying to do my login function (I am using bcrypt and jsonwebtoken) the problem is that console.log (user._id) returns me "new ObjectId (" 6148f043ebbaa0ab41ac8499 ")" instead of just "6148f043ebbaa0ab41ac8499" , which would be easier for the creation of the token.
module.exports.login = async (req, res) => {
const { email, password } = req.body;
// Compare the req.body.password to the hashed password in DB
const user = await UserModel.findOne({ email: email });
const match = await bcrypt.compare(password, user.password);
if (match) {
try {
const user = await UserModel.findOne({ email: email });
console.log(user._id);
// Assign a token
const token = jwt.sign({ userId: user._id }, process.env.LOGIN_TOKEN, {
expiresIn: "1h",
});
console.log(token);
res.cookie("jwt", token, { httpOnly: true});
res.status(200).json({ user: user._id });
} catch (err) {
res.status(500).json(err);
}
} else {
res.status(500).json({ message: "error!!!" });
}
};
How to fix this please?
That is a normal behaviour. Since you got an ObjectId, you can convert it to a string by calling the toHexString() method on it. I have also modified the code to check for an undefined user, and removed the extra call to find a user since you already did in the previous line. Please see updated code:
module.exports.login = async (req, res) => {
const { email, password } = req.body;
const user = await UserModel.findOne({ email: email });
if (!user) {
return res.status(400).json({ message: "Unauthorised"});
}
// Compare the req.body.password to the hashed password in DB
const match = await bcrypt.compare(password, user.password);
if (match) {
try {
// Convert user id (ObjectId) to a string
const userId = user._id.toHexString();
// Now user id is a string
console.log(userId);
// Assign a token
const token = jwt.sign({ userId }, process.env.LOGIN_TOKEN, {
expiresIn: "1h",
});
console.log(token);
res.cookie("jwt", token, { httpOnly: true});
res.status(200).json({ user });
} catch (err) {
res.status(500).json(err);
}
} else {
res.status(400).json({ message: "Unauthorised" });
}
};
use the following
const id = _id.toHexString();
I am using passportJs to authenticate users on my app, I can create users, create them credentials, and they can login with no issues. For the protected routes, I'm using passport-jwt strategy, and all routes work just fine. However, I have been trying to use the same passport strategy for the route that creates those users, but no matter what I do I always get the unauthorized response 401. On the front end I use react and axios.
Here is my passport code:
const opts = {
jwtFromRequest: ExtractJWT.fromAuthHeaderWithScheme("JWT"),
secretOrKey: process.env.JWT_SECRET
};
passport.use(
"jwtToPostUser",
new JWTstrategy(opts, (jwt_payload, done) => {
try {
User.findOne({
where: {
email: jwt_payload.email
}
}).then(user => {
if (user) {
console.log("user found in db in passport");
done(null, user);
} else {
console.log("user not found in db");
done(null, false);
}
});
} catch (err) {
done(err);
}
})
);
Here is the route to create users:
router.post(
"/signup",
passport.authenticate("jwtToPostUser", { session: false }),
(req, res, next) => {
console.log(req.body);
const {
businessname,
username,
firstName,
lastName,
phoneNumber,
email,
password,
customerStatus,
userType,
Gooduntil
} = req.body;
if (password.length < 8) {
throw "Password must be at least 8 characters";
} else {
User.findOne({
where: {
email
}
}).then(user => {
if (user) {
res.send("Email already exists!");
} else {
const encryptedPassword = bcrypt.hashSync(password, salt);
let newUser = {
businessname,
username,
firstName,
lastName,
phoneNumber,
email,
password: encryptedPassword,
customerStatus,
userType,
Gooduntil
};
User.create(newUser)
.then(() => {
// newUser.isAdmin = true
delete newUser.password;
res.send(newUser);
})
.catch(function(err) {
console.log(err);
res.json(err);
});
}
});
}
}
);
This JWTstrategy works for all the get routes, except for this one, that creates the user.
Here is my request from the front end:
addClient = async e => {
let newUser = {
businessname: businessname.toLowerCase(),
firstName: firstName.toLowerCase(),
lastName: lastName.toLowerCase(),
email,
username,
password,
phoneNumber,
customerStatus: customerStatus.value,
userType,
Gooduntil
};
const accessString = localStorage.getItem("JWT");
await Axios.post(
"/auth/signup",
{
headers: {
Authorization: `JWT ${accessString}`
}
},
newUser
)
.then(res => {
console.log(res);
this.setState({
loadingAxiosReq: false
});
})
.catch(err => console.log(err));
}
This it the error I'm getting: Error: Request failed with status code 401
Does anybody know why is this happening? The only way I can make it work is by removing the passport-JWT strategy from the signup route, but that's not secure. please help!
are you sure that you have attached your auth token on your POST request?
I was setting the header with Authorization, instead, I replaced it with jwt, now works perfectly.