67-Nodejs Course 2023: Auth: Access Token When Login

hassanzohdy

Hasan Zohdy

Posted on November 18, 2022

67-Nodejs Course 2023: Auth: Access Token When Login

So we have seen how we created Current User utility, which is a powerful one, but that one was only with the Guest user. Now we will see how we can create a Current User utility with the Logged In user.

How this works

So let's see how this works first, we'll first check if the user is passing his/her credentials, if yes, then we'll check if the credentials are correct or not, if yes, then we'll create a JWT token and we'll pass the user payload to JWT when generating the token, this will be used to identify the user when he/she will make a request to the server again with the JWT token.

Login Controller

Our login controller now looks like:

// src/app/users/controllers/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",
    });
  }

  console.log("Logged in successfully");

  // generate access token

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

As you can see, there is a comment in the controller generate access token, but sadly there is no generated token 🥺, so let's generate the token.

We already have our own jwt which can generate a new token, that's what we'll do right now.

Generate Access Token

So let's create a new method in the User model, which will generate a new token for the user.

// src/app/users/controllers/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",
  });
}
Enter fullscreen mode Exit fullscreen mode

Here we generated a new token using jwt.generate method, and we passed the user payload to the jwt.generate method, so that we can identify the user when he/she will make a request to the server again with the JWT token.

Now let's give it a try, and see if it works or not.

Don't forget that the /login requires a Bearer Token, which will be in this case the Guest access token.

Now open Postman and make a POST request to the /login route, and pass the email and password in the request body.

Don't forget to pass the Authorization header with the Bearer token.

You should see something like this:

User resposne

From now on, we can use this access token to make requests to the server, now let's see if it works.

Logged In User With list users

Now let's update the list users request to make it only accessible to the logged in user only.

We already have authMiddleware added in the /users request, but we need to tell it allow only the user user type, so let's modify that middleware to receive a userType parameter, and we'll use that parameter to check if the user is of the same type or not.

If no arguments passed, it will be accessible to all the user types.

// src/core/auth/auth-middleware.ts
import config from "@mongez/config";
import { Request } from "core/http/request";
import { Response } from "core/http/response";
import { setCurrentUser } from "./current-user";
import jwt from "./jwt";

export async function authMiddleware(request: Request, response: Response) {
  try {
    // use our own jwt verify to verify the token
    await jwt.verify();
    // get current user
    const user: any = request.baseRequest.user;

    // now, we need to get an instance of user using its corresponding model
    const userType = user.userType;
    // get user model class
    const UserModel = config.get(`auth.userType.${userType}`);

    // get user model instance
    const currentUser = await UserModel.findBy("_id", user._id);

    // set current user
    setCurrentUser(currentUser);
  } catch (err) {
    // unset current user
    setCurrentUser(undefined);
    return response.badRequest({
      error: "Unauthorized: Invalid Access Token",
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

This is the current state of the middleware file, what're going to do now is what we call Higher Order Function

Higher Order Function

A higher-order function is a function that does at least one of the following:

  • Take one or more functions as an input
  • Output/Return a function

It can be one of these cases or both.

In our case, we're going to create a function that takes a userType parameter, and returns a function that will be used as a middleware.

// src/core/auth/auth-middleware.ts
import config from "@mongez/config";
import { Request } from "core/http/request";
import { Response } from "core/http/response";
import { setCurrentUser } from "./current-user";
import jwt from "./jwt";

export function authMiddleware(allowedUserType?: string) {
  return async function auth(request: Request, response: Response) {
    try {
      // use our own jwt verify to verify the token
      await jwt.verify();
      // get current user
      const user: any = request.baseRequest.user;

      // now, we need to get an instance of user using its corresponding model
      const userType = user.userType;

      // check if the user type is allowed
      if (allowedUserType && userType !== allowedUserType) {
        return response.unauthorized({
          error: "You are not allowed to access this resource",
        });
      }

      // get user model class
      const UserModel = config.get(`auth.userType.${userType}`);

      // get user model instance
      const currentUser = await UserModel.findBy("_id", user._id);

      // set current user
      setCurrentUser(currentUser);
    } catch (err) {
      // unset current user
      setCurrentUser(undefined);
      return response.unauthorized({
        error: "Unauthorized: Invalid Access Token",
      });
    }
  };
}
Enter fullscreen mode Exit fullscreen mode

Here we moved the auth middleware function into the function and called it auth, this is a kind of nested functions, a function inside a function and we also returned it. the authMiddleware function now can receive a userType parameter, and it will return a function that will be used as a middleware.

Then we checked if the allowedUserType is passed, and if it's passed, we checked if the user type is the same as the allowedUserType, if not, we'll return an unauthorized response.

Now let's update the /users route to use the authMiddleware with the user user type.

// src/app/users/routes.ts
import { authMiddleware } from "core/auth/auth-middleware";
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";

router.get("/users", usersList, {
  middleware: [authMiddleware("user")],
});

router.get("/users/:id", getUser);
router.post("/users", createUser);
router.post("/login", login);
Enter fullscreen mode Exit fullscreen mode

Now let's try to make a request to the /users route, and see if it works or not.

Try first with no Authorization header, you should see a unauthorized response.

Then try it with guest access token, you should see an unauthorized response.

Now try it with user access token that we got from /login request, you should see a list of users.

Current User as Logged In User

Now we can use the user function to get the current user, and we can use it in the usersList controller.

// src/app/users/controllers/users-list.ts
import { user } from "core/auth/current-user";
import database from "core/database";
import { Request } from "core/http/request";

export default async function usersList(request: Request) {
  const usersCollection = database.collection("users");

  // log the current user

  const users = await usersCollection.find({}).toArray();

  console.log(user());

  return {
    users,
  };
}
Enter fullscreen mode Exit fullscreen mode

You should see the logged in user in the console, you should see the logged in user in the console.

Logged In User

🎨 Conclusion

In this article, we learned how to generate a JWT for logged in user, and also how to make a middleware to protect routes based on user type.

☕♨️ Buy me a Coffee ♨️☕

If you enjoy my articles and see it useful to you, you may buy me a coffee, 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

😍 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 18, 2022

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

Sign up to receive the latest update from our blog.

Related