mbkauthe 1.1.18 → 1.2.0

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/README.md CHANGED
@@ -76,7 +76,7 @@ const app = express();
76
76
  app.use(mbkAuthRouter);
77
77
 
78
78
  app.listen(3000, () => {
79
- console.log("Server is running on port 3000");
79
+ console.log("[mbkauthe] Server is running on port 3000");
80
80
  });
81
81
  ```
82
82
  2. Ensure your `.env` file is properly configured. Refer to the [Configuration Guide(env.md)](env.md) for details.
package/index.js CHANGED
@@ -35,19 +35,23 @@ if (mbkautheVar.COOKIE_EXPIRE_TIME !== undefined) {
35
35
 
36
36
  const app = express();
37
37
  if (process.env.test === "true") {
38
- console.log("Test mode is enabled. Starting server in test mode.");
38
+ console.log("[mbkauthe] Test mode is enabled. Starting server in test mode.");
39
39
  const port = 3000;
40
40
  app.use(router);
41
41
  app.listen(port, () => {
42
- console.log(`Server running on http://localhost:${port}`);
42
+ console.log(`[mbkauthe] Server running on http://localhost:${port}`);
43
43
  });
44
44
  }
45
45
 
46
- app.set("views", path.join(__dirname, "node_modules/mbkauthe/views"));
46
+ app.set("views", [
47
+ path.join(__dirname, "views"),
48
+ path.join(__dirname, "node_modules/mbkauthe/views")
49
+ ]);
47
50
 
48
51
  app.engine("handlebars", engine({
49
52
  defaultLayout: false,
50
53
  partialsDir: [
54
+ path.join(__dirname, "views"),
51
55
  path.join(__dirname, "node_modules/mbkauthe/views"),
52
56
  path.join(__dirname, "node_modules/mbkauthe/views/Error"),
53
57
  ],
@@ -60,7 +64,7 @@ const require = createRequire(import.meta.url);
60
64
  const packageJson = require("./package.json");
61
65
  const latestVersion = await getLatestVersion();
62
66
  if(latestVersion !== packageJson.version) {
63
- console.warn(`Warning: The current version (${packageJson.version}) is not the latest version (${latestVersion}). Please update mbkauthe.`);
67
+ console.warn(`[mbkauthe] Warning: The current version (${packageJson.version}) is not the latest version (${latestVersion}). Please update mbkauthe.`);
64
68
  }
65
69
 
66
70
  export { validateSession, checkRolePermission, validateSessionAndRole, getUserData, authenticate, authapi } from "./lib/validateSessionAndRole.js";
package/lib/main.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import express from "express";
2
+ import csurf from "csurf";
2
3
  import crypto from "crypto";
3
4
  import session from "express-session";
4
5
  import pgSession from "connect-pg-simple";
@@ -28,6 +29,10 @@ router.use(express.json());
28
29
  router.use(express.urlencoded({ extended: true }));
29
30
  router.use(cookieParser());
30
31
 
32
+ // CSRF protection middleware
33
+ const csrfProtection = csurf({ cookie: false });
34
+
35
+ // CORS and security headers
31
36
  router.use((req, res, next) => {
32
37
  const origin = req.headers.origin;
33
38
  if (origin && origin.endsWith(`.${mbkautheVar.DOMAIN}`)) {
@@ -87,7 +92,7 @@ router.use(async (req, res, next) => {
87
92
  };
88
93
  }
89
94
  } catch (err) {
90
- console.error("Session restoration error:", err);
95
+ console.error("[mbkauthe] Session restoration error:", err);
91
96
  }
92
97
  }
93
98
  next();
@@ -102,6 +107,66 @@ const getCookieOptions = () => ({
102
107
  httpOnly: true
103
108
  });
104
109
 
110
+ async function completeLoginProcess(req, res, user, redirectUrl = null) {
111
+ try {
112
+ const sessionId = crypto.randomBytes(256).toString("hex");
113
+ console.log(`[mbkauthe] Generated session ID for username: ${user.username}`);
114
+
115
+ // Delete old session record for this user
116
+ await dblogin.query('DELETE FROM "session" WHERE username = $1', [user.username]);
117
+
118
+ await dblogin.query(`UPDATE "Users" SET "SessionId" = $1 WHERE "id" = $2`, [
119
+ sessionId,
120
+ user.id,
121
+ ]);
122
+
123
+ req.session.user = {
124
+ id: user.id,
125
+ username: user.username,
126
+ role: user.role,
127
+ sessionId,
128
+ };
129
+
130
+ if (req.session.preAuthUser) {
131
+ delete req.session.preAuthUser;
132
+ }
133
+
134
+ req.session.save(async (err) => {
135
+ if (err) {
136
+ console.log("[mbkauthe] Session save error:", err);
137
+ return res.status(500).json({ success: false, message: "Internal Server Error" });
138
+ }
139
+ try {
140
+ await dblogin.query(
141
+ 'UPDATE "session" SET username = $1 WHERE sid = $2',
142
+ [user.username, req.sessionID]
143
+ );
144
+ } catch (e) {
145
+ console.log("[mbkauthe] Failed to update username in session table:", e);
146
+ }
147
+
148
+ const cookieOptions = getCookieOptions();
149
+ res.cookie("sessionId", sessionId, cookieOptions);
150
+ console.log(`[mbkauthe] User "${user.username}" logged in successfully`);
151
+
152
+ const responsePayload = {
153
+ success: true,
154
+ message: "Login successful",
155
+ sessionId,
156
+ };
157
+
158
+ if (redirectUrl) {
159
+ responsePayload.redirectUrl = redirectUrl;
160
+ }
161
+
162
+ res.status(200).json(responsePayload);
163
+ });
164
+ } catch (err) {
165
+ console.log("[mbkauthe] Error during login completion:", err);
166
+ res.status(500).json({ success: false, message: "Internal Server Error" });
167
+ }
168
+ }
169
+
105
170
  router.use(async (req, res, next) => {
106
171
  if (req.session && req.session.user) {
107
172
  const cookieOptions = getCookieOptions();
@@ -118,7 +183,7 @@ router.post("/mbkauthe/api/terminateAllSessions", authenticate(mbkautheVar.Main_
118
183
 
119
184
  req.session.destroy((err) => {
120
185
  if (err) {
121
- console.log("Error destroying session:", err);
186
+ console.log("[mbkauthe] Error destroying session:", err);
122
187
  return res.status(500).json({ success: false, message: "Failed to terminate sessions" });
123
188
  }
124
189
 
@@ -127,26 +192,26 @@ router.post("/mbkauthe/api/terminateAllSessions", authenticate(mbkautheVar.Main_
127
192
  res.clearCookie("sessionId", cookieOptions);
128
193
  res.clearCookie("username", cookieOptions);
129
194
 
130
- console.log("All sessions terminated successfully");
195
+ console.log("[mbkauthe] All sessions terminated successfully");
131
196
  res.status(200).json({
132
197
  success: true,
133
198
  message: "All sessions terminated successfully",
134
199
  });
135
200
  });
136
201
  } catch (err) {
137
- console.log("Database query error during session termination:", err);
202
+ console.log("[mbkauthe] Database query error during session termination:", err);
138
203
  res.status(500).json({ success: false, message: "Internal Server Error" });
139
204
  }
140
205
  });
141
206
 
142
207
  router.post("/mbkauthe/api/login", LoginLimit, async (req, res) => {
143
- console.log("Login request received");
208
+ console.log("[mbkauthe] Login request received");
144
209
 
145
- const { username, password, token } = req.body;
146
- console.log(`Login attempt for username: ${username}`);
210
+ const { username, password } = req.body;
211
+ console.log(`[mbkauthe] Login attempt for username: ${username}`);
147
212
 
148
213
  if (!username || !password) {
149
- console.log("Missing username or password");
214
+ console.log("[mbkauthe] Missing username or password");
150
215
  return res.status(400).json({
151
216
  success: false,
152
217
  message: "Username and password are required",
@@ -158,7 +223,7 @@ router.post("/mbkauthe/api/login", LoginLimit, async (req, res) => {
158
223
  const userResult = await dblogin.query(userQuery, [username]);
159
224
 
160
225
  if (userResult.rows.length === 0) {
161
- console.log(`Username does not exist: ${username}`);
226
+ console.log(`[mbkauthe] Username does not exist: ${username}`);
162
227
  return res.status(404).json({ success: false, message: "Incorrect Username Or Password" });
163
228
  }
164
229
 
@@ -168,114 +233,115 @@ router.post("/mbkauthe/api/login", LoginLimit, async (req, res) => {
168
233
  try {
169
234
  const result = await bcrypt.compare(password, user.Password);
170
235
  if (!result) {
171
- console.log("Incorrect password.");
236
+ console.log("[mbkauthe] Incorrect password.");
172
237
  return res.status(401).json({ success: false, errorCode: 603, message: "Incorrect Username Or Password." });
173
238
  }
174
- console.log("Password matches!");
239
+ console.log("[mbkauthe] Password matches!");
175
240
  } catch (err) {
176
- console.error("Error comparing password:", err);
241
+ console.error("[mbkauthe] Error comparing password:", err);
177
242
  return res.status(500).json({ success: false, errorCode: 605, message: `Internal Server Error` });
178
243
  }
179
244
  } else {
180
245
  if (user.Password !== password) {
181
- console.log(`Incorrect password for username: ${username}`);
246
+ console.log(`[mbkauthe] Incorrect password for username: ${username}`);
182
247
  return res.status(401).json({ success: false, errorCode: 603, message: "Incorrect Username Or Password" });
183
248
  }
184
249
  }
185
250
 
186
251
  if (!user.Active) {
187
- console.log(`Inactive account for username: ${username}`);
252
+ console.log(`[mbkauthe] Inactive account for username: ${username}`);
188
253
  return res.status(403).json({ success: false, message: "Account is inactive" });
189
254
  }
190
255
 
191
256
  if (user.Role !== "SuperAdmin") {
192
257
  const allowedApps = user.AllowedApps;
193
258
  if (!allowedApps || !allowedApps.some(app => app.toLowerCase() === mbkautheVar.APP_NAME.toLowerCase())) {
194
- console.warn(`User \"${user.UserName}\" is not authorized to use the application \"${mbkautheVar.APP_NAME}\"`);
259
+ console.warn(`[mbkauthe] User \"${user.UserName}\" is not authorized to use the application \"${mbkautheVar.APP_NAME}\"`);
195
260
  return res.status(403).json({ success: false, message: `You Are Not Authorized To Use The Application \"${mbkautheVar.APP_NAME}\"` });
196
261
  }
197
262
  }
198
263
 
199
264
  if ((mbkautheVar.MBKAUTH_TWO_FA_ENABLE || "").toLocaleLowerCase() === "true") {
200
- let sharedSecret;
201
- const query = `SELECT "TwoFAStatus", "TwoFASecret" FROM "TwoFA" WHERE "UserName" = $1`;
265
+ const query = `SELECT "TwoFAStatus" FROM "TwoFA" WHERE "UserName" = $1`;
202
266
  const twoFAResult = await dblogin.query(query, [username]);
203
- console.log("TwoFA query result:", twoFAResult.rows);
204
-
205
- sharedSecret = twoFAResult.rows[0]?.TwoFASecret;
206
- if (twoFAResult.rows.length > 0 && twoFAResult.rows[0].TwoFAStatus && !token) {
207
- console.log("2FA code required but not provided");
208
- return res.status(401).json({ success: false, message: "Please Enter 2FA code" });
209
- }
210
-
211
- if (token && twoFAResult.rows[0]?.TwoFAStatus) {
212
- const tokenValidates = speakeasy.totp.verify({
213
- secret: sharedSecret,
214
- encoding: "base32",
215
- token: token,
216
- window: 1,
217
- });
218
267
 
219
- if (!tokenValidates) {
220
- console.log(`Invalid 2FA code for username: ${username}`);
221
- return res.status(401).json({ success: false, message: "Invalid 2FA code" });
222
- }
268
+ if (twoFAResult.rows.length > 0 && twoFAResult.rows[0].TwoFAStatus) {
269
+ // 2FA is enabled, prompt for token on a separate page
270
+ req.session.preAuthUser = {
271
+ id: user.id,
272
+ username: user.UserName,
273
+ role: user.Role,
274
+ };
275
+ console.log(`[mbkauthe] 2FA required for user: ${username}`);
276
+ return res.json({ success: true, twoFactorRequired: true });
223
277
  }
224
278
  }
225
279
 
226
- const sessionId = crypto.randomBytes(256).toString("hex");
227
- console.log(`Generated session ID for username: ${username}`);
228
-
229
- // Delete old session record for this user
230
- if (user.SessionId) {
231
- await dblogin.query('DELETE FROM "session" WHERE username = $1', [user.UserName]);
232
- }
233
-
234
- await dblogin.query(`UPDATE "Users" SET "SessionId" = $1 WHERE "id" = $2`, [
235
- sessionId,
236
- user.id,
237
- ]);
238
-
239
- req.session.user = {
280
+ // If 2FA is not enabled, proceed with login
281
+ const userForSession = {
240
282
  id: user.id,
241
283
  username: user.UserName,
242
284
  role: user.Role,
243
- sessionId,
244
285
  };
286
+ await completeLoginProcess(req, res, userForSession);
245
287
 
246
- const cookieOptions = getCookieOptions();
247
- res.cookie("sessionId", sessionId, cookieOptions);
248
- console.log(req.session.user);
288
+ } catch (err) {
289
+ console.log("[mbkauthe] Error during login process:", err);
290
+ res.status(500).json({ success: false, message: "Internal Server Error" });
291
+ }
292
+ });
249
293
 
294
+ router.get("/mbkauthe/2fa", csrfProtection, (req, res) => {
295
+ if (!req.session.preAuthUser) {
296
+ return res.redirect("/mbkauthe/login");
297
+ }
298
+ res.render("2fa.handlebars", {
299
+ layout: false,
300
+ customURL: mbkautheVar.loginRedirectURL || '/home',
301
+ csrfToken: req.csrfToken(),
302
+ });
303
+ });
250
304
 
251
- // Save session and update username in session table
252
- req.session.save(async (err) => {
253
- if (err) {
254
- console.log("Session save error:", err);
255
- return res.status(500).json({ success: false, message: "Internal Server Error" });
256
- }
257
- try {
258
- await dblogin.query(
259
- 'UPDATE "session" SET username = $1 WHERE sid = $2',
260
- [user.UserName, req.sessionID]
261
- );
262
- } catch (e) {
263
- console.log("Failed to update username in session table:", e);
264
- }
305
+ router.post("/mbkauthe/api/verify-2fa", async (req, res) => {
306
+ if (!req.session.preAuthUser) {
307
+ return res.status(401).json({ success: false, message: "Not authorized. Please login first." });
308
+ }
265
309
 
266
- const cookieOptions = getCookieOptions();
267
- res.cookie("sessionId", sessionId, cookieOptions);
268
- console.log(req.session.user);
310
+ const { token } = req.body;
311
+ const { username, id, role } = req.session.preAuthUser;
269
312
 
270
- console.log(`User "${username}" logged in successfully`);
271
- res.status(200).json({
272
- success: true,
273
- message: "Login successful",
274
- sessionId,
275
- });
313
+ if (!token) {
314
+ return res.status(400).json({ success: false, message: "2FA token is required" });
315
+ }
316
+
317
+ try {
318
+ const query = `SELECT "TwoFASecret" FROM "TwoFA" WHERE "UserName" = $1`;
319
+ const twoFAResult = await dblogin.query(query, [username]);
320
+
321
+ if (twoFAResult.rows.length === 0 || !twoFAResult.rows[0].TwoFASecret) {
322
+ return res.status(500).json({ success: false, message: "2FA is not configured correctly." });
323
+ }
324
+
325
+ const sharedSecret = twoFAResult.rows[0].TwoFASecret;
326
+ const tokenValidates = speakeasy.totp.verify({
327
+ secret: sharedSecret,
328
+ encoding: "base32",
329
+ token: token,
330
+ window: 1,
276
331
  });
332
+
333
+ if (!tokenValidates) {
334
+ console.log(`[mbkauthe] Invalid 2FA code for username: ${username}`);
335
+ return res.status(401).json({ success: false, message: "Invalid 2FA code" });
336
+ }
337
+
338
+ // 2FA successful, complete login
339
+ const userForSession = { id, username, role };
340
+ const redirectUrl = mbkautheVar.loginRedirectURL || '/home';
341
+ await completeLoginProcess(req, res, userForSession, redirectUrl);
342
+
277
343
  } catch (err) {
278
- console.log("Error during login process:", err);
344
+ console.log("[mbkauthe] Error during 2FA verification:", err);
279
345
  res.status(500).json({ success: false, message: "Internal Server Error" });
280
346
  }
281
347
  });
@@ -293,7 +359,7 @@ router.post("/mbkauthe/api/logout", async (req, res) => {
293
359
 
294
360
  req.session.destroy((err) => {
295
361
  if (err) {
296
- console.log("Error destroying session:", err);
362
+ console.log("[mbkauthe] Error destroying session:", err);
297
363
  return res.status(500).json({ success: false, message: "Logout failed" });
298
364
  }
299
365
 
@@ -302,11 +368,11 @@ router.post("/mbkauthe/api/logout", async (req, res) => {
302
368
  res.clearCookie("sessionId", cookieOptions);
303
369
  res.clearCookie("username", cookieOptions);
304
370
 
305
- console.log(`User "${username}" logged out successfully`);
371
+ console.log(`[mbkauthe] User "${username}" logged out successfully`);
306
372
  res.status(200).json({ success: true, message: "Logout successful" });
307
373
  });
308
374
  } catch (err) {
309
- console.log("Database query error during logout:", err);
375
+ console.log("[mbkauthe] Database query error during logout:", err);
310
376
  res.status(500).json({ success: false, message: "Internal Server Error" });
311
377
  }
312
378
  } else {
@@ -314,7 +380,7 @@ router.post("/mbkauthe/api/logout", async (req, res) => {
314
380
  }
315
381
  });
316
382
 
317
- router.get("/mbkauthe/login", LoginLimit, (req, res) => {
383
+ router.get("/mbkauthe/login", LoginLimit, csrfProtection, (req, res) => {
318
384
  return res.render("loginmbkauthe.handlebars", {
319
385
  layout: false,
320
386
  customURL: mbkautheVar.loginRedirectURL || '/home',
@@ -322,6 +388,7 @@ router.get("/mbkauthe/login", LoginLimit, (req, res) => {
322
388
  username: req.session?.user?.username || '',
323
389
  version: packageJson.version,
324
390
  appName: mbkautheVar.APP_NAME.toUpperCase(),
391
+ csrfToken: req.csrfToken(),
325
392
  });
326
393
  });
327
394
 
@@ -334,7 +401,7 @@ async function getLatestVersion() {
334
401
  const latestPackageJson = await response.json();
335
402
  return latestPackageJson.version;
336
403
  } catch (error) {
337
- console.error('Error fetching latest version from GitHub:', error);
404
+ console.error('[mbkauthe] Error fetching latest version from GitHub:', error);
338
405
  return null;
339
406
  }
340
407
  }
@@ -345,7 +412,7 @@ async function getPackageLock() {
345
412
  return new Promise((resolve, reject) => {
346
413
  fs.readFile(packageLockPath, "utf8", (err, data) => {
347
414
  if (err) {
348
- console.error("Error reading package-lock.json:", err);
415
+ console.error("[mbkauthe] Error reading package-lock.json:", err);
349
416
  return reject({ success: false, message: "Failed to read package-lock.json" });
350
417
  }
351
418
  try {
@@ -361,7 +428,7 @@ async function getPackageLock() {
361
428
  const rootDependency = packageLock.dependencies?.mbkauthe || {};
362
429
  resolve({ mbkautheData, rootDependency });
363
430
  } catch (parseError) {
364
- console.error("Error parsing package-lock.json:", parseError);
431
+ console.error("[mbkauthe] Error parsing package-lock.json:", parseError);
365
432
  reject("Error parsing package-lock.json");
366
433
  }
367
434
  });
@@ -409,7 +476,7 @@ router.get(["/mbkauthe/info", "/mbkauthe/i"], LoginLimit, async (_, res) => {
409
476
  latestVersion = await getLatestVersion();
410
477
  //latestVersion = "Under Development"; // Placeholder for the latest version
411
478
  } catch (err) {
412
- console.error("Error fetching package-lock.json:", err);
479
+ console.error("[mbkauthe] Error fetching package-lock.json:", err);
413
480
  pkgl = { error: "Failed to fetch package-lock.json" };
414
481
  }
415
482
 
@@ -781,7 +848,7 @@ router.get(["/mbkauthe/info", "/mbkauthe/i"], LoginLimit, async (_, res) => {
781
848
  btn.innerHTML = '<span class="tooltiptext">Copy to clipboard</span>' + originalText.replace('✓ Copied', 'Copy JSON');
782
849
  }, 2000);
783
850
  }).catch(err => {
784
- console.error('Failed to copy text: ', err);
851
+ console.error('[mbkauthe] Failed to copy text: ', err);
785
852
  });
786
853
  }
787
854
  </script>
@@ -789,7 +856,7 @@ router.get(["/mbkauthe/info", "/mbkauthe/i"], LoginLimit, async (_, res) => {
789
856
  </html>
790
857
  `);
791
858
  } catch (err) {
792
- console.error("Error fetching version information:", err);
859
+ console.error("[mbkauthe] Error fetching version information:", err);
793
860
  res.status(500).send(`
794
861
  <html>
795
862
  <head>
package/lib/pool.js CHANGED
@@ -39,9 +39,8 @@ export const dblogin = new Pool(poolConfig);
39
39
  (async () => {
40
40
  try {
41
41
  const client = await dblogin.connect();
42
- console.log("Connected to PostgreSQL database (pool)!");
43
42
  client.release();
44
43
  } catch (err) {
45
- console.error("Database connection error (pool):", err);
44
+ console.error("[mbkauthe] Database connection error (pool):", err);
46
45
  }
47
46
  })();