diff --git a/src/controllers/auth.controller.ts b/src/controllers/auth.controller.ts index f1e56f2a..4537961f 100644 --- a/src/controllers/auth.controller.ts +++ b/src/controllers/auth.controller.ts @@ -1,23 +1,17 @@ import { Request, Response } from "express"; -import { userService } from "../services/auth.service"; +import { authService } from "../services/auth.service"; const login = async (req: Request, res: Response) => { - const resp = await userService.login(req); + const resp = await authService.login(req); res.status(resp?.status).json(resp?.data); }; const RequestSms = async (req: Request, res: Response) => { - const resp = await userService.requestSms(req); + const resp = await authService.requestSms(req); res.status(resp.status).json(resp.data); }; -const getUserProfile = async (req: Request, res: Response) => { - const user = await userService.getUserProfile(req); - res.status(200).json(user); -}; - export const authController = { login, RequestSms, - getUserProfile, }; diff --git a/src/controllers/user.controller.ts b/src/controllers/user.controller.ts new file mode 100644 index 00000000..05c00271 --- /dev/null +++ b/src/controllers/user.controller.ts @@ -0,0 +1,12 @@ +import { Request, Response } from "express"; +import { userService } from "../services/user.service"; +import { constants } from "../constants"; + +const getUserProfile = async (req: Request, res: Response) => { + const user = await userService.getUserProfile(req); + res.status(constants.HTTP_CODES.OK).json(user); +}; + +export const userController = { + getUserProfile, +}; diff --git a/src/middlewares/auth.middleware.ts b/src/middlewares/auth.middleware.ts index f8f739fe..ff28ab41 100644 --- a/src/middlewares/auth.middleware.ts +++ b/src/middlewares/auth.middleware.ts @@ -2,28 +2,30 @@ import { Request, Response, NextFunction } from "express"; import jwt from "jsonwebtoken"; import { config } from "../config"; +import { constants } from "../constants"; export const authenticateUser = ( req: Request, res: Response, next: NextFunction ) => { - // authentication logic (check for a valid token) + const status = constants.HTTP_CODES.UNAUTHORIZED; const token = req.get("app_token"); - if (token) { - jwt.verify(token, config.APP_TOKEN_KEY, (err, decoded) => { - if (err) { - return res - .status(401) - .json({ message: "Unauthorized - Invalid token" }); - } - // Attach the decoded token to the request object for later use - (req as any).decodedToken = decoded; + if (!token) + return res + .status(status) + .json({ status, message: "Unauthorized - Token missing" }); - next(); - }); - } else { - return res.status(401).json({ message: "Unauthorized - Token missing" }); - } + jwt.verify(token, config.APP_TOKEN_KEY, (err, payload) => { + if (err) + return res + .status(status) + .json({ status, message: "Unauthorized - Invalid token" }); + + // Attach the payload to the request object for later use + (req as any).body.token_payload = payload; + + next(); + }); }; diff --git a/src/models/types.ts b/src/models/types.ts index 2f407966..5be9656e 100644 --- a/src/models/types.ts +++ b/src/models/types.ts @@ -12,7 +12,7 @@ export interface UserProfile { }; } -export interface MigrationPayload { +export interface AppTokenPayload { region: string; user_id: string; } diff --git a/src/routes/auth.routes.ts b/src/routes/auth.routes.ts index 286f440e..3532a251 100644 --- a/src/routes/auth.routes.ts +++ b/src/routes/auth.routes.ts @@ -1,6 +1,5 @@ import express from "express"; import { authController } from "../controllers/auth.controller"; -import { authenticateUser } from "../middlewares/auth.middleware"; import { asyncRouter } from "../utils/async-router.utils"; const router = express.Router(); @@ -11,14 +10,4 @@ router.post("/user-session", asyncRouter(authController.login)); // SMS token route router.post("/request-token-sms", asyncRouter(authController.RequestSms)); -// Logout route -//TODO: - -// Profile route -router.get( - "/profile", - authenticateUser, - asyncRouter(authController.getUserProfile) -); - export default router; diff --git a/src/routes/user.routes.ts b/src/routes/user.routes.ts new file mode 100644 index 00000000..b69bc2d0 --- /dev/null +++ b/src/routes/user.routes.ts @@ -0,0 +1,10 @@ +import express from "express"; +import { userController } from "../controllers/user.controller"; +import { asyncRouter } from "../utils/async-router.utils"; + +const router = express.Router(); + +// Profile route +router.get("/profile", asyncRouter(userController.getUserProfile)); + +export default router; diff --git a/src/server.ts b/src/server.ts index 5d9283b0..6758cdf3 100644 --- a/src/server.ts +++ b/src/server.ts @@ -4,6 +4,7 @@ import express from "express"; import cors from "cors"; import helmet from "helmet"; import authRoutes from "./routes/auth.routes"; +import userRoutes from "./routes/user.routes"; import projectRoutes from "./routes/projects.routes"; import { errorMiddleware } from "./middlewares/error.middleware"; import loggerMiddleware from "./middlewares/logger.middleware"; @@ -29,6 +30,7 @@ try { // Routes app.use("/v2/auth", authRoutes); + app.use("/v2/user", authenticateUser, userRoutes); app.use("/v2/org/:orgId/project", authenticateUser, projectRoutes); //For unmatched route patterns diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts index 2418c675..ca89ba42 100644 --- a/src/services/auth.service.ts +++ b/src/services/auth.service.ts @@ -2,90 +2,79 @@ import { Request } from "express"; import { config } from "../config"; import { safePromise } from "../utils/index"; import https from "../utils/https.utils"; -import * as fs from "fs/promises"; -import { - LoginServiceType, - MigrationPayload, - UserProfile, -} from "../models/types"; +import { LoginServiceType, AppTokenPayload } from "../models/types"; import { constants } from "../constants"; import { generateToken } from "../utils/jwt.utils"; import { BadRequestError, InternalServerError, } from "../utils/custom-errors.utils"; +import AuthenticationModel from "../models/authentication"; const login = async (req: Request): Promise => { - //TODO: 1. request validation, 2. saving the authtoken in DB + //TODO: 1. request validation const userData = req?.body; - try { - const [err, res] = await safePromise( - https({ - method: "POST", - url: `${config.CS_API[ - userData?.region as keyof typeof config.CS_API - ]!}/user-session`, - headers: { - "Content-Type": "application/json", - }, - data: { - user: { - email: userData?.email, - password: userData?.password, - ...(userData?.tfa_token && { tfa_token: userData?.tfa_token }), - }, + const [err, res] = await safePromise( + https({ + method: "POST", + url: `${config.CS_API[ + userData?.region as keyof typeof config.CS_API + ]!}/user-session`, + headers: { + "Content-Type": "application/json", + }, + data: { + user: { + email: userData?.email, + password: userData?.password, + ...(userData?.tfa_token && { tfa_token: userData?.tfa_token }), }, - }) - ); - - if (err) - return { - data: err?.response?.data, - status: err?.response?.status, - }; - - if (res?.status === constants.HTTP_CODES.SUPPORT_DOC) - return { - data: res?.data, - status: res?.status, - }; - - if (!res?.data?.user) - throw new BadRequestError(constants.HTTP_TEXTS.NO_CS_USER); + }, + }) + ); - const migration_payload: MigrationPayload = { - region: userData?.region, - user_id: res?.data?.user.uid, + if (err) + return { + data: err?.response?.data, + status: err?.response?.status, }; - // JWT token generation - const app_token = generateToken(migration_payload); - const response = { - data: { - message: constants.HTTP_TEXTS.SUCCESS_LOGIN, - app_token, - }, - status: constants.HTTP_CODES.OK, + if (res?.status === constants.HTTP_CODES.SUPPORT_DOC) + return { + data: res?.data, + status: res?.status, }; - // Write the data to a JSON file (e.g., tokens.json) - //TODO: remove this temp localStorage file, and use DB instead - await fs.writeFile( - "tokens.json", - JSON.stringify( - { - [app_token]: res?.data.user?.authtoken, - }, - null, - 2 - ) - ); - - return response; - } catch (err) { - throw new InternalServerError(); - } + if (!res?.data?.user) + throw new BadRequestError(constants.HTTP_TEXTS.NO_CS_USER); + + const appTokenPayload: AppTokenPayload = { + region: userData?.region, + user_id: res?.data?.user.uid, + }; + + // Saving auth info in the DB + await AuthenticationModel.findOneAndUpdate( + appTokenPayload, + { + authtoken: res?.data.user?.authtoken, + }, + { + upsert: true, + } + ); + + // JWT token generation + const app_token = generateToken(appTokenPayload); + + return { + data: { + message: constants.HTTP_TEXTS.SUCCESS_LOGIN, + app_token, + }, + status: constants.HTTP_CODES.OK, + }; }; const requestSms = async (req: Request): Promise => { @@ -123,48 +112,7 @@ const requestSms = async (req: Request): Promise => { } }; -const getUserProfile = async ( - req: Request -): Promise => { - try { - //TODO: replace the current logic with the actual db-fetch logic - const tokens = JSON.parse(await fs.readFile(`tokens.json`, "utf8")); - const authtoken = tokens?.[req?.headers?.app_token as string]; - - const apiResponse = - authtoken && - (await https({ - method: "GET", - url: `${config.CS_API.US}/user?include_orgs_roles=true`, - headers: { - "Content-Type": "application/json", - authtoken: authtoken, - }, - })); - - if (!apiResponse?.data?.user) - throw new BadRequestError(constants.HTTP_TEXTS.NO_CS_USER); - - const orgs = apiResponse?.data?.user?.organizations - ?.filter((org: any) => org?.org_roles?.some((item: any) => item.admin)) - ?.map(({ uid, name }: any) => ({ org_id: uid, org_name: name })); - - const userProfile: UserProfile = { - user: { - email: apiResponse?.data?.user?.email, - first_name: apiResponse?.data?.user?.first_name, - last_name: apiResponse?.data?.user?.last_name, - orgs: orgs, - }, - }; - return userProfile; - } catch (error) { - throw new InternalServerError("Error while getting user profile"); - } -}; - -export const userService = { +export const authService = { login, requestSms, - getUserProfile, }; diff --git a/src/services/user.service.ts b/src/services/user.service.ts new file mode 100644 index 00000000..89110f20 --- /dev/null +++ b/src/services/user.service.ts @@ -0,0 +1,50 @@ +import { Request } from "express"; +import { config } from "../config"; +import https from "../utils/https.utils"; +import { AppTokenPayload, UserProfile } from "../models/types"; +import { constants } from "../constants"; +import { BadRequestError } from "../utils/custom-errors.utils"; +import AuthenticationModel from "../models/authentication"; + +const getUserProfile = async (req: Request): Promise => { + const appTokenPayload: AppTokenPayload = req?.body?.token_payload; + + const user = await AuthenticationModel.findOne({ + user_id: appTokenPayload?.user_id, + region: appTokenPayload?.region, + }).lean(); + + if (!user?.authtoken) + throw new BadRequestError(constants.HTTP_TEXTS.NO_CS_USER); + + const res = await https({ + method: "GET", + url: `${config.CS_API[ + appTokenPayload?.region as keyof typeof config.CS_API + ]!}/user?include_orgs_roles=true`, + headers: { + "Content-Type": "application/json", + authtoken: user?.authtoken, + }, + }); + + if (!res?.data?.user) + throw new BadRequestError(constants.HTTP_TEXTS.NO_CS_USER); + + const orgs = (res?.data?.user?.organizations || []) + ?.filter((org: any) => org?.org_roles?.some((item: any) => item.admin)) + ?.map(({ uid, name }: any) => ({ org_id: uid, org_name: name })); + + return { + user: { + email: res?.data?.user?.email, + first_name: res?.data?.user?.first_name, + last_name: res?.data?.user?.last_name, + orgs: orgs, + }, + }; +}; + +export const userService = { + getUserProfile, +}; diff --git a/src/utils/jwt.utils.ts b/src/utils/jwt.utils.ts index 9639c443..9d1ce88c 100644 --- a/src/utils/jwt.utils.ts +++ b/src/utils/jwt.utils.ts @@ -1,10 +1,10 @@ /// src/utils/jwt.utils.ts import jwt from "jsonwebtoken"; -import { MigrationPayload } from "../models/types"; +import { AppTokenPayload } from "../models/types"; import { config } from "../config"; // @typescript-eslint/no-explicit-any -export const generateToken = (payload: MigrationPayload): string => { +export const generateToken = (payload: AppTokenPayload): string => { return jwt.sign(payload, config.APP_TOKEN_KEY, { expiresIn: config.APP_TOKEN_EXP, });