From 0bd08309f6f15ff622c2ce46731b84faef2a2c0e Mon Sep 17 00:00:00 2001 From: Francisco Cardoso Date: Wed, 8 Feb 2023 19:29:19 +0000 Subject: [PATCH] Adding custom errors for token validation Co-authored-by: Daniel Ferreira --- src/api/middleware/application.js | 5 +++-- src/api/middleware/auth.js | 19 ++++++++++--------- src/api/middleware/validators/application.js | 6 +----- .../validators/validationReasons.js | 3 +++ src/lib/emailService.js | 12 ++++++++++-- src/lib/token.js | 13 ++++++++++--- src/models/CompanyApplication.js | 5 ++++- src/services/application.js | 5 +++-- 8 files changed, 44 insertions(+), 24 deletions(-) diff --git a/src/api/middleware/application.js b/src/api/middleware/application.js index 316cd4cd..904ef3b7 100644 --- a/src/api/middleware/application.js +++ b/src/api/middleware/application.js @@ -1,9 +1,10 @@ import CompanyApplication, { CompanyApplicationRules } from "../../models/CompanyApplication.js"; - +import { APIError, ErrorTypes } from "./errorHandler.js"; +import { StatusCodes as HTTPStatus } from "http-status-codes/build/cjs/status-codes.js"; export const exceededCreationTimeLimit = async (email) => { const cursor = await CompanyApplication.findOne({ email, isVerified: false }).exec(); if (cursor !== null && Date.now() - cursor.submittedAt < 5000 * 60) { - throw new Error(CompanyApplicationRules.APPLICATION_RECENTLY_CREATED.msg); + throw new APIError(HTTPStatus.FORBIDDEN, ErrorTypes.FORBIDDEN, CompanyApplicationRules.APPLICATION_RECENTLY_CREATED); } return true; }; diff --git a/src/api/middleware/auth.js b/src/api/middleware/auth.js index a469b1e5..48c5539c 100644 --- a/src/api/middleware/auth.js +++ b/src/api/middleware/auth.js @@ -78,15 +78,16 @@ export const hasAdminPrivileges = async (req, res, next) => { }; export const validToken = (req, res, next) => { - const decoded = verifyAndDecodeToken(req.params.token, config.jwt_secret); - - if (!decoded) { - return next(new APIError(HTTPStatus.FORBIDDEN, ErrorTypes.FORBIDDEN, ValidationReasons.INVALID_TOKEN)); - } + try { + const decoded = verifyAndDecodeToken(req.params.token, config.jwt_secret, next); - storeInLocals(req, { - token: decoded, - }); + storeInLocals(req, { + token: decoded, + }); - return next(); + return next(); + } catch (err) { + console.log(err); + return next(err); + } }; diff --git a/src/api/middleware/validators/application.js b/src/api/middleware/validators/application.js index d5485291..c78203e2 100644 --- a/src/api/middleware/validators/application.js +++ b/src/api/middleware/validators/application.js @@ -119,10 +119,6 @@ export const token = param("token", ValidationReasons.DEFAULT) .isString().withMessage(ValidationReasons.STRING) .trim(); -export const confirmValidation = useExpressValidators([ - token, -]); - export const finishValidation = useExpressValidators([ - token, + token.exists().withMessage(ValidationReasons.NON_EXISTING_APPLICATION).bail(), ]); diff --git a/src/api/middleware/validators/validationReasons.js b/src/api/middleware/validators/validationReasons.js index 0b438f3c..b32e0369 100644 --- a/src/api/middleware/validators/validationReasons.js +++ b/src/api/middleware/validators/validationReasons.js @@ -41,6 +41,7 @@ const ValidationReasons = Object.freeze({ OFFER_EDIT_PERIOD_OVER: (value) => `offer-edit-period-over:${value}-hours`, INVALID_QUERY_TOKEN: "invalid-query-token", INVALID_TOKEN: "invalid-token", + EXPIRED_TOKEN: "expired-token", JOB_MIN_DURATION_NOT_SPECIFIED: "job-max-duration-requires-job-min-duration", REGISTRATION_FINISHED: "registration-already-finished", REGISTRATION_NOT_FINISHED: "registration-not-finished-yet", @@ -48,6 +49,8 @@ const ValidationReasons = Object.freeze({ IMAGE_FORMAT: "formats-supported-png-jpeg-jpg", OFFER_BLOCKED_ADMIN: "offer-blocked-by-admin", OFFER_HIDDEN: "offer-is-hidden", + ALREADY_VALIDATED: "application-already-validated", + NON_EXISTING_APPLICATION: "application-does-not-exist", FILE_TOO_LARGE: (max) => `file-cant-be-larger-than-${max}MB` }); diff --git a/src/lib/emailService.js b/src/lib/emailService.js index d48e7a58..2043cede 100644 --- a/src/lib/emailService.js +++ b/src/lib/emailService.js @@ -6,7 +6,7 @@ export class EmailService { async init({ email: user, password: pass }) { this.email = user; - const transporter = await nodemailer.createTransport({ + /* const transporter = await nodemailer.createTransport({ pool: true, host: "smtp.gmail.com", port: 465, @@ -17,7 +17,15 @@ export class EmailService { }, connectionTimeout: 30000 }); - console.log("transporter"); + console.log("transporter");*/ + const transporter = nodemailer.createTransport({ + host: "smtp.ethereal.email", + port: 587, + auth: { + user: "naomie.koelpin2@ethereal.email", + pass: "NGEVbMnTZzyA3MQD3V" + } + }); transporter.use("compile", hbs({ viewEngine: { diff --git a/src/lib/token.js b/src/lib/token.js index 171906bf..5728271a 100644 --- a/src/lib/token.js +++ b/src/lib/token.js @@ -1,4 +1,7 @@ import jwt from "jsonwebtoken"; +import { APIError, ErrorTypes } from "../api/middleware/errorHandler.js"; +import { StatusCodes as HTTPStatus } from "http-status-codes/build/cjs/status-codes.js"; +import ValidationReasons from "../api/middleware/validators/validationReasons.js"; export const generateToken = (data, secret, expiresInSeconds) => jwt.sign( { ...data }, @@ -6,10 +9,14 @@ export const generateToken = (data, secret, expiresInSeconds) => jwt.sign( { expiresIn: `${expiresInSeconds} seconds`, algorithm: "HS256" } ); -export const verifyAndDecodeToken = (token, secret) => { +export const verifyAndDecodeToken = (token, secret, next) => { try { return jwt.verify(token, secret, { algorithm: "HS256" }); - } catch (err) { - return null; + } catch (jwtErr) { + if (jwtErr.name === "TokenExpiredError") { + return next(new APIError(HTTPStatus.FORBIDDEN, ErrorTypes.FORBIDDEN, ValidationReasons.EXPIRED_TOKEN)); + } else { + return next(new APIError(HTTPStatus.FORBIDDEN, ErrorTypes.FORBIDDEN, ValidationReasons.INVALID_TOKEN)); + } } }; diff --git a/src/models/CompanyApplication.js b/src/models/CompanyApplication.js index ef75a732..c51b4b47 100644 --- a/src/models/CompanyApplication.js +++ b/src/models/CompanyApplication.js @@ -2,6 +2,9 @@ import mongoose from "mongoose"; import ApplicationStatus from "./constants/ApplicationStatus.js"; import CompanyApplicationConstants from "./constants/CompanyApplication.js"; import { checkDuplicatedEmail } from "../api/middleware/validators/validatorUtils.js"; +import { APIError, ErrorTypes } from "../api/middleware/errorHandler.js"; +import { StatusCodes as HTTPStatus } from "http-status-codes/build/cjs/status-codes.js"; +import ValidationReasons from "../api/middleware/validators/validationReasons.js"; const { Schema } = mongoose; @@ -161,7 +164,7 @@ export const isRejectable = (application) => { CompanyApplicationSchema.methods.companyValidation = function() { - if (this.isVerified) throw new Error("Application was already validated by the company"); + if (this.isVerified) throw new APIError(HTTPStatus.FORBIDDEN, ErrorTypes.FORBIDDEN, ValidationReasons.ALREADY_VALIDATED); this.isVerified = true; return this.save({ validateModifiedOnly: true }); }; diff --git a/src/services/application.js b/src/services/application.js index afb5b177..87feed79 100644 --- a/src/services/application.js +++ b/src/services/application.js @@ -5,6 +5,7 @@ import { RECOVERY_LINK_EXPIRATION } from "../models/constants/ApplicationStatus. import { APPLICATION_CONFIRMATION } from "../email-templates/companyApplicationConfirmation.js"; import AccountService from "./account.js"; import EmailService from "../lib/emailService.js"; +import { StatusCodes as HTTPStatus } from "http-status-codes/build/cjs/status-codes.js"; import { NEW_COMPANY_APPLICATION_ADMINS, NEW_COMPANY_APPLICATION_COMPANY, @@ -12,6 +13,8 @@ import { REJECTION_NOTIFICATION, } from "../email-templates/companyApplicationApproval.js"; import config from "../config/env.js"; +import { APIError, ErrorTypes } from "../api/middleware/errorHandler.js"; +import ValidationReasons from "../api/middleware/validators/validationReasons.js"; export class CompanyApplicationNotFound extends Error { constructor(msg) { @@ -44,13 +47,11 @@ class CompanyApplicationService { submittedAt: Date.now(), isVerified: false, }); - console.log("criar"); const link = this.buildConfirmationLink(application._id); await EmailService.sendMail({ to: email, ...APPLICATION_CONFIRMATION(link), }); - console.log("Criado"); return application.toObject(); }