64-Nodejs Course 2023: Auth: Logged In User

hassanzohdy

Hasan Zohdy

Posted on November 14, 2022

64-Nodejs Course 2023: Auth: Logged In User

We have seen the access token being generated and verified then stored in database, now let's generate an access token for the user when he hits the login route.

Login Route

Let's create a new route in src/app/users/routes.ts and create anew handler/controller for it (src/app/userss/controllers/auth/login.ts), notice that i added a new folder called auth to hold all the authentication related controllers to be in one folder like login, register, logout, forgot password, reset password etc.

// src/app/users/routes.ts
import login from './controllers/auth/login.ts';
// ...

router.post('/login', login);
Enter fullscreen mode Exit fullscreen mode
// src/app/users/controllers/auth/login.ts
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"]);
}
Enter fullscreen mode Exit fullscreen mode

Now let's get the user from the database using the email and check if the password is correct, if it is correct then we will generate a new access token for the user and store it in the database.

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

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",
    });
  }

  console.log("Logged in successfully");

  // generate access token
  return response.success({
    user: user.data,
  });
}
Enter fullscreen mode Exit fullscreen mode

Here we imported the user model and called a method called attempt which will check if the user exists and the password is correct, if it is correct then it will return the user, if not then it will return null.

So let's create this static method first.

As you see, we passed an object with the email and password, why its an object? because we may use another thing than the email, for example login by username or phone number, so we can pass the email or username or phone number in the object and check if the user exists and the password is correct.

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

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

  /**
   * {@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;
  }
}
Enter fullscreen mode Exit fullscreen mode

Let's split the code into parts:

  • First we created a static method called attempt
  • This method receives an object contains the data that will be used for validation.
  • As it is an object, then the best method to get a user for object of columns is first method, so we called it and passed the object to it, but we need to exclude the password from the object because we don't want to search for the user by the password.
  • If the user exists then we will verify the password, if the password is correct then we will return the user, if not then we will return null.

We're using verify function to verify the given password with the ciphered one in the database, if it is not verified return null, otherwise return the user model object.

Now let's create a new user with your email and password 123456 (Just only for testing), then let's test the attempt method.

// src/app/users/routes.tsimport { connection } from "core/database";
import router from "core/router";
import login from "./controllers/auth/login";
import createUser from "./controllers/create-user";
import getUser from "./controllers/get-user";
import usersList from "./controllers/users-list";
import User from "./models/user";

router.get("/users", usersList);
router.get("/users/:id", getUser);
router.post("/users", createUser);
router.post("/login", login);

connection.on("connected", async () => {
  const user = await User.create({
    email: "hassanzohdy@gmail.com",
    password: "123456",
  });

  console.log(user.data);
});
Enter fullscreen mode Exit fullscreen mode

Now you should see in the console something similar to this:

Created User

Thanks to our amazing casts property, the password is auto hashed and stored in the database.

Now let's test our attempt method:

Open postman and send a POST request to http://localhost:3000/login with the following body:

Don't forget to added the Authorization header with your Bearer {token}.

And in the Body tab, select form data or json, whatever you like, i prefer form data for ease of reading and use and put your email and password.

If you entered the data correctly, you should see in the console Logged in successfully and user data will be returned, if not you should see in the postman an error object with message Invalid credentials.

Generating Access Token

Now, we need to generate the access token for current user, but if you remember, generating the token was done in the registerAuthRoutes function, in that sense, we really need to pluck it form there, this is going to be our next article Inshaa Allah.

🎨 Conclusion

In this article, we saw the login workflow and how to use the attempt method to verify user credentials, and how we made it more flexible and dynamic.

🚀 Project Repository

You can find the latest updates of this project on Github

😍 Join our community

Join our community on Discord 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, 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

Packages & Libraries

React Js Packages

Courses (Articles)

💖 💪 🙅 🚩
hassanzohdy
Hasan Zohdy

Posted on November 14, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related