127 lines
3.5 KiB
JavaScript
127 lines
3.5 KiB
JavaScript
import jwt from "jsonwebtoken";
|
|
import crypto from "node:crypto";
|
|
import User from "../models/User/model.js";
|
|
import Access from "../models/Access/model.js";
|
|
import dotenv from "dotenv";
|
|
|
|
dotenv.config();
|
|
|
|
const { ECT_KEY, ECT_IV, ECT_METHOD } = process.env;
|
|
|
|
export const getAccessToken = (payload) => {
|
|
return jwt.sign(payload, ECT_KEY, {
|
|
algorithm: "HS256",
|
|
expiresIn: "7m",
|
|
});
|
|
};
|
|
|
|
export const getEncryptedPayload = (payload) => {
|
|
const cipher = crypto.createCipheriv(ECT_METHOD, ECT_KEY, ECT_IV);
|
|
let encrypted = cipher.update(JSON.stringify(payload), "utf8", "hex");
|
|
encrypted += cipher.final("hex");
|
|
const tag = cipher.getAuthTag().toString("hex");
|
|
return `${ECT_IV}:${tag}:${encrypted}`;
|
|
};
|
|
|
|
export const getDecryptedPayload = (payload) => {
|
|
const [ivHex, authTagHex, encryptedHex] = payload.split(":");
|
|
|
|
const iv = Buffer.from(ivHex, "hex");
|
|
const authTag = Buffer.from(authTagHex, "hex");
|
|
const decipher = crypto.createDecipheriv(ECT_METHOD, ECT_KEY, ECT_IV);
|
|
|
|
decipher.setAuthTag(authTag); // Verify integrity
|
|
|
|
let decrypted = decipher.update(encryptedHex, "hex", "utf8");
|
|
decrypted += decipher.final("utf8");
|
|
|
|
return JSON.parse(decrypted);
|
|
};
|
|
|
|
export const getRefreshToken = () => {
|
|
return crypto.randomBytes(32).toString("hex");
|
|
};
|
|
|
|
export const authorize = (requiredModule, requiredPermission) => {
|
|
return async (req, res, next) => {
|
|
try {
|
|
const authHeader = req.headers.authorization;
|
|
|
|
if (!authHeader) {
|
|
throw { code: 401, message: "Bearer Token Missing" };
|
|
}
|
|
|
|
const token = authHeader.replace("Bearer ", "");
|
|
const verifiedPayload = jwt.verify(token, process.env.ECT_KEY || "");
|
|
|
|
// 1. Check if User exists
|
|
const userExists = await User.exists({ _id: verifiedPayload.id });
|
|
if (!userExists) {
|
|
throw { code: 404, message: "User not found" };
|
|
}
|
|
|
|
// 2. Fetch Access and Populate Role
|
|
// Assuming Access model has a 'user' field and a 'role' field
|
|
const access = await Access.findOne({
|
|
user: verifiedPayload.id,
|
|
}).populate("role");
|
|
|
|
console.log("access", access?.role?.modules);
|
|
|
|
if (!access || !access.role) {
|
|
throw { code: 403, message: "No role assigned to this user" };
|
|
}
|
|
|
|
// 3. Verify Permissions
|
|
const hasPermission = access?.role?.modules?.some(
|
|
(mod) =>
|
|
(mod.module === requiredModule &&
|
|
mod.permission === requiredPermission) ||
|
|
(requiredModule === "generic" && requiredPermission === "generic"),
|
|
);
|
|
|
|
if (!hasPermission) {
|
|
throw {
|
|
code: 403,
|
|
message: `Insufficient permissions for ${requiredModule} (${requiredPermission})`,
|
|
};
|
|
}
|
|
|
|
// Success
|
|
res.locals.user = verifiedPayload;
|
|
next();
|
|
} catch (error) {
|
|
console.error("AUTH_ERROR", error);
|
|
res.status(error.code || 401).json(error);
|
|
}
|
|
};
|
|
};
|
|
|
|
export const authorizeWithEncryptedKey = (
|
|
requiredModule,
|
|
requiredPermission,
|
|
) => {
|
|
return async (req, res, next) => {
|
|
try {
|
|
const { key } = req.query;
|
|
|
|
if (!key) {
|
|
throw { code: 401, message: "Encrypted Auth Key Missing" };
|
|
}
|
|
|
|
const verifiedPayload = getDecryptedPayload(key);
|
|
|
|
const userExists = await User.exists({ _id: verifiedPayload.id });
|
|
if (!userExists) {
|
|
throw { code: 404, message: "User not found" };
|
|
}
|
|
|
|
res.locals.user = verifiedPayload;
|
|
next();
|
|
} catch (error) {
|
|
console.error("AUTH_ERROR", error);
|
|
res.status(error.code || 401).json(error);
|
|
}
|
|
};
|
|
};
|