cargo / src /middleware /authMiddleware.js
vish85521's picture
Upload 53 files
964569f verified
Raw
History Blame Contribute Delete
4.01 kB
const { AppError } = require("../utils/appError");
function extractBearerToken(authorizationHeader) {
if (!authorizationHeader || typeof authorizationHeader !== "string") {
return null;
}
const [scheme, token] = authorizationHeader.trim().split(/\s+/, 2);
if (!scheme || !token) {
return null;
}
if (scheme.toLowerCase() !== "bearer") {
return null;
}
return token;
}
function createRequireAuthMiddleware(authService) {
if (!authService) {
throw new Error("createRequireAuthMiddleware requires authService.");
}
return async function requireAuth(req, _res, next) {
try {
const token = extractBearerToken(req.headers.authorization);
if (!token) {
throw new AppError("Authorization bearer token is required.", 401, {
code: "TOKEN_MISSING",
});
}
const decoded = authService.verifyAccessToken(token);
const userId = String(decoded.sub || "").trim();
if (!userId) {
throw new AppError("Invalid token subject.", 401, {
code: "TOKEN_SUBJECT_INVALID",
});
}
const authenticatedUser = await authService.getAuthenticatedUserById(userId);
req.auth = {
id: authenticatedUser.id,
email: authenticatedUser.email,
fullName: authenticatedUser.fullName,
roles: authenticatedUser.roles,
isSuperAdmin: authenticatedUser.isSuperAdmin,
tenant: authenticatedUser.tenant,
};
if (!req.context) {
req.context = {};
}
req.context.actorUserId = authenticatedUser.id;
return next();
} catch (error) {
return next(error);
}
};
}
function createRequireRolesMiddleware(allowedRoles) {
const normalizedAllowed = (Array.isArray(allowedRoles) ? allowedRoles : [])
.filter((role) => typeof role === "string")
.map((role) => role.trim().toLowerCase())
.filter(Boolean);
return function requireRoles(req, _res, next) {
if (!req.auth) {
return next(new AppError("Unauthorized.", 401));
}
if (req.auth.isSuperAdmin) {
return next();
}
if (normalizedAllowed.length === 0) {
return next();
}
const roleSet = new Set((req.auth.roles || []).map((role) => String(role).toLowerCase()));
const hasAllowedRole = normalizedAllowed.some((role) => roleSet.has(role));
if (!hasAllowedRole) {
return next(
new AppError("Insufficient permissions for this action.", 403, {
code: "RBAC_FORBIDDEN",
requiredRoles: normalizedAllowed,
})
);
}
return next();
};
}
function createTenantScopeMiddleware() {
return function enforceTenantScope(req, _res, next) {
if (!req.auth) {
return next();
}
if (!req.context) {
req.context = {};
}
const requestedTenantCodeRaw = req.query?.tenantCode;
const requestedTenantCode =
typeof requestedTenantCodeRaw === "string" && requestedTenantCodeRaw.trim()
? requestedTenantCodeRaw.trim()
: null;
if (req.auth.isSuperAdmin) {
req.context.tenantCode = requestedTenantCode;
if (requestedTenantCode) {
req.query.tenantCode = requestedTenantCode;
}
return next();
}
const userTenantCode = req.auth.tenant?.code || null;
if (!userTenantCode) {
return next(
new AppError("Authenticated user has no tenant scope.", 403, {
code: "TENANT_SCOPE_MISSING",
})
);
}
if (requestedTenantCode && requestedTenantCode !== userTenantCode) {
return next(
new AppError("Cross-tenant access is not allowed.", 403, {
code: "TENANT_SCOPE_VIOLATION",
requestedTenantCode,
tenantCode: userTenantCode,
})
);
}
req.context.tenantCode = userTenantCode;
req.query.tenantCode = userTenantCode;
return next();
};
}
module.exports = {
createRequireAuthMiddleware,
createRequireRolesMiddleware,
createTenantScopeMiddleware,
};