DEV Community

Segun98
Segun98

Posted on

Simple Password Reset Flow using Nodejs, Expressjs, SendGrid and uuid

I implemented a password reset flow in a personal project and I thought I should write about it. This article assumes you can set up an express server, query a database, create routes... and all that fun stuff.

Packages we'd use:

SendGrid (for emails) - npm i @sendgrid/mail
Knex (A Database Query Builder. Of course you can use any) - npm i pg knex
uuid (to generate unique ids) - npm i uuid
bcrypt (to hash passwords) - npm i bcryptjs

Enter fullscreen mode Exit fullscreen mode

Go ahead and install them!

First I'd explain the process:

Step 1:
  • Create a 'password_reset' table in your database with columns "id", "email".
Step 2:
  • Users submit their email requesting for a password change, we check if this email exists in our "users" table (existing users table). If the user exists, we insert a unique id and the submitted email into the "password_reset" table and send them an email with a link containing that id.
Step 3:
  • We use the id in the link to verify the user and update with the newly entered password

Easy stuff! Now let's write some code.

#### Step 2

//imports

const knex = require("--your db setup--")
//sendgrid for emails
const sgMail = require('@sendgrid/mail');
//uuid
const {
    v4: uuid
} = require("uuid")

//add your api key. Please sign up on https://sendgrid.com to get yours
sgMail.setApiKey(--Your API key--);

router.post("/password_reset", async (req, res)=> {
         //email user submitted

        const {
            email
        } = req.body

        try {
            //check if user exists

            const user = await knex("users").select("email").where({
                email
            })

            if (user.length > 0) {
            //generate a unique id

                let id = uuid()

            //insert into the "password_reset" table

                await knex("password_reset").insert([{
                    id,
                    email: user[0].email
                }])

            //email content sent with SendGrid. The unique id is sent in the email as a link

                const content = {
                    to: email,
                    from: "support@me.com",
                    subject: "Password Recovery",
                    html: `<body>
                    <p>Click to set a new password : <a href="yoururl/password/reset/${id}">Reset password</a></p>

                    </body>`
                }
                await sgMail.send(content)
                return res.send("Please check your email for the next step")
            }

          //if email doesn't exist

            return res.status(404).send("email does not exist in our records")
        } catch (error) {
            res.send(error.message)
        })

Enter fullscreen mode Exit fullscreen mode

Next step is to verify the user by checking the "password_reset" table with the id passed in the email url.

#### Step 3A

  router.get("/verify_email/:id", async (req, res) => {
       //pass in the id as a parameter

        const {
            id
        } = req.params

        try {
           //query for the email with the provided id

            const email = await knex("password_reset").select("email").where({
                id
            })
           //send back the email

           return res.send(email[0])

        } catch (error) {
            res.status(404).send(error.message)
        }
}
)

Enter fullscreen mode Exit fullscreen mode

Finally We update the user's password in the "users" table, and clear the record in the "password_reset" table to ensure the link sent in the email in "Step 2" is useless.

##### Step 3B:

router.post("/change_password", async(req,res)=>{
        const {
            email,
            id,
            newpassword
        } = req.body

        try {
            //final check if user exists in "password_reset" table

            const user = await knex("password_reset").select("email").where({
                email
            })
            if (user.length === 0) {
                return res.status(404).send("an error occured")
            }

            //hash new password

            const salt = await bcrypt.genSalt(10)
            const hashedpassword = await bcrypt.hash(newpassword, salt)

           //update the users table!

            await knex('users')
                .where({
                    email
                })
                .update({
                    password: hashedpassword
                })

            //clear the record in "password_reset" table for security

            await knex('password_reset')
                .where({
                    id
                })
                .del()

            return res.send("password successfully changed!")
        } catch (error) {
            res.send(error.message)
        }
})
Enter fullscreen mode Exit fullscreen mode

The End!

Note:

  • There are probably other ways to do this, this is what I came up with.

Top comments (1)

Collapse
 
onava84 profile image
onava84 • Edited

Hi thanks for the post. Whats url shoul I put here ===> href="yoururl/password/reset/${id}"
The one where I have the fields to enter the new password?

Thanks!