mbkauthe 1.0.16 → 1.0.18

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 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' // Unique session cookie name
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) {
@@ -94,12 +79,8 @@ router.use(async (req, res, next) => {
94
79
  req.session.user = {
95
80
  id: user.id,
96
81
  username: user.UserName,
97
- UserName: user.UserName,
98
- Role: user.Role,
99
- role: user.Role,
100
82
  sessionId,
101
83
  };
102
- console.log(`Session restored for user: ${user.UserName}`);
103
84
  }
104
85
  } catch (err) {
105
86
  console.error("Session restoration error:", err);
@@ -108,23 +89,42 @@ router.use(async (req, res, next) => {
108
89
  next();
109
90
  });
110
91
 
111
- //Invoke-RestMethod -Uri http://localhost:3030/terminateAllSessions -Method POST
112
- // Terminate all sessions route
92
+ // Set consistent cookie options for all cookies
93
+ const getCookieOptions = () => ({
94
+ maxAge: COOKIE_EXPIRE_TIME,
95
+ domain: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
96
+ secure: mbkautheVar.IS_DEPLOYED === 'true' ? 'auto' : false,
97
+ sameSite: 'lax',
98
+ path: '/',
99
+ httpOnly: true
100
+ });
101
+
102
+ router.use(async (req, res, next) => {
103
+ if (req.session && req.session.user) {
104
+ const cookieOptions = getCookieOptions();
105
+ res.cookie("username", req.session.user.username, cookieOptions);
106
+ res.cookie("sessionId", req.session.user.sessionId, cookieOptions);
107
+ }
108
+ next();
109
+ });
110
+
113
111
  router.post("/mbkauthe/api/terminateAllSessions", authenticate(mbkautheVar.Main_SECRET_TOKEN), async (req, res) => {
114
112
  try {
115
113
  await dblogin.query(`UPDATE "Users" SET "SessionId" = NULL`);
116
-
117
- // Clear the session table
118
114
  await dblogin.query('DELETE FROM "session"');
119
115
 
120
- // Destroy all sessions on the server
121
116
  req.session.destroy((err) => {
122
117
  if (err) {
123
118
  console.log("Error destroying session:", err);
124
- return res
125
- .status(500)
126
- .json({ success: false, message: "Failed to terminate sessions" });
119
+ return res.status(500).json({ success: false, message: "Failed to terminate sessions" });
127
120
  }
121
+
122
+ // Clear all cookies with proper domain
123
+ const cookieOptions = getCookieOptions();
124
+ res.clearCookie("mbkauthe.sid", cookieOptions);
125
+ res.clearCookie("sessionId", cookieOptions);
126
+ res.clearCookie("username", cookieOptions);
127
+
128
128
  console.log("All sessions terminated successfully");
129
129
  res.status(200).json({
130
130
  success: true,
@@ -133,23 +133,20 @@ router.post("/mbkauthe/api/terminateAllSessions", authenticate(mbkautheVar.Main_
133
133
  });
134
134
  } catch (err) {
135
135
  console.log("Database query error during session termination:", err);
136
- res
137
- .status(500)
138
- .json({ success: false, message: "Internal Server Error" });
136
+ res.status(500).json({ success: false, message: "Internal Server Error" });
139
137
  }
140
- }
141
- );
138
+ });
142
139
 
143
140
  router.post("/mbkauthe/api/login", async (req, res) => {
144
- console.log("Login request received"); // Log when login is initiated
141
+ console.log("Login request received");
145
142
 
146
143
  const { username, password, token, recaptcha } = req.body;
147
- console.log(`Login attempt for username: ${username}`); // Log username
144
+ console.log(`Login attempt for username: ${username}`);
148
145
 
149
146
  const secretKey = mbkautheVar.RECAPTCHA_SECRET_KEY;
150
147
  const verificationUrl = `https://www.google.com/recaptcha/api/siteverify?secret=${secretKey}&response=${recaptcha}`;
151
148
 
152
- let BypassUsers = Array.isArray(mbkautheVar.BypassUsers) ? mbkautheVar.BypassUsers : JSON.parse(mbkautheVar.BypassUsers); // Ensure it's a flat array
149
+ let BypassUsers = Array.isArray(mbkautheVar.BypassUsers) ? mbkautheVar.BypassUsers : JSON.parse(mbkautheVar.BypassUsers);
153
150
 
154
151
  if (mbkautheVar.RECAPTCHA_Enabled === "true") {
155
152
  if (!BypassUsers.includes(username)) {
@@ -160,7 +157,7 @@ router.post("/mbkauthe/api/login", async (req, res) => {
160
157
  try {
161
158
  const response = await fetch(verificationUrl, { method: 'POST' });
162
159
  const body = await response.json();
163
- console.log("reCAPTCHA verification response:", body); // Log reCAPTCHA response
160
+ console.log("reCAPTCHA verification response:", body);
164
161
 
165
162
  if (!body.success) {
166
163
  console.log("Failed reCAPTCHA verification");
@@ -182,7 +179,6 @@ router.post("/mbkauthe/api/login", async (req, res) => {
182
179
  }
183
180
 
184
181
  try {
185
- // Query to check if the username exists
186
182
  const userQuery = `SELECT * FROM "Users" WHERE "UserName" = $1`;
187
183
  const userResult = await dblogin.query(userQuery, [username]);
188
184
 
@@ -193,20 +189,16 @@ router.post("/mbkauthe/api/login", async (req, res) => {
193
189
 
194
190
  const user = userResult.rows[0];
195
191
 
196
- // Check if the password matches
197
192
  if (user.Password !== password) {
198
193
  console.log(`Incorrect password for username: ${username}`);
199
194
  return res.status(401).json({ success: false, message: "Incorrect Username Or Password" });
200
195
  }
201
196
 
202
- // Check if the account is inactive
203
197
  if (!user.Active) {
204
198
  console.log(`Inactive account for username: ${username}`);
205
199
  return res.status(403).json({ success: false, message: "Account is inactive" });
206
200
  }
207
201
 
208
-
209
- // Check if the user is authorized to use the application
210
202
  if (user.Role !== "SuperAdmin") {
211
203
  const allowedApps = user.AllowedApps;
212
204
  if (!allowedApps || !allowedApps.includes(mbkautheVar.APP_NAME)) {
@@ -219,7 +211,7 @@ router.post("/mbkauthe/api/login", async (req, res) => {
219
211
  let sharedSecret;
220
212
  const query = `SELECT "TwoFAStatus", "TwoFASecret" FROM "TwoFA" WHERE "UserName" = $1`;
221
213
  const twoFAResult = await dblogin.query(query, [username]);
222
- console.log("TwoFA query result:", twoFAResult.rows); // Log TwoFA query result
214
+ console.log("TwoFA query result:", twoFAResult.rows);
223
215
 
224
216
  sharedSecret = twoFAResult.rows[0]?.TwoFASecret;
225
217
  if (twoFAResult.rows.length > 0 && twoFAResult.rows[0].TwoFAStatus && !token) {
@@ -232,7 +224,7 @@ router.post("/mbkauthe/api/login", async (req, res) => {
232
224
  secret: sharedSecret,
233
225
  encoding: "base32",
234
226
  token: token,
235
- window: 1, // Allows a margin for clock drift, optional
227
+ window: 1,
236
228
  });
237
229
 
238
230
  if (!tokenValidates) {
@@ -242,16 +234,14 @@ router.post("/mbkauthe/api/login", async (req, res) => {
242
234
  }
243
235
  }
244
236
 
245
- // Generate session ID
246
237
  const sessionId = crypto.randomBytes(256).toString("hex");
247
- console.log(`Generated session ID for username: ${username}`); // Log session ID
238
+ console.log(`Generated session ID for username: ${username}`);
248
239
 
249
240
  await dblogin.query(`UPDATE "Users" SET "SessionId" = $1 WHERE "id" = $2`, [
250
241
  sessionId,
251
242
  user.id,
252
243
  ]);
253
244
 
254
- // Store session ID in session
255
245
  req.session.user = {
256
246
  id: user.id,
257
247
  username: user.UserName,
@@ -259,15 +249,10 @@ router.post("/mbkauthe/api/login", async (req, res) => {
259
249
  sessionId,
260
250
  };
261
251
 
262
- res.cookie("sessionId", sessionId, {
263
- maxAge: COOKIE_EXPIRE_TIME,
264
- path: '/',
265
- DOMAIN: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
266
- secure: mbkautheVar.IS_DEPLOYED === 'true',
267
- });
252
+ const cookieOptions = getCookieOptions();
253
+ res.cookie("sessionId", sessionId, cookieOptions);
268
254
  console.log(req.session.user);
269
255
 
270
-
271
256
  console.log(`User "${username}" logged in successfully`);
272
257
  res.status(200).json({
273
258
  success: true,
@@ -284,11 +269,13 @@ router.post("/mbkauthe/api/logout", async (req, res) => {
284
269
  if (req.session.user) {
285
270
  try {
286
271
  const { id, username } = req.session.user;
287
- const query = `SELECT "Active" FROM "Users" WHERE "id" = $1`;
288
- const result = await dblogin.query(query, [id]);
289
-
290
- if (result.rows.length > 0 && !result.rows[0].Active) {
291
- console.log("Account is inactive during logout");
272
+
273
+ // Clear the SessionId in the database first
274
+ await dblogin.query(`UPDATE "Users" SET "SessionId" = NULL WHERE "id" = $1`, [id]);
275
+
276
+ // Remove the session from the session table
277
+ if (req.sessionID) {
278
+ await dblogin.query('DELETE FROM "session" WHERE sid = $1', [req.sessionID]);
292
279
  }
293
280
 
294
281
  req.session.destroy((err) => {
@@ -296,9 +283,13 @@ router.post("/mbkauthe/api/logout", async (req, res) => {
296
283
  console.log("Error destroying session:", err);
297
284
  return res.status(500).json({ success: false, message: "Logout failed" });
298
285
  }
299
- // Clear both session cookies
300
- res.clearCookie("connect.sid");
301
- res.clearCookie("sessionId"); // Clear the sessionId cookie used for restoration
286
+
287
+ // Clear all cookies with proper domain
288
+ const cookieOptions = getCookieOptions();
289
+ res.clearCookie("mbkauthe.sid", cookieOptions);
290
+ res.clearCookie("sessionId", cookieOptions);
291
+ res.clearCookie("username", cookieOptions);
292
+
302
293
  console.log(`User "${username}" logged out successfully`);
303
294
  res.status(200).json({ success: true, message: "Logout successful" });
304
295
  });
@@ -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
- res.clearCookie("mbkauthe.sid", { domain: `.${mbkautheVar.DOMAIN}` });
46
- res.clearCookie("sessionId", { domain: `.${mbkautheVar.DOMAIN}` });
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
- res.clearCookie("mbkauthe.sid", { domain: `.${mbkautheVar.DOMAIN}` });
56
- res.clearCookie("sessionId", { domain: `.${mbkautheVar.DOMAIN}` });
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
- res.clearCookie("mbkauthe.sid", { domain: `.${mbkautheVar.DOMAIN}` });
68
- res.clearCookie("sessionId", { domain: `.${mbkautheVar.DOMAIN}` });
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,7 +147,6 @@ 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
151
  "Password", "UserName", "Role", "Active", "GuestRole", "HaveMailAccount", "AllowedApps",
143
152
  ];
@@ -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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mbkauthe",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "description": "MBKTechStudio's reusable authentication system for Node.js applications.",
5
5
  "main": "index.js",
6
6
  "type": "module",