How to setup email verification in Feathers.js - Frontend using Vue.js
Tags: vue, typescript, node, webdev
Published: 2021-09-10 00:39:26

This is the second part of [How to setup email verification in Feathers.js](https://dev.to/ivanz123/how-to-setup-email-verification-in-feather-js-and-node-js-4had) If you have arrived here, without reading the previous one, click on the link, and then you come back..

## Create a project.

We generate an app with **vue cli**
`vue create feathers-email-verification-vue`

Enter your project
`cd feathers-email-verification-vue`

Open your VS Code
`code .`

Execute server dev
`npm run server`

You will see this on your screen.
![Screen for default of the project with Vue](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ksgkb4bg6fduhp2ivjlo.png)

## Create pages.

*Login.vue*

```vuejs
<template>
  <h1>Login page</h1>
</template>

<script lang="ts">
import { Options, Vue } from "vue-class-component";

@Options({})
export default class LoginPage extends Vue {}
</script>
```

*Register.vue*

```vuejs
<template>
  <h1>Register page</h1>
</template>

<script lang="ts">
import { Options, Vue } from "vue-class-component";

@Options({})
export default class RegisterPage extends Vue {}
</script>
```

Add the pages to the Vue router

```typescript
src > router > index.ts

const routes: Array<RouteRecordRaw> = [
  {
    path: "/register",
    name: "Register",
    component: () => import("@/views/Register.vue"),
  },
  {
    path: "/login",
    name: "Login",
    component: () => import("@/views/Login.vue"),
  },
  // Mores pages.
];
```

## Styles to components.
Now we will add styles to the pages, so that they do not look so simple.

*Register.vue*
Copy the following content in your component.

```vuejs
<template>
  <div class="container-fluid">
    <div
      class="row justify-content-center align-items-center"
      style="min-height: 85vh"
    >
      <div class="col-12 col-sm-8 col-md-6 col-lg-5 col-xl-4">
        <div class="card bg-white border-0 shadow p-2">
          <div class="card-body">
            <form>
              <div class="form-group py-2">
                <label for="fullname">Fullname</label>
                <input
                  placeholder="Exp: Ivan Zaldivar"
                  class="form-control my-1"
                  autocomplete="off"
                  name="fullname"
                  id="fullname"
                  autofocus
                  required
                />
                <small class="form-text text-muted">Enter your name</small>
              </div>
              <div class="form-group py-2">
                <label for="email">Email</label>
                <input
                  placeholder="Exp: abc@gmail.com"
                  class="form-control my-1"
                  autocomplete="off"
                  name="email"
                  id="email"
                  required
                />
                <small class="form-text text-muted">Enter your email</small>
              </div>
              <div class="form-group py-2">
                <label for="password">Password</label>
                <input
                  class="form-control my-1"
                  type="password"
                  name="password"
                  id="password"
                  required
                />
                <small class="form-text text-muted">Enter your password</small>
              </div>
            </form>
            <div class="pt-2">
              <button class="btn btn-primary border-0 py-2 px-3 w-100">
                Create account
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { Options, Vue } from "vue-class-component";

@Options({})
export default class RegisterPage extends Vue {}
</script>
```

*Login.vue*
Copy the following content.

```vuejs
<template>
  <section class="Login">
    <div class="container-fluid">
      <div
        class="row justify-content-center align-items-center"
        style="min-height: 85vh"
      >
        <div class="col-12 col-sm-8 col-md-6 col-lg-5 col-xl-4">
          <div class="card bg-white border-0 shadow p-2">
            <div class="card-header border-0 bg-white pb-0">
              <h2><strong>Login</strong></h2>
              <p class="text-muted">Log in and access our services.</p>
            </div>
            <div class="card-body pt-0">
              <form>
                <div class="form-group py-2">
                  <label for="email">Email</label>
                  <input
                    placeholder="Exp: abc@gmail.com"
                    class="form-control my-1"
                    autocomplete="off"
                    name="email"
                    id="email"
                    autofocus
                    required
                  />
                  <small class="form-text text-muted">Enter your email</small>
                </div>
                <div class="form-group py-2">
                  <label for="password">Password</label>
                  <input
                    class="form-control my-1"
                    type="password"
                    name="password"
                    id="password"
                    required
                  />
                  <small class="form-text text-muted"
                    >Enter your password</small
                  >
                </div>
              </form>
              <div class="pt-2">
                <button class="btn btn-primary border-0 py-2 px-3 w-100">
                  Login
                </button>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </section>
</template>

<script lang="ts">
import { Options, Vue } from "vue-class-component";

@Options({})
export default class LoginPage extends Vue {}
</script>
```

Preview of both components.

![Auth Forms](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n11agnrb7fhsnqcsdaz9.png)

By the way, if you are wondering about the layout, I am using [Bootstrap](https://getbootstrap.com) for this example.

## Setting Feathers.
Before starting with the validation of emails, it is necessary to download some packages.

`npm i @feathersjs/feathers @feathersjs/authentication-client @feathersjs/rest-client`

> - `@feathersjs/feathers`: Package main.
> - `@feathersjs/authentication-client`: Add authentication support.
> - `@feathersjs/rest-client`: Add support REST. This is necessary because somehow we have to communicate with the server. If you want to add communication in real time it will be necessary to install `@feathersjs/socketio-client socket.io-client`

Once the packages are installed, we create a `config/feathers.ts` file.

```typescript
src > config > feathers.ts

import feathers, { Service } from "@feathersjs/feathers";
import authentication from "@feathersjs/authentication-client";
import _rest from "@feathersjs/rest-client";
// import socketio from "@feathersjs/socketio-client";
// import io from "socket.io-client";

import { User } from "@/services/auth.service";

// Initialize transport.
const rest = _rest("http://localhost:3030");

// Initialize socket.io
// const socket = io("http://localhost:3030");

// Initialize feathers app
const app = feathers();
// Add support real-time with socket.io
// app.configure(socketio(socket));

// Configure transport REST API.
app.configure(rest.fetch(window.fetch));
// Add support authentication-client.
app.configure(
  authentication({
    storage: window.localStorage,
    // By default it is <authentication> but, if it has changed, it is necessary to add its URL
    // path: "authentication"
  })
);

// Export authentication-cient
export const Auth = app.authentication;
// Export auth management.
export const AuthManagement: Service<any> = app.service("authManagement");
// Export user service.
export const UserService: Service<User> = app.service("users");

```

Perfect, we've configured feathers.

## Create a Auth Service.
This service will be responsible for taking care of all the authentication logic of our application. Copy The following content.

```typescript
src > services > auth.service.ts

import { AuthenticationClient } from "@feathersjs/authentication-client/lib";
import { Auth, AuthManagement, UserService } from "@/config/feathers";

export interface User {
  _id: string;
  email: string;
  password: string;
  fullname: string;
}

export interface AuthResult {
  accessToken: string;
  authentication: {
    strategy: string;
  };
  user: User;
}

export class AuthService {
  private readonly auth: AuthenticationClient;

  constructor() {
    this.auth = Auth;
  }

  async login(email: string, password: string): Promise<AuthResult> {
    return this.auth.authenticate({
      strategy: "local",
      email,
      password,
    }) as Promise<AuthResult>;
  }

  async signup(user: Omit<User, "_id">): Promise<User> {
    return await UserService.create(user);
  }

  /**
   * Verify the email account.
   */
  async verifySignUp(token: string): Promise<User> {
    return (await AuthManagement.create({
      action: "verifySignupLong",
      value: token,
    })) as Promise<User>;
  }
}

```

> Note: Remember that in *Feathers* you can not only verify emails, also passwords, identity, etc. If you need more information visit the following link: https://github.com/feathersjs-ecosystem/feathers-authentication-management/blob/HEAD/docs.md In it you will find all the available strategies.

Add functionality to components.

*Register.vue*

```vue
src > views > Register.vue

<template>
  <div>
    <form action="">
      <div
        v-if="message"
        :class="`alert alert-${message.status} alert-dismissible fade show`"
        role="alert"
      >
        {{ message.text }}
      </div>
      <!-- Rest of the component. -->
    </form>
  </div>
</template>

<script lang="ts">
import { Options, Vue } from "vue-class-component";
import { AuthService } from "@/services/auth.service";

@Options({})
export default class RegisterPage extends Vue {
  message: Record<string, string> | null = null;
  user = {
    fullname: "",
    email: "",
    password: "",
  };

  async signup(): Promise<void> {
    try {
      const { fullname, email, password } = this.user;
      const { signup } = new AuthService();
      const data = await signup({ fullname, email, password });
      this.setMessage(
        `You have successfully registered, an email has been sent to ${data.email} to confirm that it is you. ✨`,
        "success"
      );
    } catch (error) {
      this.setMessage(error.message || "", "danger");
    }
  }

  setMessage(text: string, status: string): void {
    this.message = { text, status };
    setTimeout(() => (this.message = null), 5000);
  }
}
</script>

```

*Login.vue*

```vue
src > views > Login.vue

<template>
  <div>
    <form action="">
      <div
        v-if="message"
        :class="`alert alert-${message.status} alert-dismissible fade show`"
        role="alert"
      >
        {{ message.text }}
      </div>
      <!-- Rest of the component -->
    </form>
  </div>
</template>

<script lang="ts">
import { Options, Vue } from "vue-class-component";

import { AuthService } from "@/services/auth.service";

@Options({})
export default class LoginPage extends Vue {
  message: Record<string, string> | null = null;
  credentials: Record<string, string> = {
    email: "",
    password: "",
  };

  async login(): Promise<void> {
    try {
      const { email, password } = this.credentials;
      const auth = new AuthService();
      await auth.login(email, password);
      // Enter profile.
      this.$router.replace("/profile");
    } catch (error) {
      this.setMessage(error.message || "", "danger");
    }
  }

  setMessage(text: string, status: string): void {
    this.message = { text, status };
    setTimeout(() => (this.message = null), 5000);
  }
}
</script>

```

Perfect, the logic has been added to the authentication components and display messages when something goes right or wrong.

## Create email verification page.
This page is responsible for sending the request to verify the user's account. Copy the following content.

```vue
src > views > VerifyEmail.vue

<template>
  <div :class="`container-fluid bg-${notification.color}`">
    <div
      class="row justify-content-center align-items-center"
      style="min-height: 90vh"
    >
      <div class="col-12 col-sm-8 col-md-6 col-xl-4">
        <div :class="`card bg-white border-0`">
          <div class="card-body">
            <div class="text-center w-100">
              <img
                :src="require(`@/assets/${notification.picture}`)"
                alt="Picture"
              />
            </div>
            <h1 class="text-center mt-3">{{ notification.title }}</h1>
            <p class="text-muted text-center">{{ notification.subtitle }}</p>
            <div
              v-if="notification.status === 'pending'"
              class="progress"
              style="height: 10px"
            >
              <div
                class="progress-bar progress-bar-striped progress-bar-animated"
                role="progressbar"
                aria-valuenow="100"
                aria-valuemin="0"
                aria-valuemax="100"
                style="width: 100%"
              ></div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { Options, Vue } from "vue-class-component";
import { LocationQuery } from "vue-router";

import { AuthService } from "@/services/auth.service";

enum STATUS {
  PENDING = "pending",
  COMPLETED = "completed",
  ERROR = "error",
}

@Options({})
export default class VerifyEmail extends Vue {
  notification: Record<string, string | boolean> | null = {
    picture: "picture-one.png",
    title: "Email verification",
    subtitle: "Your request is being processed.",
    status: STATUS.PENDING,
  };

  created(): void {
    // We carry out the verification.
    this.verifyAccount(this.$route.query);
  }

  private async verifyAccount(query: LocationQuery): Promise<void> {
    try {
      // Instance class.
      const { verifySignUp } = new AuthService();
      // Send request to the server.
      const user = await verifySignUp((query.token as string) || "");
      // Show sucess message.
      this.notification = {
        picture: "picture-three.png",
        title: "Verified account",
        subtitle: `${user.fullname} Your account has been verified successfully`,
        color: "primary",
        status: STATUS.ERROR,
      };
    } catch (error) {
      // Show failure message.
      this.notification = {
        picture: "picture-two.png",
        title: "Error",
        subtitle: error.message || "An error occurred during the operation.",
        color: "danger",
        status: STATUS.ERROR,
      };
    }
  }
}
</script>

<style scoped>
.card {
  border-radius: 20px;
}
img {
  width: 100px;
  height: 100px;
  object-fit: contain;
}
</style>

```

We add the page to the vue routing.

```typescript
src > router > index.ts

const routes: Array<RouteRecordRaw> = [
  {
    path: "/verifyEmail",
    name: "Verify-Email",
    component: () => import("@/views/VerifyEmail.vue"),
  },
  // Mores pages...
];

```

Now, it is time to test what we have done.
1. We create an account.
2. We get a link to verify the email.
3. We click on the link, and the email is verified.
4. We received a confirmation email.

![Previews email verification](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ws0wjzm222pkxxu6lk23.png)

Excellent! We have finished the client side verification with Vuejs. In the next article, we will do it with Angular. So don't miss it. Bye.

### Articles previosly.

How to setup email verification in Feathers.js
- Article: https://dev.to/ivanz123/how-to-setup-email-verification-in-feather-js-and-node-js-4had

- Repository: https://github.com/IvanZM123/feathers-email-verification

In case you have any questions, I leave you the source code: https://github.com/IvanZM123/feathers-email-verification-vue

### Follow me on social networks.

- 🎉 Twitter: https://twitter.com/ToSatn2
- 💡 Github: https://github.com/IvanZM123