feat: implement identity and security features

- Added account types and privileged account types constants.
- Created admin user controller for updating user security fields.
- Developed role assignment controller for managing user roles.
- Implemented validation middleware for request schemas.
- Defined user role and auth session models for database interactions.
- Created services for authentication, email notifications, and OTP handling.
- Developed OAuth service for Google and Apple authentication.
- Added JWT utility functions for token generation and verification.
- Implemented comprehensive tests for authentication, session management, and password policies.
- Created migration for updating user schema and adding new tables for auth sessions and user identities.
This commit is contained in:
Sathira Sri Sathara
2026-09-03 13:56:18 +05:30
parent 267e80e2ec
commit 9d3d431416
54 changed files with 1389 additions and 1076 deletions
+15 -82
View File
@@ -1,93 +1,26 @@
/**
* Copyright (c) 2026 Niolla
* All rights reserved.
*
* This source code is proprietary and confidential.
* Unauthorized copying, modification, distribution, or use
* of this file, via any medium, is strictly prohibited.
*/
// app/middleware/auth.middleware.js
const { verifyToken } = require("../utils/jwt.util");
const { getEffectivePermissions } = require("../services/permission.service");
const db = require("../models");
const authenticate = async (req, res, next) => {
try {
let token = null;
// Get token from cookie
if (req.cookies?.access_token) {
token = req.cookies.access_token;
}
// Fallback to Bearer token
if (!token && req.headers.authorization?.startsWith("Bearer ")) {
token = req.headers.authorization.split(" ")[1];
}
if (!token) {
return res.status(401).json({
success: false,
message: "Unauthorized",
});
}
// Verify token
const token = req.cookies?.access_token || (req.headers.authorization?.startsWith("Bearer ") ? req.headers.authorization.slice(7) : null);
if (!token) return res.status(401).json({ success: false, error: { code: "UNAUTHORIZED", message: "Authentication required" } });
const decoded = verifyToken(token);
if (process.env.NODE_ENV === "development") {
console.log("DECODED:", decoded);
if (!decoded.sub || !decoded.sid || !Number.isInteger(decoded.tokenVersion)) throw new Error("Required claims missing");
const [user, session] = await Promise.all([
db.User.findByPk(decoded.sub),
db.AuthSession.findByPk(decoded.sid),
]);
if (!user || user.accountStatus !== "ACTIVE" || user.tokenVersion !== decoded.tokenVersion || !session || session.user_id !== user.id || session.revoked_at || session.expires_at <= new Date() || session.token_version !== user.tokenVersion) {
return res.status(401).json({ success: false, error: { code: "SESSION_INVALID", message: "Session is no longer valid" } });
}
const userId = decoded.sub || decoded.id;
if (!userId) {
throw new Error("User ID missing in token");
}
// Load permissions
const permissions = await getEffectivePermissions(userId);
req.user = {
...decoded,
id: userId,
permissions,
};
req.user = { id: user.id, sessionId: session.id, firstName: user.firstName, lastName: user.lastName, email: user.email, accountType: user.accountType, accountStatus: user.accountStatus, permissions: await getEffectivePermissions(user.id) };
next();
} catch (err) {
console.error("AUTH ERROR:", err);
// Clear invalid/expired cookie
res.clearCookie("access_token", {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: process.env.NODE_ENV === "production" ? "None" : "Lax",
});
// Token expired
if (err.name === "TokenExpiredError") {
return res.status(401).json({
success: false,
message: "Session expired. Please login again.",
});
}
// Invalid token
if (err.name === "JsonWebTokenError") {
return res.status(401).json({
success: false,
message: "Invalid token",
});
}
// Default
return res.status(401).json({
success: false,
message: "Authentication failed",
});
} catch (error) {
res.clearCookie("access_token");
return res.status(401).json({ success: false, error: { code: "UNAUTHORIZED", message: "Invalid or expired access token" } });
}
};
module.exports = { authenticate };
module.exports = { authenticate, requireAuth: authenticate };