This is the second part of How to setup email verification in Feathers.js 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.
Create pages.
Login.vue
<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
<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
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.
<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.
<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.
By the way, if you are wondering about the layout, I am using Bootstrap 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.
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.
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
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
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.
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.
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.
- We create an account.
- We get a link to verify the email.
- We click on the link, and the email is verified.
- We received a confirmation email.
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
Top comments (0)