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 +59 -68
- package/lib/validateSessionAndRole.js +23 -16
- 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) {
|
|
@@ -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
|
-
//
|
|
112
|
-
|
|
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");
|
|
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}`);
|
|
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);
|
|
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);
|
|
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);
|
|
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,
|
|
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}`);
|
|
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
|
-
|
|
263
|
-
|
|
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
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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
|
-
|
|
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,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) {
|