Submitted May 11, 2020 at 11:39AM by SageRepulsion
Monday, 11 May 2020
Validating password-reset JWT token .
I am doing password-reset in my React/Node app, and need some help in validation of the reset-password JWT token.The flow looks like this:User clicks forgot-password, a form appears and enters the email to receive a link.A reset-password link is a one-time-link sent on the email(with token and userId embedded) like www.abc.com/reset-pw/5e53c/eyJhbdjfsjdflksj.User clicks the link; redirected to the reset password form.Now, I am getting the link, and it is a different one every time (because of the token obviously).I want that when the user submits the form for password change, it should be checked that the token is valid or not. If it is valid, update the password, and if not, just show some message like token expired or something.As you can see the code, I'm just doing this:When the user clicks the link, redirected to the reset password page.userId and token are in params. ex: localhost:3000/reset-pw/5ebcWk/eYjfdkfdkjWhen the user submits the form, the request goes to the resetPasswordcontroller.First, find the user by the userId from the params.Then generate a secret by the combination of user's current hashed password from the db and createdAt value.Now, decode the token from the params, and we get the payload.Now I'm comparing if the userId in payload is same as the userId from the db. If it is, generate a salt, and hash the password. Now, update the user in the database by replacing the old hash with the newly hashed password.That's what I'm doing. I don't know if it's right or not.Here's the code.```js module.exports = { createOneTimeTokenAndSendMail: async (req, res) => { console.log("Inside create token") const { email } = req.body try { const user = await User.findOne({ email }) if (!user) { return res.status(404).json({ error: "No user with that email" }) } const hashedPassword = user.password const createdAt = user.createdAt const userId = user._id const secret = hashedPassword + "-" + createdAt //creating a secret this way makes it unique const token = jwt.sign({ userId }, secret, { expiresIn: 60, }) console.log("ONE-TIME-TOKEN=>", token) const url = getResetPasswordURL(user, token) //one-time url with the userId and token embedded to it const emailTemplate = forgotPasswordTemplate(user, url) transporter.sendMail(emailTemplate, (err, info) => { //sending a mail template to the user if (err) { res.status(500).json({ error: err }) } res.status(200).json({ info: info.response }) }) } catch (error) { console.log(error) } },resetPassword: (req, res) => { const { userId, token } = req.params const password = req.body.password //password coming from the form the user has entered into User.findOne({ _id: userId }, (err, user) => { //find user by id if (err) console.log(err) const secret = user.password + "-" + user.createdAt //again, using the same secret(used above) to decode the token const payload = jwt.decode(token, secret) //decoding token using user's current password hash and createdAt value as a secret if (payload.userId == user.id) { bcrypt.genSalt(10, (err, salt) => { if (err) console.log(err) bcrypt.hash(password, salt, (err, hash) => { //hash the password coming in the body if (err) console.log(err) User.findOneAndUpdate( { _id: userId }, { password: hash }, (err) => { //replacing old password hash with a new hash if (err) console.log(err) res.status(202).json("password changed") receiveConfirmationEmail(userId) } ) }) }) } }) } ``````js import React, { Component } from "react" import axios from "axios"class ResetPasswordForm extends Component { constructor(props) { super(props) this.state = { password: "", confirmPassword: "", isSubmitted: false, } }handleChange = (event) => { const { name, value } = event.target this.setState({ [name]: value, }) }handleSubmit = (e) => { e.preventDefault() const { userId, token } = this.props.match.params const { password } = this.state axios .post( http://localhost:3000/api/v1/users/reset-password/${userId}/${token}, { password } ) .then((res) => { return alert( "Password Updated successfully. You can now sign in " ) }) .catch((err) => { if (err) { return alert("Sorry, please try again") } }) this.setState({ password: "", confirmPassword: "", isSubmitted: true }) this.props.history.push("/") }render() { return (
) } }export default ResetPasswordForm ```
Submitted May 11, 2020 at 11:39AM by SageRepulsion
Submitted May 11, 2020 at 11:39AM by SageRepulsion
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment