mbkauthe 1.4.1 → 2.0.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.
@@ -1,11 +1,10 @@
1
1
  import { dblogin } from "./pool.js";
2
2
  const mbkautheVar = JSON.parse(process.env.mbkautheVar);
3
- let pool = dblogin;
4
3
 
5
4
  const getCookieOptions = () => ({
6
5
  maxAge: mbkautheVar.COOKIE_EXPIRE_TIME * 24 * 60 * 60 * 1000,
7
6
  domain: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
8
- secure: mbkautheVar.IS_DEPLOYED === 'true' ? 'auto' : false,
7
+ secure: mbkautheVar.IS_DEPLOYED === 'true',
9
8
  sameSite: 'lax',
10
9
  path: '/',
11
10
  httpOnly: true
@@ -13,44 +12,13 @@ const getCookieOptions = () => ({
13
12
 
14
13
  const getClearCookieOptions = () => ({
15
14
  domain: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
16
- secure: mbkautheVar.IS_DEPLOYED === 'true' ? 'auto' : false,
15
+ secure: mbkautheVar.IS_DEPLOYED === 'true',
17
16
  sameSite: 'lax',
18
17
  path: '/',
19
18
  httpOnly: true
20
19
  });
21
20
 
22
21
  async function validateSession(req, res, next) {
23
- if (!req.session.user && req.cookies.sessionId) {
24
- try {
25
- const sessionId = req.cookies.sessionId;
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] });
35
-
36
- if (result.rows.length > 0) {
37
- const user = result.rows[0];
38
- req.session.user = {
39
- id: user.id,
40
- username: user.UserName,
41
- UserName: user.UserName,
42
- role: user.Role,
43
- Role: user.Role,
44
- sessionId,
45
- allowedApps: user.AllowedApps,
46
- };
47
- }
48
- } catch (err) {
49
- console.error("[mbkauthe] Session validation error:", err);
50
- return res.status(500).json({ success: false, message: "Internal Server Error" });
51
- }
52
- }
53
-
54
22
  if (!req.session.user) {
55
23
  console.log("[mbkauthe] User not authenticated");
56
24
  console.log("[mbkauthe]: ", req.session.user);
@@ -67,11 +35,14 @@ async function validateSession(req, res, next) {
67
35
  try {
68
36
  const { id, sessionId, role, allowedApps } = req.session.user;
69
37
 
70
- // Fetch only SessionId and Active status for validation (reduced query)
38
+ // Normalize sessionId to lowercase for consistent comparison
39
+ const normalizedSessionId = sessionId.toLowerCase();
40
+
41
+ // Single optimized query to validate session
71
42
  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] });
43
+ const result = await dblogin.query({ name: 'validate-user-session', text: query, values: [id] });
73
44
 
74
- if (result.rows.length === 0 || result.rows[0].SessionId !== sessionId) {
45
+ if (result.rows.length === 0 || result.rows[0].SessionId.toLowerCase() !== normalizedSessionId) {
75
46
  console.log(`[mbkauthe] Session invalidated for user "${req.session.user.username}"`);
76
47
  req.session.destroy();
77
48
  const cookieOptions = getClearCookieOptions();
@@ -101,7 +72,7 @@ async function validateSession(req, res, next) {
101
72
  error: "Account Inactive",
102
73
  message: "Your Account Is Inactive. Please Contact Support.",
103
74
  pagename: "Support",
104
- page: "https://mbktechstudio.com/Support",
75
+ page: "https://mbktech.org/Support",
105
76
  });
106
77
  }
107
78
 
@@ -131,12 +102,11 @@ async function validateSession(req, res, next) {
131
102
  }
132
103
  }
133
104
 
134
- const checkRolePermission = (requiredRole, notAllowed) => {
105
+ const checkRolePermission = (requiredRoles, notAllowed) => {
135
106
  return async (req, res, next) => {
136
107
  try {
137
108
  if (!req.session || !req.session.user || !req.session.user.id) {
138
109
  console.log("[mbkauthe] User not authenticated");
139
- console.log("[mbkauthe]: ", req.session);
140
110
  return res.render("Error/dError.handlebars", {
141
111
  layout: false,
142
112
  code: 401,
@@ -150,10 +120,10 @@ const checkRolePermission = (requiredRole, notAllowed) => {
150
120
  const userId = req.session.user.id;
151
121
 
152
122
  const query = `SELECT "Role" FROM "Users" WHERE "id" = $1`;
153
- const result = await dblogin.query({ name: 'get-role-by-id', text: query, values: [userId] });
123
+ const result = await dblogin.query({ name: 'check-role-permission', text: query, values: [userId] });
154
124
 
155
125
  if (result.rows.length === 0) {
156
- return res.status(401).json({ success: false, message: "User not found" });
126
+ return res.status(401).json({ success: false, message: "Authentication failed" });
157
127
  }
158
128
 
159
129
  const userRole = result.rows[0].Role;
@@ -164,22 +134,27 @@ const checkRolePermission = (requiredRole, notAllowed) => {
164
134
  layout: false,
165
135
  code: 403,
166
136
  error: "Access Denied",
167
- message: `You are not allowed to access this resource with role: ${notAllowed}`,
137
+ message: "You are not allowed to access this resource",
168
138
  pagename: "Home",
169
139
  page: `/${mbkautheVar.loginRedirectURL}`
170
140
  });
171
141
  }
172
142
 
173
- if (requiredRole === "Any" || requiredRole === "any") {
143
+ // Convert to array if single role provided
144
+ const rolesArray = Array.isArray(requiredRoles) ? requiredRoles : [requiredRoles];
145
+
146
+ // Check for "Any" or "any" role
147
+ if (rolesArray.includes("Any") || rolesArray.includes("any")) {
174
148
  return next();
175
149
  }
176
150
 
177
- if (userRole !== requiredRole) {
151
+ // Check if user role is in allowed roles
152
+ if (!rolesArray.includes(userRole)) {
178
153
  return res.render("Error/dError.handlebars", {
179
154
  layout: false,
180
155
  code: 403,
181
156
  error: "Access Denied",
182
- message: `You do not have permission to access this resource. Required role: ${requiredRole}`,
157
+ message: "You do not have permission to access this resource",
183
158
  pagename: "Home",
184
159
  page: `/${mbkautheVar.loginRedirectURL}`
185
160
  });
@@ -253,7 +228,7 @@ const authapi = (requiredRole = []) => {
253
228
  LIMIT 1
254
229
  `;
255
230
 
256
- const result = await pool.query(jointQuery, [token]);
231
+ const result = await dblogin.query({ name: 'validate-api-key', text: jointQuery, values: [token] });
257
232
 
258
233
  if (result.rows.length === 0) {
259
234
  console.warn("[mbkauthe] [authapi] Invalid token or associated user inactive");
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "mbkauthe",
3
- "version": "1.4.1",
4
- "description": "MBKTechStudio's reusable authentication system for Node.js applications.",
3
+ "version": "2.0.0",
4
+ "description": "MBKTech's reusable authentication system for Node.js applications.",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
7
  "scripts": {
8
- "dev": "set test=dev&& nodemon index.js"
8
+ "dev": "cross-env test=dev nodemon index.js"
9
9
  },
10
10
  "repository": {
11
11
  "type": "git",
@@ -41,12 +41,15 @@
41
41
  "express-session": "^1.18.1",
42
42
  "marked": "^15.0.11",
43
43
  "node-fetch": "^3.3.2",
44
+ "passport": "^0.7.0",
45
+ "passport-github2": "^0.1.12",
44
46
  "path": "^0.12.7",
45
47
  "pg": "^8.14.1",
46
48
  "speakeasy": "^2.0.0",
47
49
  "url": "^0.11.4"
48
50
  },
49
51
  "devDependencies": {
52
+ "cross-env": "^7.0.3",
50
53
  "nodemon": "^3.1.11"
51
54
  }
52
55
  }
package/public/bg.avif ADDED
Binary file
package/public/main.js CHANGED
@@ -5,9 +5,7 @@ async function logout() {
5
5
  }
6
6
 
7
7
  try {
8
- // Clear all caches before logging out (except rememberedUsername)
9
- await nuclearCacheClear();
10
-
8
+ // First, logout from server
11
9
  const response = await fetch("/mbkauthe/api/logout", {
12
10
  method: "POST",
13
11
  headers: {
@@ -20,15 +18,15 @@ async function logout() {
20
18
  const result = await response.json();
21
19
 
22
20
  if (response.ok) {
23
- alert(result.message);
24
- // Force a full page reload with cache bypass
25
- window.location.href = window.location.origin;
21
+ // Then clear all caches after successful logout (except rememberedUsername)
22
+ await nuclearCacheClear();
23
+ // nuclearCacheClear already redirects, so no need for additional redirect
26
24
  } else {
27
25
  alert(result.message);
28
26
  }
29
27
  } catch (error) {
30
28
  console.error("Error during logout:", error);
31
- alert("Logout failed");
29
+ alert(`Logout failed: ${error.message}`);
32
30
  }
33
31
  }
34
32
 
@@ -5,13 +5,13 @@
5
5
  <meta charset="UTF-8">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
  <meta name="description"
8
- content="Log in to portal.mbktechstudio.com to access your resources and manage projects securely.">
8
+ content="Log in to portal.mbktech.org to access your resources and manage projects securely.">
9
9
  <meta name="keywords" content="MBK Tech Studio, Web-Portal, Web, Portal, Admin-Panel, Admin, login">
10
10
  <meta property="og:title" content="Login | MBK Tech Studio" />
11
- <meta property="og:image" content="https://www.mbktechstudio.com/Assets/Images/Icon/logo.png" />
11
+ <meta property="og:image" content="https://mbktech.org/Assets/Images/Icon/logo.png" />
12
12
  <meta property="og:url" content="/mbkauthe/2fa">
13
13
  <title>2FA | MBK Tech Studio Portal</title>
14
- <link rel="icon" type="image/x-icon" href="https://mbktechstudio.com/Assets/Images/Icon/dgicon.svg">
14
+ <link rel="icon" type="image/x-icon" href="https://mbktech.org/Assets/Images/Icon/dgicon.svg">
15
15
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
16
16
  {{> sharedStyles}}
17
17
  </head>
@@ -28,7 +28,7 @@
28
28
  </path>
29
29
  </g>
30
30
  </svg>
31
- <span class="logo-text">{{appName}} <span class="logo-comp">MBKTECHStudio</span></span>
31
+ <span class="logo-text">{{appName}} <span class="logo-comp">mbktech</span></span>
32
32
  </a>
33
33
  </div>
34
34
  </header>
@@ -63,10 +63,10 @@
63
63
 
64
64
  <p class="terms-info">
65
65
  By logging in, you agree to our
66
- <a href="https://portal.mbktechstudio.com/info/Terms&Conditions" target="_blank"
66
+ <a href="https://portal.mbktech.org/info/Terms&Conditions" target="_blank"
67
67
  class="terms-link">Terms & Conditions</a>
68
68
  and
69
- <a href="https://portal.mbktechstudio.com/info/PrivacyPolicy" target="_blank"
69
+ <a href="https://portal.mbktech.org/info/PrivacyPolicy" target="_blank"
70
70
  class="terms-link">Privacy Policy</a>.
71
71
  </p>
72
72
  </form>
@@ -5,7 +5,7 @@
5
5
  <meta charset="UTF-8">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
  <title>{{code}} - {{error}}</title>
8
- <link rel="icon" type="image/x-icon" href="https://mbktechstudio.com/Assets/Images/Icon/dgicon.svg">
8
+ <link rel="icon" type="image/x-icon" href="https://mbktech.org/Assets/Images/Icon/dgicon.svg">
9
9
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
10
10
  {{> sharedStyles}}
11
11
  <style>
@@ -186,8 +186,8 @@
186
186
  </path>
187
187
  </g>
188
188
  </svg>
189
- <span class="logo-text">{{#if app}}{{app}}{{else}}MBK Authe{{/if}} <span
190
- class="logo-comp">MBKTECHStudio</span></span>
189
+ <span class="logo-text">{{#if app}}{{app}}{{else}}mbkauthe{{/if}} <span
190
+ class="logo-comp">mbktech</span></span>
191
191
  </a>
192
192
  </div>
193
193
  </header>
@@ -5,7 +5,7 @@
5
5
  <meta charset="UTF-8">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
  <title>Version and Configuration Information</title>
8
- <link rel="icon" type="image/x-icon" href="https://mbktechstudio.com/Assets/Images/Icon/dgicon.svg">
8
+ <link rel="icon" type="image/x-icon" href="https://mbktech.org/Assets/Images/Icon/dgicon.svg">
9
9
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
10
10
  {{> sharedStyles}}
11
11
  <style>
@@ -184,7 +184,7 @@
184
184
  </path>
185
185
  </g>
186
186
  </svg>
187
- <span class="logo-text">{{mbkautheVar.APP_NAME}} <span class="logo-comp">MBKTECHStudio</span></span>
187
+ <span class="logo-text">{{mbkautheVar.APP_NAME}} <span class="logo-comp">mbktech</span></span>
188
188
  </a>
189
189
  </div>
190
190
  </header>
@@ -224,6 +224,16 @@
224
224
  <div class="info-value">{{mbkautheVar.APP_NAME}}</div>
225
225
  </div>
226
226
  <div class="info-row">
227
+ <div class="info-label">Domain:</div>
228
+ <div class="info-value">{{mbkautheVar.DOMAIN}}</div>
229
+ </div>
230
+ <div class="info-row">
231
+ <div class="info-label">Login Redirect URL:</div>
232
+ <div class="info-value">{{mbkautheVar.loginRedirectURL}}</div>
233
+ </div>
234
+ {{#if authorized}}
235
+
236
+ <div class="info-row">
227
237
  <div class="info-label">Two Factor Authentication:</div>
228
238
  <div class="info-value">{{mbkautheVar.MBKAUTH_TWO_FA_ENABLE}}</div>
229
239
  </div>
@@ -235,14 +245,7 @@
235
245
  <div class="info-label">Deployment Status:</div>
236
246
  <div class="info-value">{{mbkautheVar.IS_DEPLOYED}}</div>
237
247
  </div>
238
- <div class="info-row">
239
- <div class="info-label">Domain:</div>
240
- <div class="info-value">{{mbkautheVar.DOMAIN}}</div>
241
- </div>
242
- <div class="info-row">
243
- <div class="info-label">Login Redirect URL:</div>
244
- <div class="info-value">{{mbkautheVar.loginRedirectURL}}</div>
245
- </div>
248
+ {{/if}}
246
249
  </div>
247
250
  </div>
248
251
  </section>
@@ -5,13 +5,13 @@
5
5
  <meta charset="UTF-8">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
  <meta name="description"
8
- content="Log in to portal.mbktechstudio.com to access your resources and manage projects securely.">
8
+ content="Log in to portal.mbktech.org to access your resources and manage projects securely.">
9
9
  <meta name="keywords" content="MBK Tech Studio, Web-Portal, Web, Portal, Admin-Panel, Admin, login">
10
10
  <meta property="og:title" content="Login | MBK Tech Studio" />
11
- <meta property="og:image" content="https://www.mbktechstudio.com/Assets/Images/Icon/logo.png" />
11
+ <meta property="og:image" content="https://mbktech.org/Assets/Images/Icon/logo.png" />
12
12
  <meta property="og:url" content="/mbkauthe/login">
13
13
  <title>Login | MBK Tech Studio Portal</title>
14
- <link rel="icon" type="image/x-icon" href="https://mbktechstudio.com/Assets/Images/Icon/dgicon.svg">
14
+ <link rel="icon" type="image/x-icon" href="https://mbktech.org/Assets/Images/Icon/dgicon.svg">
15
15
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
16
16
  {{> sharedStyles}}
17
17
  </head>
@@ -28,7 +28,7 @@
28
28
  </path>
29
29
  </g>
30
30
  </svg>
31
- <span class="logo-text">{{appName}} <span class="logo-comp">MBKTECHStudio</span></span>
31
+ <span class="logo-text">{{appName}} <span class="logo-comp">mbktech</span></span>
32
32
  </a>
33
33
  </div>
34
34
  </header>
@@ -68,17 +68,16 @@
68
68
  <button type="submit" class="btn-login" id="loginButton">
69
69
  <span id="loginButtonText">Login</span>
70
70
  </button>
71
- {{#if githubLoginEnabled }}
72
71
  <div class="social-login">
73
72
  <div class="divider">
74
73
  <span>or</span>
75
74
  </div>
76
- <a href="/mbkauthe/api/github/login" class="btn-github">
75
+ <!-- Use JS to initiate GitHub login and pass redirect param to backend -->
76
+ <a type="button" id="githubLoginBtn" class="btn-github">
77
77
  <i class="fab fa-github"></i>
78
78
  <span>Continue with GitHub</span>
79
79
  </a>
80
80
  </div>
81
- {{/if }}
82
81
 
83
82
  <div class="remember-me">
84
83
  <input type="checkbox" id="rememberMe" name="rememberMe">
@@ -94,16 +93,16 @@
94
93
  {{/if }}
95
94
 
96
95
  <div class="login-links">
97
- <a href="https://portal.mbktechstudio.com/forgot-password" class="login-link">Forgot Password?</a>
98
- <a href="https://www.mbktechstudio.com/Support" target="_blank" class="login-link">Need Help?</a>
96
+ <a href="https://portal.mbktech.org/forgot-password" class="login-link">Forgot Password?</a>
97
+ <a href="https://mbktech.org/Support" target="_blank" class="login-link">Need Help?</a>
99
98
  </div>
100
99
 
101
100
  <p class="terms-info">
102
101
  By logging in, you agree to our
103
- <a href="https://portal.mbktechstudio.com/info/Terms&Conditions" target="_blank"
102
+ <a href="https://portal.mbktech.org/info/Terms&Conditions" target="_blank"
104
103
  class="terms-link">Terms & Conditions</a>
105
104
  and
106
- <a href="https://portal.mbktechstudio.com/info/PrivacyPolicy" target="_blank"
105
+ <a href="https://portal.mbktech.org/info/PrivacyPolicy" target="_blank"
107
106
  class="terms-link">Privacy Policy</a>.
108
107
  </p>
109
108
  </form>
@@ -128,13 +127,13 @@
128
127
  });
129
128
 
130
129
  function fpass() {
131
- showMessage(`If you have forgotten your password, please contact support at <a href="https://www.mbktechstudio.com/Support" target="_blank">https://www.mbktechstudio.com/Support</a> to reset it.`, `Forgot Password`);
130
+ showMessage(`If you have forgotten your password, please contact support at <a href="https://mbktech.org/Support" target="_blank">https://mbktech.org/Support</a> to reset it.`, `Forgot Password`);
132
131
  }
133
132
 
134
133
  // Info dialogs
135
134
  function usernameinfo() {
136
- showMessage(`Your username is the part of your MBK Tech Studio email before the @ (e.g., abc.xyz@mbktechstudio.com
137
- → abc.xyz). For guests or if you’ve forgotten your credentials, contact <a href="https://mbktechstudio.com/Support">Support</a>.`, `What is my username?`);
135
+ showMessage(`Your username is the part of your MBK Tech Studio email before the @ (e.g., abc.xyz@mbktech.org
136
+ → abc.xyz). For guests or if you’ve forgotten your credentials, contact <a href="https://mbktech.org/Support">Support</a>.`, `What is my username?`);
138
137
  }
139
138
 
140
139
  function tokeninfo() {
@@ -272,6 +271,45 @@
272
271
  document.getElementById('loginPassword').focus();
273
272
  }
274
273
  });
274
+
275
+ // GitHub login: Attempt to POST redirect to backend, fallback to direct navigation
276
+ async function startGithubLogin() {
277
+ const urlParams = new URLSearchParams(window.location.search);
278
+ const redirect = urlParams.get('redirect') || '{{customURL}}';
279
+
280
+ try {
281
+ // Try POSTing to the backend so it can establish any session state
282
+ const resp = await fetch('/mbkauthe/api/github/login', {
283
+ method: 'POST',
284
+ headers: { 'Content-Type': 'application/json' },
285
+ credentials: 'include',
286
+ body: JSON.stringify({ redirect })
287
+ });
288
+
289
+ // If backend responds with a JSON containing redirectUrl, navigate there
290
+ if (resp.ok) {
291
+ // If server redirected directly (resp.redirected), follow the final URL
292
+ if (resp.redirected) {
293
+ window.location.href = resp.url;
294
+ return;
295
+ }
296
+ const data = await resp.json().catch(() => null);
297
+ if (data && data.redirectUrl) {
298
+ window.location.href = data.redirectUrl;
299
+ return;
300
+ }
301
+ }
302
+ } catch (error) {
303
+ // swallow and fallback to direct navigation
304
+ console.warn('[mbkauthe] GitHub login POST failed, falling back to direct redirect', error);
305
+ }
306
+
307
+ // Fallback: navigate to the backend GET endpoint with redirect query
308
+ window.location.href = `/mbkauthe/api/github/login?redirect=${encodeURIComponent(redirect)}`;
309
+ }
310
+
311
+ const githubBtn = document.getElementById('githubLoginBtn');
312
+ if (githubBtn) githubBtn.addEventListener('click', startGithubLogin);
275
313
  </script>
276
314
  </body>
277
315
 
@@ -98,7 +98,7 @@
98
98
  left: 0;
99
99
  width: 100%;
100
100
  height: 100%;
101
- background: url(https://images.unsplash.com/photo-1555066931-4365d14bab8c?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80) center/cover no-repeat;
101
+ background: url(/mbkauthe/bg.avif) center/cover no-repeat;
102
102
  opacity: .1;
103
103
  z-index: 0;
104
104
  }
@@ -20,7 +20,7 @@
20
20
  document.querySelector(".showmessageWindow .error-code").style.display = "none";
21
21
  }
22
22
 
23
- document.querySelector(".showmessageWindow .error-code").href = `https://mbktechstudio.com/ErrorCode/#${errorCode}`;
23
+ document.querySelector(".showmessageWindow .error-code").href = `https://mbktech.org/ErrorCode/#${errorCode}`;
24
24
  */
25
25
  document
26
26
  .querySelector(".showMessageblurWindow")