68-Nodejs Course 2023: Auth: Base Auth Model Class
Tags: node, typescript, mongodb, fastify
Published: 2022-11-19 18:42:56

---
title: 68-Nodejs Course 2023: Auth: Base Auth Model Class
published: true
description: 
series: Nodejs Course 2023
tags: nodejs, typescript, mongodb, fastify
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/77i5uf469rhgcj69e4og.png
# Use a ratio of 100:42 for best results.
---

So we saw how to generate an access token when the guest performs a login, let's see a quick peek at the code.

```ts
// src/app/users/controllers/auth/login.ts
import User from "app/users/models/user";
import jwt from "core/auth/jwt";
import { Request } from "core/http/request";
import { Response } from "core/http/response";

export default async function login(request: Request, response: Response) {
  // get the email and password from the request body
  const { email, password } = request.only(["email", "password"]);

  const user = await User.attempt({ email, password });

  if (!user) {
    return response.badRequest({
      error: "Invalid credentials",
    });
  }

  // generate access token
  const token = await jwt.generate({
    ...user.only(["id", "_id"]),
    userType: "user",
  });

  return response.success({
    user: user.data,
    // send the access token to the client
    accessToken: token,
    // send the user type to the client
    userType: "user",
  });
}
```

We did also make it with guests as well, let's have a look

```ts
// src/core/auth/registerAuthRoutes.ts
import { Response } from "core/http/response";
import router from "core/router";
import jwt from "./jwt";
import AccessToken from "./models/access-token";
import Guest from "./models/guest";

export default function registerAuthRoutes() {
  // now let's add a guests route in our routes to generate a guest token to our guests.
  router.post("/guests", async (_request, response: Response) => {
    // generate a new guest first
    const guest = await Guest.create({
      userType: "guest",
    });

    // use our own jwt generator to generate a token for the guest
    const token = await jwt.generate(guest.data);

    AccessToken.create({
      token,
      // get the guest user type, id and _id
      ...guest.only(["id", "userType", "_id"]),
    });

    return response.send({
      accessToken: token,
      // return the user type
      userType: guest.get("userType"),
    });
  });
}
```

You'll notice that, we forgot in the login to store the token in `AccessToken` model, also there is a duplicate in the code when generating a jwt token.

Let's encapsulate the code with `Auth` model class.

```ts
// src/core/auth/models/auth.ts
import { Model } from "core/database";

export default abstract class Auth extends Model {
  /**
   * Get user type
   */
  public abstract get userType(): string;

  /**
   * Generate jwt token
   */
  public async generateAccessToken(): Promise<string> {
    //

    return "";
  }
}
```

Here we used the power of `abstract` concept (You can find it in depth in the OOP Course, link in the article tail), to enforce that this class can not be instantiated, and any child must implement a getter method `userType` to return the user type of its own.

Then we defined `generateAccessToken` method, but it is so far has empty body with an empty string returned, this method should generate an access token, store it in the `AccessToken` model, and return that token.

```ts
// src/core/auth/models/auth.ts
import { Model } from "core/database";
import jwt from "../jwt";
import AccessToken from "./access-token";

export default abstract class Auth extends Model {
  /**
   * Get user type
   */
  public abstract get userType(): string;

  /**
   * Generate jwt token
   */
  public async generateAccessToken(): Promise<string> {
    //
    // store the main data in the data object
    // we need to store the user data in an object
    // that we'll sue to generate the token
    // and also it will be saved in the Access Token model under `user` column
    const data = {
      ...this.only(["id", "_id"]),
      userType: this.userType,
    };

    // use our own jwt generator to generate a token for the guest
    const token = await jwt.generate(data);

    // store token and the auth model data in the access token model
    // note that we didn't make it sync because we don't want to wait for the token to be stored in the database
    // as nothing depends on it
    AccessToken.create({
      token,
      user: data,
    });

    return token;
  }
}
```

Here we created an object that collects the main data that we need, the `id`, `_id` and the `userType`, then we used our own jwt generator to generate a token for our current user, after that we stored the token and the auth model `data` in the access token model.

We grouped the data under `user` field so we don't override the `_id` or `id` of the `AccessToken` collection itself.

Let's now make our `Guest` extends `Auth` class.

```ts
// src/core/auth/models/guest.ts
import Auth from "./auth";

export default class Guest extends Auth {
  /**
   * {@inheritDoc}
   */
  public static collectionName = "guests";

  /**
   * Get user type
   */
  public get userType(): string {
    return "guest";
  }
}
```

We extended the `Auth` class, and implemented the `userType` getter method to return the user type of the guest.

Now let's update our `registerAuthRoutes` function to use the new `generateAccessToken` method.

```ts
// src/core/auth/registerAuthRoutes.ts
import { Response } from "core/http/response";
import router from "core/router";
import Guest from "./models/guest";

export default function registerAuthRoutes() {
  // now let's add a guests route in our routes to generate a guest token to our guests.
  router.post("/guests", async (_request, response: Response) => {
    // generate a new guest first
    const guest = await Guest.create({
      userType: "guest",
    });

    // use our own jwt generator to generate a token for the guest
    const token = await guest.generateAccessToken();

    return response.send({
      accessToken: token,
      // return the user type
      userType: guest.userType,
    });
  });
}
```

Now our code is much cleaner and more readable, and we can use the same method in our `User` model.

```ts
// src/app/users/models/user.ts
import { verify } from "@mongez/password";
import { except } from "@mongez/reinforcements";
import Auth from "core/auth/models/auth";
import castPassword from "core/database/casts/cast-password";
import { Casts, Document } from "core/database/model/types";

export default class User extends Auth {
  /**
   * Collection name
   */
  public static collectionName = "users";

  /**
   * Get user type
   */
  public get userType(): string {
    return "user";
  }

  /**
   * {@inheritDoc}
   */
  public defaultValue: Document = {
    isActive: true,
    isEmailVerified: false,
    isPhoneVerified: false,
  };

  protected casts: Casts = {
    isActive: "boolean",
    isPhoneVerified: "boolean",
    joinDate: "date",
    password: castPassword,
  };

  /**
   * Attempt to login the user
   */
  public static async attempt(data: any) {
    // find first user with the given data, but exclude from it the password
    const user = await this.first(except(data, ["password"]));

    if (!user) {
      return null;
    }

    // now verify the password

    if (!verify(user.get("password"), data.password)) {
      return null;
    }

    return user;
  }
}
```

We extended the `Auth` class, and implemented the `userType` getter method to return the user type of the user.

Actually you know what, the `attempt` method can be used with multiple users, so let's move it to the `Auth` class.

```ts
// src/core/auth/models/auth.ts
// ...
  /**
   * Attempt to login the user
   */
  public static async attempt<T>(
    this: ChildModel<T>,
    data: any,
  ): Promise<T | null> {
    // find first user with the given data, but exclude from it the password
    const user = await this.first(except(data, ["password"]));

    if (!user) {
      return null;
    }

    // now verify the password

    if (!verify((user as Model).get("password"), data.password)) {
      return null;
    }

    return user;
  }
```

Here we moved the `attempt` method to the Auth class, we also as you can remember, did update the definition of `this` in the static method so we can return a proper return type when the login attempt is successful.

Now let's update our login route to use the new `generateAccessToken` method.

```ts
// src/app/users/controllers/auth/login.ts
import User from "app/users/models/user";
import { Request } from "core/http/request";
import { Response } from "core/http/response";

export default async function login(request: Request, response: Response) {
  // get the email and password from the request body
  const { email, password } = request.only(["email", "password"]);

  const user = await User.attempt({ email, password });

  if (!user) {
    return response.badRequest({
      error: "Invalid credentials",
    });
  }

  const token = user.generateAccessToken();

  return response.success({
    user: user.data,
    // send the access token to the client
    accessToken: token,
    // send the user type to the client
    userType: user.userType,
  });
}
```

We did exact same thing here, we used the `attempt` method to attempt to login the user, and if the login attempt is successful, we used the `generateAccessToken` method to generate a token for the user.

> Also don't forget that we return the userType using the `userType` getter method.

## 🎨 Conclusion

We created an `Auth` base model so we can extend from, and we used the `generateAccessToken` method to generate a token for the user, and we moved the `attempt` method to attempt from the `User` model to the `Auth` model so we can use it with multiple users.

## ☕♨️ Buy me a Coffee ♨️☕

If you enjoy my articles and see it useful to you, you may [buy me a coffee](https://www.paypal.com/paypalme/hassanzohdy), it will help me to keep going and keep creating more content.

## 🚀 Project Repository

You can find the latest updates of this project on [Github](https://github.com/hassanzohdy/nodejs-2023)

## 😍 Join our community

Join our community on [Discord](https://discord.gg/pb2vmdfhGf) to get help and support (Node Js 2023 Channel).

## 🎞️ Video Course (Arabic Voice)

If you want to learn this course in video format, you can find it on [Youtube](https://www.youtube.com/playlist?list=PLGO8ntvxgiZMJc7RN2lIq9WmMOlWZGzmz), the course is in Arabic language.

## 📚 Bonus Content 📚

You may have a look at these articles, it will definitely boost your knowledge and productivity.

General Topics

- [Event Driven Architecture: A Practical Guide in Javascript](https://dev.to/hassanzohdy/event-driven-architecture-the-best-paradigm-that-i-love-to-work-with-in-javascript-and-node-js-1gnk)
- [Best Practices For Case Styles: Camel, Pascal, Snake, and Kebab Case In Node And Javascript](https://dev.to/hassanzohdy/best-practices-for-case-styles-camel-pascal-snake-and-kebab-case-in-node-and-javascript-55oi)
- [After 6 years of practicing MongoDB, Here are my thoughts on MongoDB vs MySQL
](https://dev.to/hassanzohdy/after-6-years-of-practicing-mongodb-here-are-my-thoughts-on-mongodb-vs-mysql-574b)

Packages & Libraries

- [Collections: Your ultimate Javascript Arrays Manager](https://dev.to/hassanzohdy/collections-your-ultimate-javascript-array-handler-3o15)
- [Supportive Is: an elegant utility to check types of values in JavaScript](https://dev.to/hassanzohdy/supportive-is-an-elegant-utility-to-check-types-of-values-in-javascript-1b3e)
- [Localization: An agnostic i18n package to manage localization in your project](https://dev.to/hassanzohdy/mongez-localization-the-simplest-way-to-translate-your-website-regardless-your-favorite-framework-4gi3)

 React Js Packages

- [useFetcher: easiest way to fetch data in React Js](https://dev.to/hassanzohdy/usefetcher-the-easiest-way-to-fetch-data-in-react-45o9)

Courses (Articles)

- [OOP In JS And TS From The Very Beginning](https://dev.to/hassanzohdy/jsts-oop-course-1-introduction-2ld)
- [ES6++: The Ultimate Guide to ES6 and Beyond](https://dev.to/hassanzohdy/es6-1-introduction-h3e)
- [React Js: Let"s Create File Manager With React Js and Node Js](https://dev.to/hassanzohdy/lets-create-a-file-manager-from-scratch-with-react-and-typescript-chapter-i-a-good-way-to-expand-your-experience-5g4k)