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,
});
}
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",
});
}
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 theGuest
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 theBearer
token.
You should see something like this:
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",
});
}
}
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",
});
}
};
}
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);
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,
};
}
You should see the logged in user in the console, you should see the logged in user in the console.
🎨 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
- Event Driven Architecture: A Practical Guide in Javascript
- Best Practices For Case Styles: Camel, Pascal, Snake, and Kebab Case In Node And Javascript
- After 6 years of practicing MongoDB, Here are my thoughts on MongoDB vs MySQL
Packages & Libraries
- Collections: Your ultimate Javascript Arrays Manager
- Supportive Is: an elegant utility to check types of values in JavaScript
- Localization: An agnostic i18n package to manage localization in your project
React Js Packages
Courses (Articles)
Top comments (0)