From e71faab04e83fed1729e4c778049fcb21e9f07f0 Mon Sep 17 00:00:00 2001 From: hanoak20 Date: Mon, 22 Jan 2024 19:13:32 +0530 Subject: [PATCH 1/2] core: added express-validator, validatorMiddleware, and added validations to auth route --- package.json | 1 + src/constants/index.ts | 17 ++++++ src/middlewares/validation.middleware.ts | 16 ++++++ src/routes/auth.routes.ts | 16 +++++- src/utils/custom-errors.utils.ts | 2 +- src/validators/auth.validator.ts | 67 ++++++++++++++++++++++++ 6 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 src/middlewares/validation.middleware.ts create mode 100644 src/validators/auth.validator.ts diff --git a/package.json b/package.json index a17956ebd..3437c87dc 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "cors": "^2.8.5", "dotenv": "^16.3.1", "express": "^4.18.2", + "express-validator": "^7.0.1", "express-winston": "^4.2.0", "helmet": "^6.0.1", "jsonwebtoken": "^9.0.2", diff --git a/src/constants/index.ts b/src/constants/index.ts index 9a936cd2a..3eabc5098 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -9,7 +9,16 @@ export type HttpErrorCodes = { MOVED_PERMANENTLY: number; SUPPORT_DOC: number; SERVER_ERROR: number; + UNPROCESSABLE_CONTENT: number; }; + +export type ValidationErrors = { + INVALID_EMAIL: string; + EMAIL_LIMIT: string; + STRING_REQUIRED: string; + INVALID_REGION: string; +}; + export type ConstantType = { CS_REGIONS: Array; AXIOS_TIMEOUT: number; @@ -17,6 +26,7 @@ export type ConstantType = { HTTP_TEXTS: HttpErrorTexts; HTTP_RESPONSE_HEADERS: HttpResponseHeaders; METHODS_TO_INCLUDE_DATA_IN_AXIOS: Array; + VALIDATION_ERRORS: ValidationErrors; }; export type HttpErrorTexts = { @@ -49,6 +59,7 @@ export const constants: ConstantType = { MOVED_PERMANENTLY: 301, SUPPORT_DOC: 294, SERVER_ERROR: 500, + UNPROCESSABLE_CONTENT: 422, }, HTTP_TEXTS: { INTERNAL_ERROR: "Internal server error, please try again later.", @@ -66,4 +77,10 @@ export const constants: ConstantType = { Connection: "close", }, METHODS_TO_INCLUDE_DATA_IN_AXIOS: ["PUT", "POST", "DELETE", "PATCH"], + VALIDATION_ERRORS: { + INVALID_EMAIL: "Given email ID is invalid.", + EMAIL_LIMIT: "Email's max limit reached.", + STRING_REQUIRED: "Provided $ should be a string.", + INVALID_REGION: "Provided region doesn't exists.", + }, }; diff --git a/src/middlewares/validation.middleware.ts b/src/middlewares/validation.middleware.ts new file mode 100644 index 000000000..446ec6146 --- /dev/null +++ b/src/middlewares/validation.middleware.ts @@ -0,0 +1,16 @@ +// middleware/authentication.middleware.ts +import { Request, Response, NextFunction } from "express"; +import { validationResult } from "express-validator"; +import { ValidationError } from "../utils/custom-errors.utils"; + +export const validationMiddleware = ( + req: Request, + res: Response, + next: NextFunction +) => { + const errors = validationResult(req); + + if (!errors.isEmpty()) throw new ValidationError(errors.array()[0].msg); + + return next(); +}; diff --git a/src/routes/auth.routes.ts b/src/routes/auth.routes.ts index 3532a251e..d179f4c80 100644 --- a/src/routes/auth.routes.ts +++ b/src/routes/auth.routes.ts @@ -1,13 +1,25 @@ import express from "express"; import { authController } from "../controllers/auth.controller"; import { asyncRouter } from "../utils/async-router.utils"; +import authValidator from "../validators/auth.validator"; +import { validationMiddleware } from "../middlewares/validation.middleware"; const router = express.Router(); // Login route -router.post("/user-session", asyncRouter(authController.login)); +router.post( + "/user-session", + authValidator, + validationMiddleware, + asyncRouter(authController.login) +); // SMS token route -router.post("/request-token-sms", asyncRouter(authController.RequestSms)); +router.post( + "/request-token-sms", + authValidator, + validationMiddleware, + asyncRouter(authController.RequestSms) +); export default router; diff --git a/src/utils/custom-errors.utils.ts b/src/utils/custom-errors.utils.ts index 82e4e8fba..b5323b2b8 100644 --- a/src/utils/custom-errors.utils.ts +++ b/src/utils/custom-errors.utils.ts @@ -27,7 +27,7 @@ export class DatabaseError extends AppError { export class ValidationError extends AppError { constructor(message: string = "User validation error") { - super(constants.HTTP_CODES.FORBIDDEN, message); + super(constants.HTTP_CODES.UNPROCESSABLE_CONTENT, message); } } diff --git a/src/validators/auth.validator.ts b/src/validators/auth.validator.ts new file mode 100644 index 000000000..3add739e4 --- /dev/null +++ b/src/validators/auth.validator.ts @@ -0,0 +1,67 @@ +import { checkSchema } from "express-validator"; +import { constants } from "../constants"; + +export default checkSchema({ + email: { + in: "body", + isString: { + errorMessage: constants.VALIDATION_ERRORS.STRING_REQUIRED.replace( + "$", + "Email" + ), + bail: true, + }, + isEmail: { + errorMessage: constants.VALIDATION_ERRORS.INVALID_EMAIL, + bail: true, + }, + trim: true, + isLength: { + errorMessage: constants.VALIDATION_ERRORS.EMAIL_LIMIT, + options: { + min: 3, + max: 350, + }, + bail: true, + }, + }, + password: { + in: "body", + isString: { + errorMessage: constants.VALIDATION_ERRORS.STRING_REQUIRED.replace( + "$", + "Password" + ), + bail: true, + }, + trim: true, + }, + region: { + in: "body", + isString: { + errorMessage: constants.VALIDATION_ERRORS.STRING_REQUIRED.replace( + "$", + "Region" + ), + bail: true, + }, + trim: true, + isIn: { + options: [constants.CS_REGIONS], + errorMessage: constants.VALIDATION_ERRORS.INVALID_REGION, + bail: true, + }, + }, + tfa_token: { + optional: true, + in: "body", + isString: { + errorMessage: constants.VALIDATION_ERRORS.STRING_REQUIRED.replace( + "$", + "2FA Token" + ), + bail: true, + }, + trim: true, + }, +}); From a555ffddb41df54a9c3a3f82db0f4fd465fc30ec Mon Sep 17 00:00:00 2001 From: hanoak20 Date: Tue, 23 Jan 2024 14:38:03 +0530 Subject: [PATCH 2/2] chore: removed the validation middleware & added a common centralised validator. --- src/middlewares/validation.middleware.ts | 16 ---------------- src/routes/auth.routes.ts | 9 +++------ src/validators/index.ts | 21 +++++++++++++++++++++ 3 files changed, 24 insertions(+), 22 deletions(-) delete mode 100644 src/middlewares/validation.middleware.ts create mode 100644 src/validators/index.ts diff --git a/src/middlewares/validation.middleware.ts b/src/middlewares/validation.middleware.ts deleted file mode 100644 index 446ec6146..000000000 --- a/src/middlewares/validation.middleware.ts +++ /dev/null @@ -1,16 +0,0 @@ -// middleware/authentication.middleware.ts -import { Request, Response, NextFunction } from "express"; -import { validationResult } from "express-validator"; -import { ValidationError } from "../utils/custom-errors.utils"; - -export const validationMiddleware = ( - req: Request, - res: Response, - next: NextFunction -) => { - const errors = validationResult(req); - - if (!errors.isEmpty()) throw new ValidationError(errors.array()[0].msg); - - return next(); -}; diff --git a/src/routes/auth.routes.ts b/src/routes/auth.routes.ts index d179f4c80..bf162718e 100644 --- a/src/routes/auth.routes.ts +++ b/src/routes/auth.routes.ts @@ -1,24 +1,21 @@ import express from "express"; import { authController } from "../controllers/auth.controller"; import { asyncRouter } from "../utils/async-router.utils"; -import authValidator from "../validators/auth.validator"; -import { validationMiddleware } from "../middlewares/validation.middleware"; +import validator from "../validators"; const router = express.Router(); // Login route router.post( "/user-session", - authValidator, - validationMiddleware, + validator("auth"), asyncRouter(authController.login) ); // SMS token route router.post( "/request-token-sms", - authValidator, - validationMiddleware, + validator("auth"), asyncRouter(authController.RequestSms) ); diff --git a/src/validators/index.ts b/src/validators/index.ts new file mode 100644 index 000000000..8a3f8de53 --- /dev/null +++ b/src/validators/index.ts @@ -0,0 +1,21 @@ +import { Request, NextFunction, Response } from "express"; +import { ValidationError } from "../utils/custom-errors.utils"; +import { asyncRouter } from "../utils/async-router.utils"; +import authValidator from "./auth.validator"; + +export default (route: string = "") => + asyncRouter(async (req: Request, res: Response, next: NextFunction) => { + const appValidators = { + auth: authValidator, + }; + + const validator = appValidators[route as keyof typeof appValidators]; + + const result = (await validator.run(req)) + .map((field) => field.array()) + .reduce((acc, val) => [...acc, ...val], []); + + if (result.length) throw new ValidationError(result[0].msg); + + return next(); + });