mbkauthe 1.3.5 → 1.4.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/index.js CHANGED
@@ -34,10 +34,21 @@ if (mbkautheVar.COOKIE_EXPIRE_TIME !== undefined) {
34
34
  }
35
35
 
36
36
  const app = express();
37
- if (process.env.test === "true") {
38
- console.log("[mbkauthe] Test mode is enabled. Starting server in test mode.");
37
+ if (process.env.test === "dev") {
38
+ console.log("[mbkauthe] Dev mode is enabled. Starting server in dev mode.");
39
39
  const port = 5555;
40
40
  app.use(router);
41
+ app.use((req, res) => {
42
+ console.log(`Path not found: ${req.method} ${req.url}`);
43
+ return res.status(404).render("Error/dError.handlebars", {
44
+ layout: false,
45
+ code: 404,
46
+ error: "Not Found",
47
+ message: "The requested page was not found.",
48
+ pagename: "Home",
49
+ page: "/mbkauthe/login",
50
+ });
51
+ });
41
52
  app.listen(port, () => {
42
53
  console.log(`[mbkauthe] Server running on http://localhost:${port}`);
43
54
  });
package/lib/main.js CHANGED
@@ -31,6 +31,10 @@ router.use(express.json());
31
31
  router.use(express.urlencoded({ extended: true }));
32
32
  router.use(cookieParser());
33
33
 
34
+ router.get('/mbkauthe/main.js', (req, res) => {
35
+ res.sendFile(path.join(process.cwd(), 'public', 'main.js'));
36
+ });
37
+
34
38
  // CSRF protection middleware
35
39
  const csrfProtection = csurf({ cookie: false });
36
40
 
@@ -55,6 +59,18 @@ const LoginLimit = rateLimit({
55
59
  }
56
60
  });
57
61
 
62
+ const LogoutLimit = rateLimit({
63
+ windowMs: 1 * 60 * 1000,
64
+ max: 10,
65
+ message: { success: false, message: "Too many logout attempts, please try again later" }
66
+ });
67
+
68
+ const TwoFALimit = rateLimit({
69
+ windowMs: 1 * 60 * 1000,
70
+ max: 5,
71
+ message: { success: false, message: "Too many 2FA attempts, please try again later" }
72
+ });
73
+
58
74
  const sessionConfig = {
59
75
  store: new PgSession({
60
76
  pool: dblogin,
@@ -79,11 +95,19 @@ const sessionConfig = {
79
95
  router.use(session(sessionConfig));
80
96
 
81
97
  router.use(async (req, res, next) => {
98
+ // Only restore session if not already present and sessionId cookie exists
82
99
  if (!req.session.user && req.cookies.sessionId) {
83
100
  try {
84
101
  const sessionId = req.cookies.sessionId;
85
- const query = `SELECT id, "UserName", "Active", "Role", "SessionId" FROM "Users" WHERE "SessionId" = $1`;
86
- const result = await dblogin.query({ name: 'get-user-by-sessionid', text: query, values: [sessionId] });
102
+
103
+ // Validate sessionId format (should be 64 hex characters)
104
+ if (typeof sessionId !== 'string' || !/^[a-f0-9]{64}$/i.test(sessionId)) {
105
+ console.warn("[mbkauthe] Invalid sessionId format detected");
106
+ return next();
107
+ }
108
+
109
+ const query = `SELECT id, "UserName", "Active", "Role", "SessionId", "AllowedApps" FROM "Users" WHERE "SessionId" = $1 AND "Active" = true`;
110
+ const result = await dblogin.query({ name: 'get-user-by-sessionid', text: query, values: [sessionId] });
87
111
 
88
112
  if (result.rows.length > 0) {
89
113
  const user = result.rows[0];
@@ -91,7 +115,10 @@ router.use(async (req, res, next) => {
91
115
  id: user.id,
92
116
  username: user.UserName,
93
117
  UserName: user.UserName,
118
+ role: user.Role,
119
+ Role: user.Role,
94
120
  sessionId,
121
+ allowedApps: user.AllowedApps,
95
122
  };
96
123
  }
97
124
  } catch (err) {
@@ -110,12 +137,20 @@ const getCookieOptions = () => ({
110
137
  httpOnly: true
111
138
  });
112
139
 
140
+ const getClearCookieOptions = () => ({
141
+ domain: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
142
+ secure: mbkautheVar.IS_DEPLOYED === 'true' ? 'auto' : false,
143
+ sameSite: 'lax',
144
+ path: '/',
145
+ httpOnly: true
146
+ });
147
+
113
148
  async function completeLoginProcess(req, res, user, redirectUrl = null) {
114
149
  try {
115
150
  // smaller session id is sufficient and faster to generate/serialize
116
151
  const sessionId = crypto.randomBytes(32).toString("hex");
117
152
  console.log(`[mbkauthe] Generated session ID for username: ${user.username}`);
118
-
153
+
119
154
  // Delete old session record for this user
120
155
  await dblogin.query('DELETE FROM "session" WHERE username = $1', [user.username]);
121
156
 
@@ -139,7 +174,7 @@ async function completeLoginProcess(req, res, user, redirectUrl = null) {
139
174
 
140
175
  req.session.save(async (err) => {
141
176
  if (err) {
142
- console.log("[mbkauthe] Session save error:", err);
177
+ console.error("[mbkauthe] Session save error:", err);
143
178
  return res.status(500).json({ success: false, message: "Internal Server Error" });
144
179
  }
145
180
  // avoid writing back into the session table here to reduce DB writes;
@@ -162,7 +197,7 @@ async function completeLoginProcess(req, res, user, redirectUrl = null) {
162
197
  res.status(200).json(responsePayload);
163
198
  });
164
199
  } catch (err) {
165
- console.log("[mbkauthe] Error during login completion:", err);
200
+ console.error("[mbkauthe] Error during login completion:", err);
166
201
  res.status(500).json({ success: false, message: "Internal Server Error" });
167
202
  }
168
203
  }
@@ -170,8 +205,11 @@ async function completeLoginProcess(req, res, user, redirectUrl = null) {
170
205
  router.use(async (req, res, next) => {
171
206
  if (req.session && req.session.user) {
172
207
  const cookieOptions = getCookieOptions();
173
- res.cookie("username", req.session.user.username, { ...cookieOptions, httpOnly: false });
174
- res.cookie("sessionId", req.session.user.sessionId, cookieOptions);
208
+ // Only set cookies if they're missing or different
209
+ if (req.cookies.sessionId !== req.session.user.sessionId) {
210
+ res.cookie("username", req.session.user.username, { ...cookieOptions, httpOnly: false });
211
+ res.cookie("sessionId", req.session.user.sessionId, cookieOptions);
212
+ }
175
213
  }
176
214
  next();
177
215
  });
@@ -187,7 +225,7 @@ router.post("/mbkauthe/api/terminateAllSessions", authenticate(mbkautheVar.Main_
187
225
  return res.status(500).json({ success: false, message: "Failed to terminate sessions" });
188
226
  }
189
227
 
190
- const cookieOptions = getCookieOptions();
228
+ const cookieOptions = getClearCookieOptions();
191
229
  res.clearCookie("mbkauthe.sid", cookieOptions);
192
230
  res.clearCookie("sessionId", cookieOptions);
193
231
  res.clearCookie("username", cookieOptions);
@@ -199,7 +237,7 @@ router.post("/mbkauthe/api/terminateAllSessions", authenticate(mbkautheVar.Main_
199
237
  });
200
238
  });
201
239
  } catch (err) {
202
- console.log("[mbkauthe] Database query error during session termination:", err);
240
+ console.error("[mbkauthe] Database query error during session termination:", err);
203
241
  res.status(500).json({ success: false, message: "Internal Server Error" });
204
242
  }
205
243
  });
@@ -208,8 +246,8 @@ router.post("/mbkauthe/api/login", LoginLimit, async (req, res) => {
208
246
  console.log("[mbkauthe] Login request received");
209
247
 
210
248
  const { username, password } = req.body;
211
- console.log(`[mbkauthe] Login attempt for username: ${username}`);
212
249
 
250
+ // Input validation
213
251
  if (!username || !password) {
214
252
  console.log("[mbkauthe] Missing username or password");
215
253
  return res.status(400).json({
@@ -218,17 +256,45 @@ router.post("/mbkauthe/api/login", LoginLimit, async (req, res) => {
218
256
  });
219
257
  }
220
258
 
259
+ // Validate username format and length
260
+ if (typeof username !== 'string' || username.trim().length === 0 || username.length > 255) {
261
+ console.warn("[mbkauthe] Invalid username format");
262
+ return res.status(400).json({
263
+ success: false,
264
+ message: "Invalid username format",
265
+ });
266
+ }
267
+
268
+ // Validate password length
269
+ if (typeof password !== 'string' || password.length < 8 || password.length > 255) {
270
+ console.warn("[mbkauthe] Invalid password length");
271
+ return res.status(400).json({
272
+ success: false,
273
+ message: "Password must be at least 8 characters long",
274
+ });
275
+ }
276
+
277
+ console.log(`[mbkauthe] Login attempt for username: ${username.trim()}`);
278
+
279
+ const trimmedUsername = username.trim();
280
+
221
281
  try {
222
- const userQuery = `SELECT id, "UserName", "Password", "Active", "Role", "AllowedApps" FROM "Users" WHERE "UserName" = $1`;
223
- const userResult = await dblogin.query({ name: 'get-user-by-username', text: userQuery, values: [username] });
282
+ const userQuery = `SELECT id, "UserName", "Password", "Active", "Role", "AllowedApps" FROM "Users" WHERE "UserName" = $1`;
283
+ const userResult = await dblogin.query({ name: 'get-user-by-username', text: userQuery, values: [trimmedUsername] });
224
284
 
225
285
  if (userResult.rows.length === 0) {
226
- console.log(`[mbkauthe] Username does not exist: ${username}`);
286
+ console.log(`[mbkauthe] Username does not exist: ${trimmedUsername}`);
227
287
  return res.status(404).json({ success: false, message: "Incorrect Username Or Password" });
228
288
  }
229
289
 
230
290
  const user = userResult.rows[0];
231
291
 
292
+ // Validate user has password field
293
+ if (!user.Password) {
294
+ console.error("[mbkauthe] User account has no password set");
295
+ return res.status(500).json({ success: false, message: "Internal Server Error" });
296
+ }
297
+
232
298
  if (mbkautheVar.EncryptedPassword === "true") {
233
299
  try {
234
300
  const result = await bcrypt.compare(password, user.Password);
@@ -243,13 +309,13 @@ router.post("/mbkauthe/api/login", LoginLimit, async (req, res) => {
243
309
  }
244
310
  } else {
245
311
  if (user.Password !== password) {
246
- console.log(`[mbkauthe] Incorrect password for username: ${username}`);
312
+ console.log(`[mbkauthe] Incorrect password for username: ${trimmedUsername}`);
247
313
  return res.status(401).json({ success: false, errorCode: 603, message: "Incorrect Username Or Password" });
248
314
  }
249
315
  }
250
316
 
251
317
  if (!user.Active) {
252
- console.log(`[mbkauthe] Inactive account for username: ${username}`);
318
+ console.log(`[mbkauthe] Inactive account for username: ${trimmedUsername}`);
253
319
  return res.status(403).json({ success: false, message: "Account is inactive" });
254
320
  }
255
321
 
@@ -262,8 +328,8 @@ router.post("/mbkauthe/api/login", LoginLimit, async (req, res) => {
262
328
  }
263
329
 
264
330
  if ((mbkautheVar.MBKAUTH_TWO_FA_ENABLE || "").toLocaleLowerCase() === "true") {
265
- const query = `SELECT "TwoFAStatus" FROM "TwoFA" WHERE "UserName" = $1`;
266
- const twoFAResult = await dblogin.query({ name: 'get-2fa-status', text: query, values: [username] });
331
+ const query = `SELECT "TwoFAStatus" FROM "TwoFA" WHERE "UserName" = $1`;
332
+ const twoFAResult = await dblogin.query({ name: 'get-2fa-status', text: query, values: [trimmedUsername] });
267
333
 
268
334
  if (twoFAResult.rows.length > 0 && twoFAResult.rows[0].TwoFAStatus) {
269
335
  // 2FA is enabled, prompt for token on a separate page
@@ -271,9 +337,9 @@ router.post("/mbkauthe/api/login", LoginLimit, async (req, res) => {
271
337
  id: user.id,
272
338
  username: user.UserName,
273
339
  role: user.Role,
274
- Role: user.role,
340
+ Role: user.Role,
275
341
  };
276
- console.log(`[mbkauthe] 2FA required for user: ${username}`);
342
+ console.log(`[mbkauthe] 2FA required for user: ${trimmedUsername}`);
277
343
  return res.json({ success: true, twoFactorRequired: true });
278
344
  }
279
345
  }
@@ -283,12 +349,12 @@ router.post("/mbkauthe/api/login", LoginLimit, async (req, res) => {
283
349
  id: user.id,
284
350
  username: user.UserName,
285
351
  role: user.Role,
286
- Role: user.role,
352
+ Role: user.Role,
287
353
  };
288
354
  await completeLoginProcess(req, res, userForSession);
289
355
 
290
356
  } catch (err) {
291
- console.log("[mbkauthe] Error during login process:", err);
357
+ console.error("[mbkauthe] Error during login process:", err);
292
358
  res.status(500).json({ success: false, message: "Internal Server Error" });
293
359
  }
294
360
  });
@@ -304,7 +370,7 @@ router.get("/mbkauthe/2fa", csrfProtection, (req, res) => {
304
370
  });
305
371
  });
306
372
 
307
- router.post("/mbkauthe/api/verify-2fa", async (req, res) => {
373
+ router.post("/mbkauthe/api/verify-2fa", TwoFALimit, csrfProtection, async (req, res) => {
308
374
  if (!req.session.preAuthUser) {
309
375
  return res.status(401).json({ success: false, message: "Not authorized. Please login first." });
310
376
  }
@@ -312,10 +378,17 @@ router.post("/mbkauthe/api/verify-2fa", async (req, res) => {
312
378
  const { token } = req.body;
313
379
  const { username, id, role } = req.session.preAuthUser;
314
380
 
315
- if (!token) {
381
+ // Validate 2FA token
382
+ if (!token || typeof token !== 'string') {
316
383
  return res.status(400).json({ success: false, message: "2FA token is required" });
317
384
  }
318
385
 
386
+ // Validate token format (should be 6 digits)
387
+ const sanitizedToken = token.trim();
388
+ if (!/^\d{6}$/.test(sanitizedToken)) {
389
+ return res.status(400).json({ success: false, message: "Invalid 2FA token format" });
390
+ }
391
+
319
392
  try {
320
393
  const query = `SELECT "TwoFASecret" FROM "TwoFA" WHERE "UserName" = $1`;
321
394
  const twoFAResult = await dblogin.query(query, [username]);
@@ -328,7 +401,7 @@ router.post("/mbkauthe/api/verify-2fa", async (req, res) => {
328
401
  const tokenValidates = speakeasy.totp.verify({
329
402
  secret: sharedSecret,
330
403
  encoding: "base32",
331
- token: token,
404
+ token: sanitizedToken,
332
405
  window: 1,
333
406
  });
334
407
 
@@ -343,12 +416,12 @@ router.post("/mbkauthe/api/verify-2fa", async (req, res) => {
343
416
  await completeLoginProcess(req, res, userForSession, redirectUrl);
344
417
 
345
418
  } catch (err) {
346
- console.log("[mbkauthe] Error during 2FA verification:", err);
419
+ console.error("[mbkauthe] Error during 2FA verification:", err);
347
420
  res.status(500).json({ success: false, message: "Internal Server Error" });
348
421
  }
349
422
  });
350
423
 
351
- router.post("/mbkauthe/api/logout", async (req, res) => {
424
+ router.post("/mbkauthe/api/logout", LogoutLimit, csrfProtection, async (req, res) => {
352
425
  if (req.session.user) {
353
426
  try {
354
427
  const { id, username } = req.session.user;
@@ -361,11 +434,11 @@ router.post("/mbkauthe/api/logout", async (req, res) => {
361
434
 
362
435
  req.session.destroy((err) => {
363
436
  if (err) {
364
- console.log("[mbkauthe] Error destroying session:", err);
437
+ console.error("[mbkauthe] Error destroying session:", err);
365
438
  return res.status(500).json({ success: false, message: "Logout failed" });
366
439
  }
367
440
 
368
- const cookieOptions = getCookieOptions();
441
+ const cookieOptions = getClearCookieOptions();
369
442
  res.clearCookie("mbkauthe.sid", cookieOptions);
370
443
  res.clearCookie("sessionId", cookieOptions);
371
444
  res.clearCookie("username", cookieOptions);
@@ -374,7 +447,7 @@ router.post("/mbkauthe/api/logout", async (req, res) => {
374
447
  res.status(200).json({ success: true, message: "Logout successful" });
375
448
  });
376
449
  } catch (err) {
377
- console.log("[mbkauthe] Database query error during logout:", err);
450
+ console.error("[mbkauthe] Database query error during logout:", err);
378
451
  res.status(500).json({ success: false, message: "Internal Server Error" });
379
452
  }
380
453
  } else {
@@ -399,7 +472,7 @@ async function getLatestVersion() {
399
472
  try {
400
473
  const response = await fetch('https://raw.githubusercontent.com/MIbnEKhalid/mbkauthe/main/package.json');
401
474
  if (!response.ok) {
402
- console.Error(`GitHub API responded with status ${response.status}`);
475
+ console.error(`GitHub API responded with status ${response.status}`);
403
476
  return "0.0.0";
404
477
  }
405
478
  const latestPackageJson = await response.json();
@@ -556,7 +629,7 @@ router.get('/mbkauthe/api/github/login/callback',
556
629
  };
557
630
 
558
631
  // Generate session and complete login
559
- const sessionId = crypto.randomBytes(256).toString("hex");
632
+ const sessionId = crypto.randomBytes(32).toString("hex");
560
633
  console.log(`[mbkauthe] GitHub login: Generated session ID for username: ${user.UserName}`);
561
634
 
562
635
  // Delete old session record for this user
package/lib/pool.js CHANGED
@@ -15,15 +15,15 @@ if (!mbkautheVar) {
15
15
  }
16
16
  const requiredKeys = ["APP_NAME", "SESSION_SECRET_KEY", "IS_DEPLOYED", "LOGIN_DB", "MBKAUTH_TWO_FA_ENABLE", "DOMAIN"];
17
17
  requiredKeys.forEach(key => {
18
- if (!mbkautheVar[key]) {
19
- throw new Error(`mbkautheVar.${key} is required`);
20
- }
18
+ if (!mbkautheVar[key]) {
19
+ throw new Error(`mbkautheVar.${key} is required`);
20
+ }
21
21
  });
22
22
  if (mbkautheVar.COOKIE_EXPIRE_TIME !== undefined) {
23
- const expireTime = parseFloat(mbkautheVar.COOKIE_EXPIRE_TIME);
24
- if (isNaN(expireTime) || expireTime <= 0) {
25
- throw new Error("mbkautheVar.COOKIE_EXPIRE_TIME must be a valid positive number");
26
- }
23
+ const expireTime = parseFloat(mbkautheVar.COOKIE_EXPIRE_TIME);
24
+ if (isNaN(expireTime) || expireTime <= 0) {
25
+ throw new Error("mbkautheVar.COOKIE_EXPIRE_TIME must be a valid positive number");
26
+ }
27
27
  }
28
28
 
29
29
  const poolConfig = {
@@ -36,7 +36,7 @@ const poolConfig = {
36
36
  // - keep max small to avoid exhausting DB connections
37
37
  // - reduce idle time so connections are returned sooner
38
38
  // - set a short connection timeout to fail fast
39
- max: 6,
39
+ max: 10,
40
40
  idleTimeoutMillis: 50000,
41
41
  connectionTimeoutMillis: 5000,
42
42
  };
@@ -11,13 +11,27 @@ const getCookieOptions = () => ({
11
11
  httpOnly: true
12
12
  });
13
13
 
14
+ const getClearCookieOptions = () => ({
15
+ domain: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
16
+ secure: mbkautheVar.IS_DEPLOYED === 'true' ? 'auto' : false,
17
+ sameSite: 'lax',
18
+ path: '/',
19
+ httpOnly: true
20
+ });
21
+
14
22
  async function validateSession(req, res, next) {
15
23
  if (!req.session.user && req.cookies.sessionId) {
16
24
  try {
17
25
  const sessionId = req.cookies.sessionId;
18
- const query = `SELECT id, "UserName", "Active", "Role", "SessionId" FROM "Users" WHERE "SessionId" = $1`;
19
- const result = await dblogin.query({ name: 'get-user-by-sessionid', text: query, values: [sessionId] });
20
- const userResult = result.rows[0];
26
+
27
+ // Validate sessionId format (should be 64 hex characters)
28
+ if (typeof sessionId !== 'string' || !/^[a-f0-9]{64}$/i.test(sessionId)) {
29
+ console.warn("[mbkauthe] Invalid sessionId format detected");
30
+ return next();
31
+ }
32
+
33
+ const query = `SELECT id, "UserName", "Active", "Role", "SessionId", "AllowedApps" FROM "Users" WHERE "SessionId" = $1 AND "Active" = true`;
34
+ const result = await dblogin.query({ name: 'get-user-by-sessionid', text: query, values: [sessionId] });
21
35
 
22
36
  if (result.rows.length > 0) {
23
37
  const user = result.rows[0];
@@ -25,7 +39,10 @@ async function validateSession(req, res, next) {
25
39
  id: user.id,
26
40
  username: user.UserName,
27
41
  UserName: user.UserName,
42
+ role: user.Role,
43
+ Role: user.Role,
28
44
  sessionId,
45
+ allowedApps: user.AllowedApps,
29
46
  };
30
47
  }
31
48
  } catch (err) {
@@ -48,15 +65,16 @@ async function validateSession(req, res, next) {
48
65
  }
49
66
 
50
67
  try {
51
- const { id, sessionId } = req.session.user;
52
- const query = `SELECT "SessionId", "Active", "Role", "AllowedApps" FROM "Users" WHERE "id" = $1`;
53
- const result = await dblogin.query({ name: 'get-user-by-id', text: query, values: [id] });
54
- const userResult = result.rows[0];
68
+ const { id, sessionId, role, allowedApps } = req.session.user;
55
69
 
56
- if (result.rows.length === 0 || userResult.SessionId !== sessionId) {
70
+ // Fetch only SessionId and Active status for validation (reduced query)
71
+ const query = `SELECT "SessionId", "Active" FROM "Users" WHERE "id" = $1`;
72
+ const result = await dblogin.query({ name: 'get-user-by-id', text: query, values: [id] });
73
+
74
+ if (result.rows.length === 0 || result.rows[0].SessionId !== sessionId) {
57
75
  console.log(`[mbkauthe] Session invalidated for user "${req.session.user.username}"`);
58
76
  req.session.destroy();
59
- const cookieOptions = getCookieOptions();
77
+ const cookieOptions = getClearCookieOptions();
60
78
  res.clearCookie("mbkauthe.sid", cookieOptions);
61
79
  res.clearCookie("sessionId", cookieOptions);
62
80
  res.clearCookie("username", cookieOptions);
@@ -70,10 +88,10 @@ async function validateSession(req, res, next) {
70
88
  });
71
89
  }
72
90
 
73
- if (!userResult.Active) {
91
+ if (!result.rows[0].Active) {
74
92
  console.log(`[mbkauthe] Account is inactive for user "${req.session.user.username}"`);
75
93
  req.session.destroy();
76
- const cookieOptions = getCookieOptions();
94
+ const cookieOptions = getClearCookieOptions();
77
95
  res.clearCookie("mbkauthe.sid", cookieOptions);
78
96
  res.clearCookie("sessionId", cookieOptions);
79
97
  res.clearCookie("username", cookieOptions);
@@ -87,12 +105,11 @@ async function validateSession(req, res, next) {
87
105
  });
88
106
  }
89
107
 
90
- if (userResult.Role !== "SuperAdmin") {
91
- const allowedApps = userResult.AllowedApps;
108
+ if (role !== "SuperAdmin") {
92
109
  if (!allowedApps || !allowedApps.some(app => app.toLowerCase() === mbkautheVar.APP_NAME.toLowerCase())) {
93
110
  console.warn(`[mbkauthe] User \"${req.session.user.username}\" is not authorized to use the application \"${mbkautheVar.APP_NAME}\"`);
94
111
  req.session.destroy();
95
- const cookieOptions = getCookieOptions();
112
+ const cookieOptions = getClearCookieOptions();
96
113
  res.clearCookie("mbkauthe.sid", cookieOptions);
97
114
  res.clearCookie("sessionId", cookieOptions);
98
115
  res.clearCookie("username", cookieOptions);
@@ -132,8 +149,8 @@ const checkRolePermission = (requiredRole, notAllowed) => {
132
149
 
133
150
  const userId = req.session.user.id;
134
151
 
135
- const query = `SELECT "Role" FROM "Users" WHERE "id" = $1`;
136
- const result = await dblogin.query({ name: 'get-role-by-id', text: query, values: [userId] });
152
+ const query = `SELECT "Role" FROM "Users" WHERE "id" = $1`;
153
+ const result = await dblogin.query({ name: 'get-role-by-id', text: query, values: [userId] });
137
154
 
138
155
  if (result.rows.length === 0) {
139
156
  return res.status(401).json({ success: false, message: "User not found" });
@@ -202,20 +219,29 @@ const authapi = (requiredRole = []) => {
202
219
  return (req, res, next) => {
203
220
  const token = req.headers["authorization"];
204
221
 
205
- if (typeof token === 'string') {
206
- console.log("[mbkauthe] [authapi] Received request with token:", token[0] + token[1] + token[2], ".....", token[63]);
207
- } else {
208
- console.log("[mbkauthe] [authapi] Token is not a valid string:", token);
209
- }
210
-
222
+ // Validate token
211
223
  if (!token) {
212
- console.log("[mbkauthe] [authapi] No token provided in the request headers");
224
+ console.warn("[mbkauthe] [authapi] No token provided in the request headers");
213
225
  return res.status(401).json({
214
226
  success: false,
215
227
  message: "Authorization token is required"
216
228
  });
217
229
  }
218
230
 
231
+ if (typeof token !== 'string' || token.length === 0 || token.length > 512) {
232
+ console.warn("[mbkauthe] [authapi] Invalid token format");
233
+ return res.status(401).json({
234
+ success: false,
235
+ message: "Invalid authorization token format"
236
+ });
237
+ }
238
+
239
+ if (typeof token === 'string' && token.length >= 64) {
240
+ console.log("[mbkauthe] [authapi] Received request with token:", token.substring(0, 3) + ".....", token.charAt(63));
241
+ } else {
242
+ console.log("[mbkauthe] [authapi] Received request with short token");
243
+ }
244
+
219
245
  // Single query to validate API key and fetch user in one DB round trip.
220
246
  (async () => {
221
247
  try {
@@ -223,27 +249,27 @@ const authapi = (requiredRole = []) => {
223
249
  SELECT u.id, u."UserName", u."Active", u."Role", k."key" as apikey
224
250
  FROM "UserAuthApiKey" k
225
251
  JOIN "Users" u ON u."UserName" = k.username
226
- + WHERE k."key" = $1 AND u."Active" = true
227
- + LIMIT 1
228
- + `;
252
+ WHERE k."key" = $1 AND u."Active" = true
253
+ LIMIT 1
254
+ `;
229
255
 
230
256
  const result = await pool.query(jointQuery, [token]);
231
257
 
232
258
  if (result.rows.length === 0) {
233
- console.log("[mbkauthe] [authapi] Invalid token or associated user inactive:", token);
259
+ console.warn("[mbkauthe] [authapi] Invalid token or associated user inactive");
234
260
  return res.status(401).json({ success: false, message: "The AuthApiToken Is Invalid or user inactive" });
235
261
  }
236
262
 
237
263
  const user = result.rows[0];
238
264
 
239
265
  if (user.UserName === "demo") {
240
- console.log("[mbkauthe] [authapi] Demo user attempted to access an endpoint. Access denied.");
266
+ console.warn("[mbkauthe] [authapi] Demo user attempted to access an endpoint. Access denied.");
241
267
  return res.status(401).json({ success: false, message: "Demo user is not allowed to access endpoints" });
242
268
  }
243
269
 
244
270
  // role check
245
271
  if ((requiredRole && user.Role !== requiredRole) && user.Role !== "SuperAdmin") {
246
- console.log(`[mbkauthe] [authapi] User does not have the required role. Required: ${requiredRole}, User's role: ${user.Role}`);
272
+ console.warn(`[mbkauthe] [authapi] User does not have the required role. Required: ${requiredRole}, User's role: ${user.Role}`);
247
273
  return res.status(403).json({ success: false, message: `Access denied. Required role: ${requiredRole}` });
248
274
  }
249
275
 
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "mbkauthe",
3
- "version": "1.3.5",
3
+ "version": "1.4.0",
4
4
  "description": "MBKTechStudio's reusable authentication system for Node.js applications.",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
7
  "scripts": {
8
- "test": "set test=true&& nodemon index.js"
8
+ "dev": "set test=dev&& nodemon index.js"
9
9
  },
10
10
  "repository": {
11
11
  "type": "git",
@@ -26,11 +26,11 @@
26
26
  },
27
27
  "homepage": "https://github.com/MIbnEKhalid/mbkauthe#readme",
28
28
  "dependencies": {
29
- "bcrypt": "^5.1.1",
29
+ "bcrypt": "^6.0.0",
30
30
  "cheerio": "^1.0.0",
31
31
  "connect-pg-simple": "^10.0.0",
32
32
  "cookie-parser": "^1.4.7",
33
- "csurf": "^1.11.0",
33
+ "csurf": "^1.2.2",
34
34
  "dotenv": "^16.4.7",
35
35
  "express": "^5.1.0",
36
36
  "express-handlebars": "^8.0.1",
@@ -44,6 +44,6 @@
44
44
  "url": "^0.11.4"
45
45
  },
46
46
  "devDependencies": {
47
- "nodemon": "^2.0.22"
47
+ "nodemon": "^3.1.11"
48
48
  }
49
49
  }