mbkauthe 2.0.1 → 2.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/lib/pool.js CHANGED
@@ -1,30 +1,6 @@
1
1
  import pkg from "pg";
2
2
  const { Pool } = pkg;
3
-
4
- import dotenv from "dotenv";
5
- dotenv.config();
6
-
7
- let mbkautheVar;
8
- try {
9
- mbkautheVar = JSON.parse(process.env.mbkautheVar);
10
- } catch (error) {
11
- throw new Error("Invalid JSON in process.env.mbkautheVar");
12
- }
13
- if (!mbkautheVar) {
14
- throw new Error("mbkautheVar is not defined");
15
- }
16
- const requiredKeys = ["APP_NAME", "SESSION_SECRET_KEY", "IS_DEPLOYED", "LOGIN_DB", "MBKAUTH_TWO_FA_ENABLE", "DOMAIN"];
17
- requiredKeys.forEach(key => {
18
- if (!mbkautheVar[key]) {
19
- throw new Error(`mbkautheVar.${key} is required`);
20
- }
21
- });
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
- }
27
- }
3
+ import { mbkautheVar } from "./config.js";
28
4
 
29
5
  const poolConfig = {
30
6
  connectionString: mbkautheVar.LOGIN_DB,
@@ -36,9 +12,9 @@ const poolConfig = {
36
12
  // - keep max small to avoid exhausting DB connections
37
13
  // - reduce idle time so connections are returned sooner
38
14
  // - set a short connection timeout to fail fast
39
- max: 20,
15
+ max: 10,
40
16
  idleTimeoutMillis: 10000,
41
- connectionTimeoutMillis: 5000,
17
+ connectionTimeoutMillis: 25000,
42
18
  };
43
19
 
44
20
  export const dblogin = new Pool(poolConfig);
@@ -1,29 +1,11 @@
1
1
  import { dblogin } from "./pool.js";
2
- const mbkautheVar = JSON.parse(process.env.mbkautheVar);
3
-
4
- const getCookieOptions = () => ({
5
- maxAge: mbkautheVar.COOKIE_EXPIRE_TIME * 24 * 60 * 60 * 1000,
6
- domain: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
7
- secure: mbkautheVar.IS_DEPLOYED === 'true',
8
- sameSite: 'lax',
9
- path: '/',
10
- httpOnly: true
11
- });
12
-
13
- const getClearCookieOptions = () => ({
14
- domain: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
15
- secure: mbkautheVar.IS_DEPLOYED === 'true',
16
- sameSite: 'lax',
17
- path: '/',
18
- httpOnly: true
19
- });
2
+ import { mbkautheVar, renderError, clearSessionCookies } from "./config.js";
20
3
 
21
4
  async function validateSession(req, res, next) {
22
5
  if (!req.session.user) {
23
6
  console.log("[mbkauthe] User not authenticated");
24
7
  console.log("[mbkauthe]: ", req.session.user);
25
- return res.render("Error/dError.handlebars", {
26
- layout: false,
8
+ return renderError(res, {
27
9
  code: 401,
28
10
  error: "Not Logged In",
29
11
  message: "You Are Not Logged In. Please Log In To Continue.",
@@ -38,19 +20,15 @@ async function validateSession(req, res, next) {
38
20
  // Normalize sessionId to lowercase for consistent comparison
39
21
  const normalizedSessionId = sessionId.toLowerCase();
40
22
 
41
- // Single optimized query to validate session
42
- const query = `SELECT "SessionId", "Active" FROM "Users" WHERE "id" = $1`;
23
+ // Single optimized query to validate session and get role
24
+ const query = `SELECT "SessionId", "Active", "Role" FROM "Users" WHERE "id" = $1`;
43
25
  const result = await dblogin.query({ name: 'validate-user-session', text: query, values: [id] });
44
26
 
45
27
  if (result.rows.length === 0 || result.rows[0].SessionId.toLowerCase() !== normalizedSessionId) {
46
28
  console.log(`[mbkauthe] Session invalidated for user "${req.session.user.username}"`);
47
29
  req.session.destroy();
48
- const cookieOptions = getClearCookieOptions();
49
- res.clearCookie("mbkauthe.sid", cookieOptions);
50
- res.clearCookie("sessionId", cookieOptions);
51
- res.clearCookie("username", cookieOptions);
52
- return res.render("Error/dError.handlebars", {
53
- layout: false,
30
+ clearSessionCookies(res);
31
+ return renderError(res, {
54
32
  code: 401,
55
33
  error: "Session Expired",
56
34
  message: "Your Session Has Expired. Please Log In Again.",
@@ -62,12 +40,8 @@ async function validateSession(req, res, next) {
62
40
  if (!result.rows[0].Active) {
63
41
  console.log(`[mbkauthe] Account is inactive for user "${req.session.user.username}"`);
64
42
  req.session.destroy();
65
- const cookieOptions = getClearCookieOptions();
66
- res.clearCookie("mbkauthe.sid", cookieOptions);
67
- res.clearCookie("sessionId", cookieOptions);
68
- res.clearCookie("username", cookieOptions);
69
- return res.render("Error/dError.handlebars", {
70
- layout: false,
43
+ clearSessionCookies(res);
44
+ return renderError(res, {
71
45
  code: 401,
72
46
  error: "Account Inactive",
73
47
  message: "Your Account Is Inactive. Please Contact Support.",
@@ -80,12 +54,8 @@ async function validateSession(req, res, next) {
80
54
  if (!allowedApps || !allowedApps.some(app => app.toLowerCase() === mbkautheVar.APP_NAME.toLowerCase())) {
81
55
  console.warn(`[mbkauthe] User \"${req.session.user.username}\" is not authorized to use the application \"${mbkautheVar.APP_NAME}\"`);
82
56
  req.session.destroy();
83
- const cookieOptions = getClearCookieOptions();
84
- res.clearCookie("mbkauthe.sid", cookieOptions);
85
- res.clearCookie("sessionId", cookieOptions);
86
- res.clearCookie("username", cookieOptions);
87
- return res.render("Error/dError.handlebars", {
88
- layout: false,
57
+ clearSessionCookies(res);
58
+ return renderError(res, {
89
59
  code: 401,
90
60
  error: "Unauthorized",
91
61
  message: `You Are Not Authorized To Use The Application \"${mbkautheVar.APP_NAME}\"`,
@@ -95,6 +65,9 @@ async function validateSession(req, res, next) {
95
65
  }
96
66
  }
97
67
 
68
+ // Store user role in request for checkRolePermission to use
69
+ req.userRole = result.rows[0].Role;
70
+
98
71
  next();
99
72
  } catch (err) {
100
73
  console.error("[mbkauthe] Session validation error:", err);
@@ -107,8 +80,7 @@ const checkRolePermission = (requiredRoles, notAllowed) => {
107
80
  try {
108
81
  if (!req.session || !req.session.user || !req.session.user.id) {
109
82
  console.log("[mbkauthe] User not authenticated");
110
- return res.render("Error/dError.handlebars", {
111
- layout: false,
83
+ return renderError(res, {
112
84
  code: 401,
113
85
  error: "Not Logged In",
114
86
  message: "You Are Not Logged In. Please Log In To Continue.",
@@ -117,21 +89,12 @@ const checkRolePermission = (requiredRoles, notAllowed) => {
117
89
  });
118
90
  }
119
91
 
120
- const userId = req.session.user.id;
121
-
122
- const query = `SELECT "Role" FROM "Users" WHERE "id" = $1`;
123
- const result = await dblogin.query({ name: 'check-role-permission', text: query, values: [userId] });
124
-
125
- if (result.rows.length === 0) {
126
- return res.status(401).json({ success: false, message: "Authentication failed" });
127
- }
128
-
129
- const userRole = result.rows[0].Role;
92
+ // Use role from validateSession to avoid additional DB query
93
+ const userRole = req.userRole;
130
94
 
131
95
  // Check notAllowed role
132
96
  if (notAllowed && userRole === notAllowed) {
133
- return res.render("Error/dError.handlebars", {
134
- layout: false,
97
+ return renderError(res, {
135
98
  code: 403,
136
99
  error: "Access Denied",
137
100
  message: "You are not allowed to access this resource",
@@ -150,8 +113,7 @@ const checkRolePermission = (requiredRoles, notAllowed) => {
150
113
 
151
114
  // Check if user role is in allowed roles
152
115
  if (!rolesArray.includes(userRole)) {
153
- return res.render("Error/dError.handlebars", {
154
- layout: false,
116
+ return renderError(res, {
155
117
  code: 403,
156
118
  error: "Access Denied",
157
119
  message: "You do not have permission to access this resource",
@@ -179,7 +141,6 @@ const validateSessionAndRole = (requiredRole, notAllowed) => {
179
141
  const authenticate = (authentication) => {
180
142
  return (req, res, next) => {
181
143
  const token = req.headers["authorization"];
182
- console.log(`[mbkauthe] Received token: ${token}`);
183
144
  if (token === authentication) {
184
145
  console.log("[mbkauthe] Authentication successful");
185
146
  next();
@@ -190,78 +151,5 @@ const authenticate = (authentication) => {
190
151
  };
191
152
  };
192
153
 
193
- const authapi = (requiredRole = []) => {
194
- return (req, res, next) => {
195
- const token = req.headers["authorization"];
196
-
197
- // Validate token
198
- if (!token) {
199
- console.warn("[mbkauthe] [authapi] No token provided in the request headers");
200
- return res.status(401).json({
201
- success: false,
202
- message: "Authorization token is required"
203
- });
204
- }
205
-
206
- if (typeof token !== 'string' || token.length === 0 || token.length > 512) {
207
- console.warn("[mbkauthe] [authapi] Invalid token format");
208
- return res.status(401).json({
209
- success: false,
210
- message: "Invalid authorization token format"
211
- });
212
- }
213
-
214
- if (typeof token === 'string' && token.length >= 64) {
215
- console.log("[mbkauthe] [authapi] Received request with token:", token.substring(0, 3) + ".....", token.charAt(63));
216
- } else {
217
- console.log("[mbkauthe] [authapi] Received request with short token");
218
- }
219
-
220
- // Single query to validate API key and fetch user in one DB round trip.
221
- (async () => {
222
- try {
223
- const jointQuery = `
224
- SELECT u.id, u."UserName", u."Active", u."Role", k."key" as apikey
225
- FROM "UserAuthApiKey" k
226
- JOIN "Users" u ON u."UserName" = k.username
227
- WHERE k."key" = $1 AND u."Active" = true
228
- LIMIT 1
229
- `;
230
-
231
- const result = await dblogin.query({ name: 'validate-api-key', text: jointQuery, values: [token] });
232
-
233
- if (result.rows.length === 0) {
234
- console.warn("[mbkauthe] [authapi] Invalid token or associated user inactive");
235
- return res.status(401).json({ success: false, message: "The AuthApiToken Is Invalid or user inactive" });
236
- }
237
-
238
- const user = result.rows[0];
239
-
240
- if (user.UserName === "demo") {
241
- console.warn("[mbkauthe] [authapi] Demo user attempted to access an endpoint. Access denied.");
242
- return res.status(401).json({ success: false, message: "Demo user is not allowed to access endpoints" });
243
- }
244
-
245
- // role check
246
- if ((requiredRole && user.Role !== requiredRole) && user.Role !== "SuperAdmin") {
247
- console.warn(`[mbkauthe] [authapi] User does not have the required role. Required: ${requiredRole}, User's role: ${user.Role}`);
248
- return res.status(403).json({ success: false, message: `Access denied. Required role: ${requiredRole}` });
249
- }
250
-
251
- req.user = {
252
- username: user.UserName,
253
- UserName: user.UserName,
254
- role: user.Role,
255
- Role: user.Role,
256
- };
257
-
258
- next();
259
- } catch (err) {
260
- console.error("[mbkauthe] [authapi] Database error while validating token/user:", err);
261
- return res.status(500).json({ success: false, message: "Internal Server Error" });
262
- }
263
- })();
264
- };
265
- };
266
154
 
267
- export { validateSession, checkRolePermission, validateSessionAndRole, authenticate, authapi };
155
+ export { validateSession, checkRolePermission, validateSessionAndRole, authenticate };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mbkauthe",
3
- "version": "2.0.1",
3
+ "version": "2.2.0",
4
4
  "description": "MBKTech's reusable authentication system for Node.js applications.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -19,7 +19,7 @@
19
19
  "session",
20
20
  "security"
21
21
  ],
22
- "author": "Muhammad Bin Khalid <support@mbktechstudio.com>",
22
+ "author": "Muhammad Bin Khalid <support@mbktech.org>",
23
23
  "license": "MPL-2.0",
24
24
  "bugs": {
25
25
  "url": "https://github.com/MIbnEKhalid/mbkauthe/issues"
package/public/bg.webp ADDED
Binary file
Binary file
@@ -0,0 +1,5 @@
1
+ <svg data-v-423bf9ae="" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90 90" class="iconAboveSlogan">
2
+ <g data-v-423bf9ae="" id="8db2a7f9-6efc-4f7e-ae5b-8ba33875da43" transform="matrix(2.8125,0,0,2.8125,0,0)" stroke="none" fill="#88df95">
3
+ <path d="M0 32h32V0H0v32zm19.377-19.492l6.936-6.936v20.855h-6.936V12.508zM5.688 5.572l6.936 6.936v13.919H5.688V5.572z"></path>
4
+ </g>
5
+ </svg>
@@ -1,61 +1,38 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="en">
3
3
 
4
- <head>
5
- <meta charset="UTF-8">
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <meta name="description"
8
- content="Log in to portal.mbktech.org to access your resources and manage projects securely.">
9
- <meta name="keywords" content="MBK Tech Studio, Web-Portal, Web, Portal, Admin-Panel, Admin, login">
10
- <meta property="og:title" content="Login | MBK Tech Studio" />
11
- <meta property="og:image" content="https://mbktech.org/Assets/Images/Icon/logo.png" />
12
- <meta property="og:url" content="/mbkauthe/2fa">
13
- <title>2FA | MBK Tech Studio Portal</title>
14
- <link rel="icon" type="image/x-icon" href="https://mbktech.org/Assets/Images/Icon/dgicon.svg">
15
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
16
- {{> sharedStyles}}
17
- </head>
4
+ {{> head pageTitle="2FA" ogUrl="/mbkauthe/2fa"}}
18
5
 
19
6
  <body>
20
- <header>
21
- <div class="header-container">
22
- <a class="logo">
23
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90 90" class="iconAboveSlogan">
24
- <g id="8db2a7f9-6efc-4f7e-ae5b-8ba33875da43" transform="matrix(2.8125,0,0,2.8125,0,0)" stroke="none"
25
- fill="#00b894">
26
- <path
27
- d="M0 32h32V0H0v32zm19.377-19.492l6.936-6.936v20.855h-6.936V12.508zM5.688 5.572l6.936 6.936v13.919H5.688V5.572z">
28
- </path>
29
- </g>
30
- </svg>
31
- <span class="logo-text">{{appName}} <span class="logo-comp">mbktech</span></span>
32
- </a>
33
- </div>
34
- </header>
7
+ {{> header}}
35
8
 
36
9
  {{> showmessage}}
37
10
 
38
11
  <section class="login-container">
39
12
 
40
- <i class="fas fa-robot ai-element"></i>
41
- <i class="fas fa-microchip ai-element"></i>
42
- <i class="fas fa-user-astronaut ai-element"></i>
43
- <i class="fas fa-satellite-dish ai-element"></i>
44
- <i class="fas fa-cloud ai-element"></i>
45
- <i class="fas fa-shield-alt ai-element"></i>
13
+ {{> backgroundElements}}
46
14
 
47
15
  <div class="login-box">
48
- <h1 class="login-title">Two Factor Authorization</h1>
16
+ <h1 class="login-title">Two-Factor Authentication</h1>
17
+ <p class="login-subtitle">Enter the 6-digit code from your authenticator app</p>
49
18
 
50
19
  <form id="loginForm" method="POST">
51
20
  <input type="hidden" name="_csrf" value="{{csrfToken}}">
52
21
  <div class="form-group token-container" id="tokenCon">
53
22
  <input id="token" class="form-input" type="text" name="token" placeholder=" " pattern="\d{6}"
54
- title="Token must be exactly 6 digits" maxlength="6" minlength="6" />
23
+ title="Token must be exactly 6 digits" maxlength="6" minlength="6" autocomplete="off" required />
55
24
  <label class="form-label">2FA Token</label>
56
25
  <i class="fas fa-info-circle input-icon" onclick="tokeninfo()"></i>
57
26
  </div>
58
27
 
28
+ <div class="trust-device-container">
29
+ <label class="trust-device-label">
30
+ <input type="checkbox" id="trustDevice" name="trustDevice" class="trust-device-checkbox" />
31
+ <span class="checkbox-custom"></span>
32
+ <span class="checkbox-text">Trust this device for {{DEVICE_TRUST_DURATION_DAYS}} days</span>
33
+ </label>
34
+ <i class="fas fa-info-circle trust-device-info" onclick="trustDeviceInfo()" title="Learn more about trusted devices"></i>
35
+ </div>
59
36
 
60
37
  <button type="submit" class="btn-login" id="loginButton">
61
38
  <span id="loginButtonText">Verify</span>
@@ -73,19 +50,21 @@
73
50
  </div>
74
51
  </section>
75
52
 
76
- <!-- Version Info -->
77
- <div class="version-info">
78
- v{{version}}
79
- </div>
53
+ {{> versionInfo}}
80
54
 
81
55
  <script>
82
56
  function tokeninfo() {
83
57
  showMessage(`The 2FA token is a 6-digit code generated by your authenticator app (such as Google Authenticator, Microsoft Authenticator, or Authy). Enter the code shown in your app to complete login. If you have not set up 2FA or are having trouble, please contact support.`, `What is the 2FA token?`);
84
58
  }
85
59
 
60
+ function trustDeviceInfo() {
61
+ showMessage(`When you trust this device, you won't need to enter a 2FA code for {{DEVICE_TRUST_DURATION_DAYS}} days when logging in from this browser. This makes logging in faster while keeping your account secure. You can revoke trusted devices at any time from your account settings.`, `Trust This Device`);
62
+ }
63
+
86
64
  document.getElementById('loginForm').addEventListener('submit', async (e) => {
87
65
  e.preventDefault();
88
66
  const token = document.getElementById('token').value;
67
+ const trustDevice = document.getElementById('trustDevice').checked;
89
68
  const csrfToken = document.querySelector('input[name="_csrf"]').value;
90
69
  const loginButton = document.getElementById('loginButton');
91
70
  const loginButtonText = document.getElementById('loginButtonText');
@@ -101,6 +80,7 @@
101
80
  },
102
81
  body: JSON.stringify({
103
82
  token,
83
+ trustDevice,
104
84
  _csrf: csrfToken
105
85
  })
106
86
  });
@@ -1,14 +1,7 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="en">
3
3
 
4
- <head>
5
- <meta charset="UTF-8">
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>{{code}} - {{error}}</title>
8
- <link rel="icon" type="image/x-icon" href="https://mbktech.org/Assets/Images/Icon/dgicon.svg">
9
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
10
- {{> sharedStyles}}
11
- <style>
4
+ {{> head pageCode=code pageError=error ogUrl="/error" extraStyles="<style>
12
5
  .login-box {
13
6
  max-width: 600px;
14
7
  }
@@ -171,34 +164,14 @@
171
164
  gap: 0.5rem;
172
165
  }
173
166
  }
174
- </style>
175
- </head>
167
+ </style>"}}
176
168
 
177
169
  <body>
178
- <header>
179
- <div class="header-container">
180
- <a class="logo">
181
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90 90" class="iconAboveSlogan">
182
- <g id="8db2a7f9-6efc-4f7e-ae5b-8ba33875da43" transform="matrix(2.8125,0,0,2.8125,0,0)" stroke="none"
183
- fill="#00b894">
184
- <path
185
- d="M0 32h32V0H0v32zm19.377-19.492l6.936-6.936v20.855h-6.936V12.508zM5.688 5.572l6.936 6.936v13.919H5.688V5.572z">
186
- </path>
187
- </g>
188
- </svg>
189
- <span class="logo-text">{{#if app}}{{app}}{{else}}mbkauthe{{/if}} <span
190
- class="logo-comp">mbktech</span></span>
191
- </a>
192
- </div>
193
- </header>
194
-
170
+ {{> header}}
195
171
 
196
172
  <section class="login-container">
197
173
 
198
- <i class="fas fa-server ai-element"></i>
199
- <i class="fas fa-database ai-element"></i>
200
- <i class="fas fa-network-wired ai-element"></i>
201
- <i class="fas fa-code ai-element"></i>
174
+ {{> backgroundElements}}
202
175
 
203
176
  <div class="login-box">
204
177
  <h1 class="status-code">{{code}}</h1>
@@ -230,10 +203,9 @@
230
203
 
231
204
  </div>
232
205
  </section>
233
- <!-- Version Info -->
234
- <div class="version-info">
235
- v{{version}}
236
- </div>
206
+
207
+ {{> versionInfo}}
208
+
237
209
  <script>
238
210
  function toggleDetailsDropdown(element) {
239
211
  const errorDetailsWrapper = element.querySelector('.error-details-wrapper');
@@ -0,0 +1,6 @@
1
+ <i class="fas fa-robot ai-element"></i>
2
+ <i class="fas fa-microchip ai-element"></i>
3
+ <i class="fas fa-user-astronaut ai-element"></i>
4
+ <i class="fas fa-satellite-dish ai-element"></i>
5
+ <i class="fas fa-cloud ai-element"></i>
6
+ <i class="fas fa-shield-alt ai-element"></i>
@@ -0,0 +1,14 @@
1
+ <head>
2
+ <meta charset="UTF-8">
3
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
4
+ <meta name="description" content="{{#if description}}{{description}}{{else}}Log in to portal.mbktech.org to access your resources and manage projects securely.{{/if}}">
5
+ <meta name="keywords" content="MBK Tech, Web-Portal, Web, Portal, Admin-Panel, Admin, login">
6
+ <meta property="og:title" content="{{#if pageTitle}}{{pageTitle}}{{else}}{{#if pageCode}}{{pageCode}}{{/if}}{{#if pageError}} - {{pageError}}{{/if}}{{/if}} | MBK Tech" />
7
+ <meta property="og:image" content="https://mbktech.org/Assets/Images/Icon/logo.png" />
8
+ <meta property="og:url" content="{{ogUrl}}">
9
+ <title>{{#if pageTitle}}{{pageTitle}}{{else}}{{#if pageCode}}{{pageCode}}{{/if}}{{#if pageError}} - {{pageError}}{{/if}}{{/if}} | MBK Tech Portal</title>
10
+ <link rel="icon" type="image/x-icon" href="/favicon.ico">
11
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
12
+ {{> sharedStyles}}
13
+ {{#if extraStyles}}{{{extraStyles}}}{{/if}}
14
+ </head>
@@ -0,0 +1,15 @@
1
+ <header>
2
+ <div class="header-container">
3
+ <a class="logo">
4
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90 90" class="iconAboveSlogan">
5
+ <g id="8db2a7f9-6efc-4f7e-ae5b-8ba33875da43" transform="matrix(2.8125,0,0,2.8125,0,0)" stroke="none"
6
+ fill="#00b894">
7
+ <path
8
+ d="M0 32h32V0H0v32zm19.377-19.492l6.936-6.936v20.855h-6.936V12.508zM5.688 5.572l6.936 6.936v13.919H5.688V5.572z">
9
+ </path>
10
+ </g>
11
+ </svg>
12
+ <span class="logo-text">{{#if appName}}{{appName}}{{else}}{{#if app}}{{app}}{{else}}mbkauthe{{/if}}{{/if}} <span class="logo-comp">mbktech</span></span>
13
+ </a>
14
+ </div>
15
+ </header>
@@ -1,14 +1,7 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="en">
3
3
 
4
- <head>
5
- <meta charset="UTF-8">
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>Version and Configuration Information</title>
8
- <link rel="icon" type="image/x-icon" href="https://mbktech.org/Assets/Images/Icon/dgicon.svg">
9
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
10
- {{> sharedStyles}}
11
- <style>
4
+ {{> head pageTitle="Version and Configuration Information" ogUrl="/mbkauthe/info" extraStyles="<style>
12
5
  .info-box {
13
6
  background: rgba(10, 20, 20, 0.95);
14
7
  backdrop-filter: blur(10px);
@@ -169,25 +162,10 @@
169
162
  padding: 1rem;
170
163
  }
171
164
  }
172
- </style>
173
- </head>
165
+ </style>"}}
174
166
 
175
167
  <body>
176
- <header>
177
- <div class="header-container">
178
- <a class="logo">
179
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90 90" class="iconAboveSlogan">
180
- <g id="8db2a7f9-6efc-4f7e-ae5b-8ba33875da43" transform="matrix(2.8125,0,0,2.8125,0,0)" stroke="none"
181
- fill="#00b894">
182
- <path
183
- d="M0 32h32V0H0v32zm19.377-19.492l6.936-6.936v20.855h-6.936V12.508zM5.688 5.572l6.936 6.936v13.919H5.688V5.572z">
184
- </path>
185
- </g>
186
- </svg>
187
- <span class="logo-text">{{mbkautheVar.APP_NAME}} <span class="logo-comp">mbktech</span></span>
188
- </a>
189
- </div>
190
- </header>
168
+ {{> header appName=mbkautheVar.APP_NAME}}
191
169
 
192
170
  <section class="login-container">
193
171
  <i class="fas fa-info-circle ai-element"></i>
@@ -227,6 +205,10 @@
227
205
  <div class="info-label">Domain:</div>
228
206
  <div class="info-value">{{mbkautheVar.DOMAIN}}</div>
229
207
  </div>
208
+ <div class="info-row">
209
+ <div class="info-label">IS_DEPLOYED:</div>
210
+ <div class="info-value">{{mbkautheVar.IS_DEPLOYED}}</div>
211
+ </div>
230
212
  <div class="info-row">
231
213
  <div class="info-label">Login Redirect URL:</div>
232
214
  <div class="info-value">{{mbkautheVar.loginRedirectURL}}</div>
@@ -250,10 +232,7 @@
250
232
  </div>
251
233
  </section>
252
234
 
253
- <!-- Version Info -->
254
- <div class="version-info">
255
- v{{version}}
256
- </div>
235
+ {{> versionInfo}}
257
236
  </body>
258
237
 
259
238
  </html>
@@ -1,48 +1,16 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="en">
3
3
 
4
- <head>
5
- <meta charset="UTF-8">
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <meta name="description"
8
- content="Log in to portal.mbktech.org to access your resources and manage projects securely.">
9
- <meta name="keywords" content="MBK Tech Studio, Web-Portal, Web, Portal, Admin-Panel, Admin, login">
10
- <meta property="og:title" content="Login | MBK Tech Studio" />
11
- <meta property="og:image" content="https://mbktech.org/Assets/Images/Icon/logo.png" />
12
- <meta property="og:url" content="/mbkauthe/login">
13
- <title>Login | MBK Tech Studio Portal</title>
14
- <link rel="icon" type="image/x-icon" href="https://mbktech.org/Assets/Images/Icon/dgicon.svg">
15
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
16
- {{> sharedStyles}}
17
- </head>
4
+ {{> head pageTitle="Login" ogUrl="/mbkauthe/login"}}
18
5
 
19
6
  <body>
20
- <header>
21
- <div class="header-container">
22
- <a class="logo">
23
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90 90" class="iconAboveSlogan">
24
- <g id="8db2a7f9-6efc-4f7e-ae5b-8ba33875da43" transform="matrix(2.8125,0,0,2.8125,0,0)" stroke="none"
25
- fill="#00b894">
26
- <path
27
- d="M0 32h32V0H0v32zm19.377-19.492l6.936-6.936v20.855h-6.936V12.508zM5.688 5.572l6.936 6.936v13.919H5.688V5.572z">
28
- </path>
29
- </g>
30
- </svg>
31
- <span class="logo-text">{{appName}} <span class="logo-comp">mbktech</span></span>
32
- </a>
33
- </div>
34
- </header>
7
+ {{> header}}
35
8
 
36
9
  {{> showmessage}}
37
10
 
38
11
  <section class="login-container">
39
12
 
40
- <i class="fas fa-robot ai-element"></i>
41
- <i class="fas fa-microchip ai-element"></i>
42
- <i class="fas fa-user-astronaut ai-element"></i>
43
- <i class="fas fa-satellite-dish ai-element"></i>
44
- <i class="fas fa-cloud ai-element"></i>
45
- <i class="fas fa-shield-alt ai-element"></i>
13
+ {{> backgroundElements}}
46
14
 
47
15
  <div class="login-box">
48
16
  <h1 class="login-title">Login</h1>
@@ -110,10 +78,7 @@
110
78
  </div>
111
79
  </section>
112
80
 
113
- <!-- Version Info -->
114
- <div class="version-info">
115
- v{{version}}
116
- </div>
81
+ {{> versionInfo}}
117
82
 
118
83
  <script>
119
84
  // Toggle password visibility
@@ -133,7 +98,7 @@
133
98
 
134
99
  // Info dialogs
135
100
  function usernameinfo() {
136
- showMessage(`Your username is the part of your MBK Tech Studio email before the @ (e.g., abc.xyz@mbktech.org
101
+ showMessage(`Your username is the part of your MBKTech.org email before the @ (e.g., abc.xyz@mbktech.org
137
102
  → 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
103
  }
139
104