Handling password changes with Next.js, GraphQL and Mikro-ORM
Tags: nextjs, react, typescript, graphql
Published: 2020-09-29 19:03:51

---
title: Handling password changes with Next.js, GraphQL and Mikro-ORM
published: true
description: "I will discuss how I handle changing passwords using Next.js in combination with GraphQL and Mikro-ORM" 
tags: nextjs, react, typescript, graphql
//cover_image: https://direct_url_to_image.jpg
---
***
<center>Posted to [my blog](https://dwalsh.surge.sh/changing-password-with-next-js/) first! Please feel free to read it there.</center>
***
## Introduction

I am going to assume you have your application up and running, if you have not set it up yet I suggest just following along with [their docs](https://nextjs.org/docs/getting-started) and then coming back to here. A great thing about Next.js is that you can get started very quickly.

For the login portion of this I will assume you have that handled, however I will probably write another post about handling that with this stack soon and link it here in case anyone struggles with it. It is also important to note that I will include examples and explanations of my backend code and although I understand that you may not be using this exact stack, it will be useful for explaining the logic behind my decisions.

Some of the main packages I am using are [mikro-orm](https://mikro-orm.io/), [type-graphql](https://typegraphql.com/docs/introduction.html), [urql](https://formidable.com/open-source/urql/), [graphQL](https://graphql.org/), [Apollo server express](https://www.google.com/search?q=apollo+server+express&oq=apollo+server+express&aqs=chrome..69i57j0l7.3127j0j4&sourceid=chrome&ie=UTF-8), [nodemailer](https://nodemailer.com/about/), [redis](https://redis.io/) and [uuid](https://www.npmjs.com/package/uuid). I will again point this out in my code snippets as we go along.

Below are the steps we will take when a user wants to change their password.

### Steps

1. User selects forgot password on the website

2. We check if the email is valid and in use

3. We generate a token for this user

4. We email the user a link to change the password with this token in the url

5. The user submits the change password form

6. We handle this password change on our backend and delete the token

Now let’s get started!

## Backend Logic

When developing out certain features I like to have the backend logic roughly completed first and I then implement the frontend for it and making any necessary adjustments. As my backend uses graphQL the first step is to create my function which handles the user requesting an email to change their password.

### My Context

I just want to place here my context, which is accessible in all my resolvers. The request and response objects are pretty standard and I got their types simply from hovering over them in VSCode. What important to note here is the `em` and `redis` objects. The `em` object is the ORM which is configured to connect to my database and the `redis` object is used to access my redis instance, which is where user sessions are stored.

```ts
  // my context
  context: ({ req, res }: MyContext) => ({
      em: orm.em,
      req,
      res,
      redis,
    }),
  //...

  // types.ts
  export type MyContext = {
  em: EntityManager<any> & EntityManager<IDatabaseDriver<Connection>>;
  req: Request & { session: Express.Session };
  res: Response;
  redis: Redis;
};
```

### Forgot Password Mutation

This [mutation](https://graphql.org/learn/queries/#mutations) takes an `email` parameter and will return a boolean depending on whether the email address was valid and if the link could be sent. Below you will see the definition of this function and a first look at the use of `type-graphql`.

```ts
@Mutation(() => Boolean)
  async forgotPassword(@Arg("email") email: string, @Ctx() { em, redis }: MyContext) {
  // ...
  // ...
  }
```

The reason for using `type-graphql` is because it allows you to define schemas using only their decorators. It then allows us to inject dependencies into our resolvers and put auth guards in place, all while cutting down on code redundancy.

So the function takes an `email` parameter and accesses the `em` and `redis` objects ([see here for clarification](#my-context)). The first thing we will do is check if the email address is in the database and return false if it is not present.

```ts
  // ...
const person = await em.findOne(User, { email });
    if (!person) {
      return false;
    }
  // ...
  }
```

If the user is present we will generate a token using `uuid`'s `v4` function. This token is stored with the `forgot-password:` prefix and the key is the user's `id` field. The token will expire 3 days after the user makes the request.

```ts
// ...
const token = v4()
redis.set(
  `${FORGET_PASSWORD_PREFIX}${token}`,
  person.id,
  "ex",
  1000 * 60 * 60 * 24 * 3
) // 3 days
// ...
```

Once the token is set and stored we will send the user the email with the link. This link will include the token and we use this to identify the user.

```ts
//..
await sendEmail(
      email,
      `<a href="http:localhost:3000/change-password/${token}">reset password</a>`
    );
    return true;
}
```

The contents of the `sendEmail` function are taken directly from the example given in the Nodemailer docs. For clarity I will include it below.

```ts
let testAccount = await nodemailer.createTestAccount()
console.log("test account: ", testAccount)
let transporter = nodemailer.createTransport({
  host: "smtp.ethereal.email",
  port: 587,
  secure: false, // true for 465, false for other ports
  auth: {
    user: testAccount.user, // generated ethereal user
    pass: testAccount.pass, // generated ethereal password
  },
})

let info = await transporter.sendMail({
  from: '"Sample Person" <foo@example.com>', // sender address
  to: to, // list of receivers
  subject: "Change Password", // Subject line
  html,
})

console.log("Message sent: %s", info.messageId)

console.log("Preview URL: %s", nodemailer.getTestMessageUrl(info))
```

## Forget Password Page

Now in our Next.js application, in the `./src/pages/` folder, we will create a `change-password` folder. In this folder we create a `[token].tsx` file.

(So the full path will be `./src/pages/change-password/[token].tsx`)

### Dynamic Routing

In Next.js the `[param]` file syntax is used for **dynamic routes**. This parameter will be sent as a query parameter to this page.

The next step is you must then decide **when** you will need to access this on the page via the `props`. This can be accomplished a handful of functions given to us by Next.js, however the use case will decide what function.

The three options too us are:

1. `getServerSideProps`

2. `getStaticProps`

I choose `getServerSideProps` as the data must be fetched at **request time**. We do not have a list of possible `token`'s at build time.

The docs for `getStaticProps` states that we should only be using this function if:

> The data required to render the page is available at build time ahead of a user’s request.

So in our `[token].tsx` file we start with the following scaffolding:

```tsx
import { GetServerSideProps, NextPage } from "next";

const ChangePassword: NextPage<{token: string}> = ({token}) => {
  return (
    //..
    // form goes here
    //..
  )
};

export const getServerSideProps: GetServerSideProps = async ({ params }) => {
  return {
    props: {
      token: params.token,
    },
  };
};

export default ChangePassword;
```

As we use dynamic routing, `params` contains this dynamic data. The reason we use `params.token` is because we named our file `[token].tsx`. If we were to name it `[user-id]` then the props passed would be `token: params.user-id`.

I then use `Formik` and `urql` to handle form state and sending the data to the server. Once the form is submitted without any errors back from the server the user is logged back in with the new password and redirected to the home page. This will now take us back to the backend for handling this data submission.

## Handling the password change

Once we are back in our resolvers we create the `changePassword` resolver and it is important to take the time to define the type for the response to this. We can then make use of this type then when we generate our types in the frontend with the `graphql-codegen` package.

The `UserResponse` object will return an array of errors (each with a `field` and `message` field) and the user, with both having the option of being null. I choose an array of objects because I have a helper function for the frontend which will map the errors to the appropriate `formik` field and display them accordingly (I got this function from a Ben Awad video and I will include this below).

```tsx
// toErrorMap.tsx
import { FieldError } from "../generated/graphql";

// map errors accordingly
// taken from Ben Awad video
export const toErrorMap = (errors: FieldError[]) => {
  const errorMap: Record<string, string> = {};
  errors.forEach(({ field, message }) => {
    errorMap[field] = message;
  });
  return errorMap;
};

// form.tsx
// usage example in a formik form
const form = () => {

  const handleSubmit = (values, {setErrors}) => {
    // send data via graphql
    const response = sendDataViaGraphl(values);
    if (response.data?.errors) {
      // if there’s errors
      setErrors(toErrorMap(response.data.errors))
    }
  }
  return (
  // form down here
  )
}
```

Below is the schema typings I described above for the data returned from the mutation.

```ts
@ObjectType()
class FieldError {
  @Field()
  field: string
  @Field()
  message: string
}

@ObjectType()
class UserResponse {
  @Field(() => [FieldError], { nullable: true })
  errors?: FieldError[]

  @Field(() => User, { nullable: true })
  user?: User
}
```

Now onto the `changePassword` function itself! It takes 2 arguments, `token` and `newPassword`. From our context again we take the `redis`, `em` and `req` objects. We also state our response type as the previously defined `UserResponse` type.

```ts
@Mutation(() => UserResponse)
  async changePassword(
    @Arg("token") token: string,
    @Arg("newPassword") newPassword: string,
    @Ctx() { redis, em, req }: MyContext
  ): Promise<UserResponse> {
  // ...
  // ...
  };
```

The first thing we will check is the password length, it is just a basic security measure. Again make sure to note that this return matches the `errors` type we defined above.

```ts
// ...
{
  if (newPassword.length <= 5) {
    return {
      errors: [
        {
          field: "newPassword",
          message: "password is not long enough",
        },
      ],
    }
  }
}
// ..
```

Next we move onto checking the redis database for the users id. Remember, we are accessing the `redis` object via context.

```ts
// ..
const key = FORGET_PASSWORD_PREFIX + token
const userId = await redis.get(key)
// ..
```

Now we apply some more checks to see if the user exists both the redis and user database and if either fails we return the appropriate errors (and their corresponding messages).

```ts
// ..
if (!userId) {
  return {
    errors: [{ field: "token", message: "token expired" }],
  }
}
const user = await em.findOne(User, { id: parseInt(userId) })
if (!user) {
  return {
    errors: [{ field: "token", message: "token expired" }],
  }
}
// ..
```

If there is no issues with finding the user, we then hash the password taken as a function argument and update the database.

As a security measure we delete the key from redis so the user (or someone else) cannot go back and use the same token again.

Finally we login the user using the `req` object via the use of a session and return the `user`.

```ts
// ..
user.password = await argon2.hash(newPassword);
    em.persistAndFlush(user);
    await redis.del(key);
    req.session.userId = user.id;

    return { user };
};
```

And that's it! The user will be logged in on the frontend when they end up back on the home page.

## Final Notes

Thanks for taking the time to read this. Should you have any feedback or questions please feel free to reach out and let me know!