Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 3 additions & 9 deletions src/controllers/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -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,
};
12 changes: 12 additions & 0 deletions src/controllers/user.controller.ts
Original file line number Diff line number Diff line change
@@ -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,
};
32 changes: 17 additions & 15 deletions src/middlewares/auth.middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
};
2 changes: 1 addition & 1 deletion src/models/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export interface UserProfile {
};
}

export interface MigrationPayload {
export interface AppTokenPayload {
region: string;
user_id: string;
}
Expand Down
11 changes: 0 additions & 11 deletions src/routes/auth.routes.ts
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -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;
10 changes: 10 additions & 0 deletions src/routes/user.routes.ts
Original file line number Diff line number Diff line change
@@ -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;
2 changes: 2 additions & 0 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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
Expand Down
168 changes: 58 additions & 110 deletions src/services/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<LoginServiceType> => {
//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<LoginServiceType> => {
Expand Down Expand Up @@ -123,48 +112,7 @@ const requestSms = async (req: Request): Promise<LoginServiceType> => {
}
};

const getUserProfile = async (
req: Request
): Promise<UserProfile | LoginServiceType> => {
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,
};
50 changes: 50 additions & 0 deletions src/services/user.service.ts
Original file line number Diff line number Diff line change
@@ -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<UserProfile> => {
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,
};
Loading