mbkauthe 1.0.15 → 1.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/main.js +52 -59
- package/lib/validateSessionAndRole.js +25 -18
- package/package.json +1 -1
package/lib/main.js
CHANGED
|
@@ -25,6 +25,7 @@ try {
|
|
|
25
25
|
} catch (error) {
|
|
26
26
|
console.log("Error parsing COOKIE_EXPIRE_TIME:", error);
|
|
27
27
|
}
|
|
28
|
+
|
|
28
29
|
// Enable CORS for subdomains
|
|
29
30
|
router.use((req, res, next) => {
|
|
30
31
|
const origin = req.headers.origin;
|
|
@@ -41,46 +42,30 @@ router.use(express.json());
|
|
|
41
42
|
router.use(express.urlencoded({ extended: true }));
|
|
42
43
|
router.use(cookieParser());
|
|
43
44
|
|
|
44
|
-
// Configure session with proper domain settings
|
|
45
|
+
// Configure session with proper domain settings for cross-subdomain sharing
|
|
45
46
|
const sessionConfig = {
|
|
46
47
|
store: new PgSession({
|
|
47
48
|
pool: dblogin,
|
|
48
49
|
tableName: "session",
|
|
50
|
+
createTableIfMissing: true
|
|
49
51
|
}),
|
|
50
52
|
secret: mbkautheVar.SESSION_SECRET_KEY,
|
|
51
53
|
resave: false,
|
|
52
54
|
saveUninitialized: false,
|
|
55
|
+
proxy: true, // Trust the reverse proxy
|
|
53
56
|
cookie: {
|
|
54
57
|
maxAge: COOKIE_EXPIRE_TIME,
|
|
55
58
|
domain: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
|
|
56
59
|
httpOnly: true,
|
|
57
|
-
secure: mbkautheVar.IS_DEPLOYED === 'true',
|
|
60
|
+
secure: mbkautheVar.IS_DEPLOYED === 'true' ? 'auto' : false, // 'auto' respects X-Forwarded-Proto
|
|
58
61
|
sameSite: 'lax',
|
|
62
|
+
path: '/'
|
|
59
63
|
},
|
|
60
|
-
name: 'mbkauthe.sid'
|
|
64
|
+
name: 'mbkauthe.sid'
|
|
61
65
|
};
|
|
62
66
|
|
|
63
67
|
router.use(session(sessionConfig));
|
|
64
68
|
|
|
65
|
-
|
|
66
|
-
router.use(async (req, res, next) => {
|
|
67
|
-
if (req.session && req.session.user) {
|
|
68
|
-
res.cookie("username", req.session.user.username, {
|
|
69
|
-
maxAge: COOKIE_EXPIRE_TIME,
|
|
70
|
-
path: '/', // Ensure the cookie is available on all paths
|
|
71
|
-
DOMAIN: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
|
|
72
|
-
secure: mbkautheVar.IS_DEPLOYED === 'true',
|
|
73
|
-
});
|
|
74
|
-
res.cookie("sessionId", req.session.user.sessionId, {
|
|
75
|
-
maxAge: COOKIE_EXPIRE_TIME,
|
|
76
|
-
path: '/',
|
|
77
|
-
DOMAIN: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
|
|
78
|
-
secure: mbkautheVar.IS_DEPLOYED === 'true',
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
next();
|
|
82
|
-
});
|
|
83
|
-
|
|
84
69
|
// Middleware to handle session restoration from sessionId cookie
|
|
85
70
|
router.use(async (req, res, next) => {
|
|
86
71
|
if (!req.session.user && req.cookies.sessionId) {
|
|
@@ -108,23 +93,42 @@ router.use(async (req, res, next) => {
|
|
|
108
93
|
next();
|
|
109
94
|
});
|
|
110
95
|
|
|
111
|
-
//
|
|
112
|
-
|
|
96
|
+
// Set consistent cookie options for all cookies
|
|
97
|
+
const getCookieOptions = () => ({
|
|
98
|
+
maxAge: COOKIE_EXPIRE_TIME,
|
|
99
|
+
domain: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
|
|
100
|
+
secure: mbkautheVar.IS_DEPLOYED === 'true' ? 'auto' : false,
|
|
101
|
+
sameSite: 'lax',
|
|
102
|
+
path: '/',
|
|
103
|
+
httpOnly: true
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
router.use(async (req, res, next) => {
|
|
107
|
+
if (req.session && req.session.user) {
|
|
108
|
+
const cookieOptions = getCookieOptions();
|
|
109
|
+
res.cookie("username", req.session.user.username, cookieOptions);
|
|
110
|
+
res.cookie("sessionId", req.session.user.sessionId, cookieOptions);
|
|
111
|
+
}
|
|
112
|
+
next();
|
|
113
|
+
});
|
|
114
|
+
|
|
113
115
|
router.post("/mbkauthe/api/terminateAllSessions", authenticate(mbkautheVar.Main_SECRET_TOKEN), async (req, res) => {
|
|
114
116
|
try {
|
|
115
117
|
await dblogin.query(`UPDATE "Users" SET "SessionId" = NULL`);
|
|
116
|
-
|
|
117
|
-
// Clear the session table
|
|
118
118
|
await dblogin.query('DELETE FROM "session"');
|
|
119
119
|
|
|
120
|
-
// Destroy all sessions on the server
|
|
121
120
|
req.session.destroy((err) => {
|
|
122
121
|
if (err) {
|
|
123
122
|
console.log("Error destroying session:", err);
|
|
124
|
-
return res
|
|
125
|
-
.status(500)
|
|
126
|
-
.json({ success: false, message: "Failed to terminate sessions" });
|
|
123
|
+
return res.status(500).json({ success: false, message: "Failed to terminate sessions" });
|
|
127
124
|
}
|
|
125
|
+
|
|
126
|
+
// Clear all cookies with proper domain
|
|
127
|
+
const cookieOptions = getCookieOptions();
|
|
128
|
+
res.clearCookie("mbkauthe.sid", cookieOptions);
|
|
129
|
+
res.clearCookie("sessionId", cookieOptions);
|
|
130
|
+
res.clearCookie("username", cookieOptions);
|
|
131
|
+
|
|
128
132
|
console.log("All sessions terminated successfully");
|
|
129
133
|
res.status(200).json({
|
|
130
134
|
success: true,
|
|
@@ -133,23 +137,20 @@ router.post("/mbkauthe/api/terminateAllSessions", authenticate(mbkautheVar.Main_
|
|
|
133
137
|
});
|
|
134
138
|
} catch (err) {
|
|
135
139
|
console.log("Database query error during session termination:", err);
|
|
136
|
-
res
|
|
137
|
-
.status(500)
|
|
138
|
-
.json({ success: false, message: "Internal Server Error" });
|
|
140
|
+
res.status(500).json({ success: false, message: "Internal Server Error" });
|
|
139
141
|
}
|
|
140
|
-
}
|
|
141
|
-
);
|
|
142
|
+
});
|
|
142
143
|
|
|
143
144
|
router.post("/mbkauthe/api/login", async (req, res) => {
|
|
144
|
-
console.log("Login request received");
|
|
145
|
+
console.log("Login request received");
|
|
145
146
|
|
|
146
147
|
const { username, password, token, recaptcha } = req.body;
|
|
147
|
-
console.log(`Login attempt for username: ${username}`);
|
|
148
|
+
console.log(`Login attempt for username: ${username}`);
|
|
148
149
|
|
|
149
150
|
const secretKey = mbkautheVar.RECAPTCHA_SECRET_KEY;
|
|
150
151
|
const verificationUrl = `https://www.google.com/recaptcha/api/siteverify?secret=${secretKey}&response=${recaptcha}`;
|
|
151
152
|
|
|
152
|
-
let BypassUsers = Array.isArray(mbkautheVar.BypassUsers) ? mbkautheVar.BypassUsers : JSON.parse(mbkautheVar.BypassUsers);
|
|
153
|
+
let BypassUsers = Array.isArray(mbkautheVar.BypassUsers) ? mbkautheVar.BypassUsers : JSON.parse(mbkautheVar.BypassUsers);
|
|
153
154
|
|
|
154
155
|
if (mbkautheVar.RECAPTCHA_Enabled === "true") {
|
|
155
156
|
if (!BypassUsers.includes(username)) {
|
|
@@ -160,7 +161,7 @@ router.post("/mbkauthe/api/login", async (req, res) => {
|
|
|
160
161
|
try {
|
|
161
162
|
const response = await fetch(verificationUrl, { method: 'POST' });
|
|
162
163
|
const body = await response.json();
|
|
163
|
-
console.log("reCAPTCHA verification response:", body);
|
|
164
|
+
console.log("reCAPTCHA verification response:", body);
|
|
164
165
|
|
|
165
166
|
if (!body.success) {
|
|
166
167
|
console.log("Failed reCAPTCHA verification");
|
|
@@ -182,7 +183,6 @@ router.post("/mbkauthe/api/login", async (req, res) => {
|
|
|
182
183
|
}
|
|
183
184
|
|
|
184
185
|
try {
|
|
185
|
-
// Query to check if the username exists
|
|
186
186
|
const userQuery = `SELECT * FROM "Users" WHERE "UserName" = $1`;
|
|
187
187
|
const userResult = await dblogin.query(userQuery, [username]);
|
|
188
188
|
|
|
@@ -193,20 +193,16 @@ router.post("/mbkauthe/api/login", async (req, res) => {
|
|
|
193
193
|
|
|
194
194
|
const user = userResult.rows[0];
|
|
195
195
|
|
|
196
|
-
// Check if the password matches
|
|
197
196
|
if (user.Password !== password) {
|
|
198
197
|
console.log(`Incorrect password for username: ${username}`);
|
|
199
198
|
return res.status(401).json({ success: false, message: "Incorrect Username Or Password" });
|
|
200
199
|
}
|
|
201
200
|
|
|
202
|
-
// Check if the account is inactive
|
|
203
201
|
if (!user.Active) {
|
|
204
202
|
console.log(`Inactive account for username: ${username}`);
|
|
205
203
|
return res.status(403).json({ success: false, message: "Account is inactive" });
|
|
206
204
|
}
|
|
207
205
|
|
|
208
|
-
|
|
209
|
-
// Check if the user is authorized to use the application
|
|
210
206
|
if (user.Role !== "SuperAdmin") {
|
|
211
207
|
const allowedApps = user.AllowedApps;
|
|
212
208
|
if (!allowedApps || !allowedApps.includes(mbkautheVar.APP_NAME)) {
|
|
@@ -219,7 +215,7 @@ router.post("/mbkauthe/api/login", async (req, res) => {
|
|
|
219
215
|
let sharedSecret;
|
|
220
216
|
const query = `SELECT "TwoFAStatus", "TwoFASecret" FROM "TwoFA" WHERE "UserName" = $1`;
|
|
221
217
|
const twoFAResult = await dblogin.query(query, [username]);
|
|
222
|
-
console.log("TwoFA query result:", twoFAResult.rows);
|
|
218
|
+
console.log("TwoFA query result:", twoFAResult.rows);
|
|
223
219
|
|
|
224
220
|
sharedSecret = twoFAResult.rows[0]?.TwoFASecret;
|
|
225
221
|
if (twoFAResult.rows.length > 0 && twoFAResult.rows[0].TwoFAStatus && !token) {
|
|
@@ -232,7 +228,7 @@ router.post("/mbkauthe/api/login", async (req, res) => {
|
|
|
232
228
|
secret: sharedSecret,
|
|
233
229
|
encoding: "base32",
|
|
234
230
|
token: token,
|
|
235
|
-
window: 1,
|
|
231
|
+
window: 1,
|
|
236
232
|
});
|
|
237
233
|
|
|
238
234
|
if (!tokenValidates) {
|
|
@@ -242,16 +238,14 @@ router.post("/mbkauthe/api/login", async (req, res) => {
|
|
|
242
238
|
}
|
|
243
239
|
}
|
|
244
240
|
|
|
245
|
-
// Generate session ID
|
|
246
241
|
const sessionId = crypto.randomBytes(256).toString("hex");
|
|
247
|
-
console.log(`Generated session ID for username: ${username}`);
|
|
242
|
+
console.log(`Generated session ID for username: ${username}`);
|
|
248
243
|
|
|
249
244
|
await dblogin.query(`UPDATE "Users" SET "SessionId" = $1 WHERE "id" = $2`, [
|
|
250
245
|
sessionId,
|
|
251
246
|
user.id,
|
|
252
247
|
]);
|
|
253
248
|
|
|
254
|
-
// Store session ID in session
|
|
255
249
|
req.session.user = {
|
|
256
250
|
id: user.id,
|
|
257
251
|
username: user.UserName,
|
|
@@ -259,15 +253,10 @@ router.post("/mbkauthe/api/login", async (req, res) => {
|
|
|
259
253
|
sessionId,
|
|
260
254
|
};
|
|
261
255
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
path: '/',
|
|
265
|
-
DOMAIN: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
|
|
266
|
-
secure: mbkautheVar.IS_DEPLOYED === 'true',
|
|
267
|
-
});
|
|
256
|
+
const cookieOptions = getCookieOptions();
|
|
257
|
+
res.cookie("sessionId", sessionId, cookieOptions);
|
|
268
258
|
console.log(req.session.user);
|
|
269
259
|
|
|
270
|
-
|
|
271
260
|
console.log(`User "${username}" logged in successfully`);
|
|
272
261
|
res.status(200).json({
|
|
273
262
|
success: true,
|
|
@@ -296,9 +285,13 @@ router.post("/mbkauthe/api/logout", async (req, res) => {
|
|
|
296
285
|
console.log("Error destroying session:", err);
|
|
297
286
|
return res.status(500).json({ success: false, message: "Logout failed" });
|
|
298
287
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
288
|
+
|
|
289
|
+
// Clear all cookies with proper domain
|
|
290
|
+
const cookieOptions = getCookieOptions();
|
|
291
|
+
res.clearCookie("mbkauthe.sid", cookieOptions);
|
|
292
|
+
res.clearCookie("sessionId", cookieOptions);
|
|
293
|
+
res.clearCookie("username", cookieOptions);
|
|
294
|
+
|
|
302
295
|
console.log(`User "${username}" logged out successfully`);
|
|
303
296
|
res.status(200).json({ success: true, message: "Logout successful" });
|
|
304
297
|
});
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { dblogin } from "./pool.js";
|
|
2
2
|
const mbkautheVar = JSON.parse(process.env.mbkautheVar);
|
|
3
3
|
|
|
4
|
+
// Get consistent cookie options
|
|
5
|
+
const getCookieOptions = () => ({
|
|
6
|
+
domain: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
|
|
7
|
+
secure: mbkautheVar.IS_DEPLOYED === 'true' ? 'auto' : false,
|
|
8
|
+
sameSite: 'lax',
|
|
9
|
+
path: '/',
|
|
10
|
+
httpOnly: true
|
|
11
|
+
});
|
|
12
|
+
|
|
4
13
|
async function validateSession(req, res, next) {
|
|
5
14
|
// First check if we have a session cookie
|
|
6
15
|
if (!req.session.user && req.cookies.sessionId) {
|
|
@@ -25,7 +34,6 @@ async function validateSession(req, res, next) {
|
|
|
25
34
|
}
|
|
26
35
|
|
|
27
36
|
if (!req.session.user) {
|
|
28
|
-
|
|
29
37
|
console.log("User not authenticated");
|
|
30
38
|
console.log(req.session.user);
|
|
31
39
|
return res.render("templates/Error/NotLoggedIn.handlebars", {
|
|
@@ -42,8 +50,10 @@ async function validateSession(req, res, next) {
|
|
|
42
50
|
if (result.rows.length === 0 || userResult.SessionId !== sessionId) {
|
|
43
51
|
console.log(`Session invalidated for user "${req.session.user.username}"`);
|
|
44
52
|
req.session.destroy();
|
|
45
|
-
|
|
46
|
-
res.clearCookie("
|
|
53
|
+
const cookieOptions = getCookieOptions();
|
|
54
|
+
res.clearCookie("mbkauthe.sid", cookieOptions);
|
|
55
|
+
res.clearCookie("sessionId", cookieOptions);
|
|
56
|
+
res.clearCookie("username", cookieOptions);
|
|
47
57
|
return res.render("templates/Error/SessionExpire.handlebars", {
|
|
48
58
|
currentUrl: req.originalUrl,
|
|
49
59
|
});
|
|
@@ -52,8 +62,10 @@ async function validateSession(req, res, next) {
|
|
|
52
62
|
if (!userResult.Active) {
|
|
53
63
|
console.log(`Account is inactive for user "${req.session.user.username}"`);
|
|
54
64
|
req.session.destroy();
|
|
55
|
-
|
|
56
|
-
res.clearCookie("
|
|
65
|
+
const cookieOptions = getCookieOptions();
|
|
66
|
+
res.clearCookie("mbkauthe.sid", cookieOptions);
|
|
67
|
+
res.clearCookie("sessionId", cookieOptions);
|
|
68
|
+
res.clearCookie("username", cookieOptions);
|
|
57
69
|
return res.render("templates/Error/AccountInactive.handlebars", {
|
|
58
70
|
currentUrl: req.originalUrl,
|
|
59
71
|
});
|
|
@@ -64,8 +76,10 @@ async function validateSession(req, res, next) {
|
|
|
64
76
|
if (!allowedApps || !allowedApps.includes(mbkautheVar.APP_NAME)) {
|
|
65
77
|
console.warn(`User \"${req.session.user.username}\" is not authorized to use the application \"${mbkautheVar.APP_NAME}\"`);
|
|
66
78
|
req.session.destroy();
|
|
67
|
-
|
|
68
|
-
res.clearCookie("
|
|
79
|
+
const cookieOptions = getCookieOptions();
|
|
80
|
+
res.clearCookie("mbkauthe.sid", cookieOptions);
|
|
81
|
+
res.clearCookie("sessionId", cookieOptions);
|
|
82
|
+
res.clearCookie("username", cookieOptions);
|
|
69
83
|
return res.render("templates/Error/Error.handlebars", {
|
|
70
84
|
error: `You Are Not Authorized To Use The Application \"${mbkautheVar.APP_NAME}\"`,
|
|
71
85
|
});
|
|
@@ -100,9 +114,7 @@ const checkRolePermission = (requiredRole) => {
|
|
|
100
114
|
const result = await dblogin.query(query, [userId]);
|
|
101
115
|
|
|
102
116
|
if (result.rows.length === 0) {
|
|
103
|
-
return res
|
|
104
|
-
.status(401)
|
|
105
|
-
.json({ success: false, message: "User not found" });
|
|
117
|
+
return res.status(401).json({ success: false, message: "User not found" });
|
|
106
118
|
}
|
|
107
119
|
|
|
108
120
|
const userRole = result.rows[0].Role;
|
|
@@ -116,9 +128,7 @@ const checkRolePermission = (requiredRole) => {
|
|
|
116
128
|
next();
|
|
117
129
|
} catch (err) {
|
|
118
130
|
console.error("Permission check error:", err);
|
|
119
|
-
res
|
|
120
|
-
.status(500)
|
|
121
|
-
.json({ success: false, message: "Internal Server Error" });
|
|
131
|
+
res.status(500).json({ success: false, message: "Internal Server Error" });
|
|
122
132
|
}
|
|
123
133
|
};
|
|
124
134
|
};
|
|
@@ -137,12 +147,11 @@ async function getUserData(UserName, parameters) {
|
|
|
137
147
|
throw new Error("Parameters are required to fetch user data");
|
|
138
148
|
}
|
|
139
149
|
|
|
140
|
-
// Dynamically select fields from Users table based on `parameters`
|
|
141
150
|
const userFields = [
|
|
142
|
-
"Password", "UserName", "Role", "Active", "GuestRole", "HaveMailAccount",
|
|
151
|
+
"Password", "UserName", "Role", "Active", "GuestRole", "HaveMailAccount", "AllowedApps",
|
|
143
152
|
];
|
|
144
153
|
const profileFields = [
|
|
145
|
-
"FullName", "email", "Image", "ProjectLinks", "SocialAccounts", "Bio",
|
|
154
|
+
"FullName", "email", "Image", "ProjectLinks", "SocialAccounts", "Bio", "Positions",
|
|
146
155
|
];
|
|
147
156
|
|
|
148
157
|
let userParameters = [];
|
|
@@ -156,7 +165,6 @@ async function getUserData(UserName, parameters) {
|
|
|
156
165
|
profileParameters = profileFields.filter(field => parameters.includes(field));
|
|
157
166
|
}
|
|
158
167
|
|
|
159
|
-
// Prepare queries based on required fields
|
|
160
168
|
let userResult = {};
|
|
161
169
|
if (userParameters.length > 0) {
|
|
162
170
|
const userQuery = `SELECT ${userParameters.map(field => `"${field}"`).join(", ")}
|
|
@@ -175,7 +183,6 @@ async function getUserData(UserName, parameters) {
|
|
|
175
183
|
profileResult = profileQueryResult.rows[0];
|
|
176
184
|
}
|
|
177
185
|
|
|
178
|
-
// Combine results
|
|
179
186
|
const combinedResult = { ...userResult, ...profileResult };
|
|
180
187
|
return combinedResult;
|
|
181
188
|
} catch (err) {
|