mbkauthe 1.1.13 → 1.1.15
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/.env.example +1 -4
- package/README.md +2 -7
- package/env.md +162 -39
- package/index.js +2 -11
- package/lib/main.js +2 -33
- package/lib/pool.js +1 -11
- package/lib/validateSessionAndRole.js +42 -19
- package/package.json +1 -1
- package/views/Error/error.handlebars +723 -0
- package/views/loginmbkauthe.handlebars +7 -21
package/.env.example
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
mbkautheVar='{
|
|
2
2
|
"APP_NAME": "MBKAUTH",
|
|
3
|
-
"RECAPTCHA_SECRET_KEY": "your-recaptcha-secret-key",
|
|
4
|
-
"RECAPTCHA_Enabled": "f",
|
|
5
|
-
"BypassUsers": ["demo","user1"],
|
|
6
3
|
"SESSION_SECRET_KEY": "your-session-secret-key",
|
|
7
4
|
"IS_DEPLOYED": "true",
|
|
8
5
|
"LOGIN_DB": "postgres://username:password@host:port/database",
|
|
9
6
|
"MBKAUTH_TWO_FA_ENABLE": "false",
|
|
10
7
|
"COOKIE_EXPIRE_TIME": 2,
|
|
11
8
|
"DOMAIN": "yourdomain.com",
|
|
12
|
-
"
|
|
9
|
+
"loginRedirectURL": "/admin"
|
|
13
10
|
}'
|
|
14
11
|
|
|
15
12
|
# See env.md for more details
|
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
- [License](#license)
|
|
29
29
|
- [Contact \& Support](#contact--support)
|
|
30
30
|
|
|
31
|
-
`mbkAuthe` is a reusable authentication system for Node.js applications, designed to simplify session management, user authentication, and role-based access control. It integrates seamlessly with PostgreSQL and supports features like Two-Factor Authentication (2FA)
|
|
31
|
+
`mbkAuthe` is a reusable authentication system for Node.js applications, designed to simplify session management, user authentication, and role-based access control. It integrates seamlessly with PostgreSQL and supports features like Two-Factor Authentication (2FA) and session restoration.
|
|
32
32
|
|
|
33
33
|
## Features
|
|
34
34
|
|
|
@@ -36,7 +36,6 @@
|
|
|
36
36
|
- **User Authentication:** Provides robust authentication, including support for username/password and Two-Factor Authentication (2FA).
|
|
37
37
|
- **Role-Based Access Control (RBAC):** Enables fine-grained access control by validating user roles and permissions.
|
|
38
38
|
- **Integration with PostgreSQL:** Seamlessly integrates with PostgreSQL for user and session data storage.
|
|
39
|
-
- **reCAPTCHA Verification:** Adds an extra layer of security with reCAPTCHA support to prevent automated attacks.
|
|
40
39
|
- **Middleware Functions:** Includes reusable middleware for session validation, role checking, and user authentication.
|
|
41
40
|
- **API Endpoints:** Offers a set of RESTful APIs for login, logout, session termination, and package information retrieval.
|
|
42
41
|
- **Environment Configuration:** Supports flexible configuration through .env files for deployment-specific settings.
|
|
@@ -86,16 +85,13 @@ Example `.env` file:
|
|
|
86
85
|
```code
|
|
87
86
|
mbkautheVar='{
|
|
88
87
|
"APP_NAME": "MBKAUTH",
|
|
89
|
-
"RECAPTCHA_SECRET_KEY": "your-recaptcha-secret-key",
|
|
90
|
-
"RECAPTCHA_Enabled": "false",
|
|
91
|
-
"BypassUsers": ["user1","user2"],
|
|
92
88
|
"SESSION_SECRET_KEY": "your-session-secret-key",
|
|
93
89
|
"IS_DEPLOYED": "true",
|
|
94
90
|
"LOGIN_DB": "postgres://username:password@host:port/database",
|
|
95
91
|
"MBKAUTH_TWO_FA_ENABLE": "false",
|
|
96
92
|
"COOKIE_EXPIRE_TIME": 2,
|
|
97
93
|
"DOMAIN": "yourdomain.com",
|
|
98
|
-
"
|
|
94
|
+
"loginRedirectURL": "/admin"
|
|
99
95
|
}'
|
|
100
96
|
```
|
|
101
97
|
|
|
@@ -198,7 +194,6 @@ router.post(["/terminateAllSessions"], authenticate(mbkautheVar.Password), (req,
|
|
|
198
194
|
- `username`: User's username.
|
|
199
195
|
- `password`: User's password.
|
|
200
196
|
- `token`: (Optional) 2FA token.
|
|
201
|
-
- `recaptcha`: reCAPTCHA response.
|
|
202
197
|
|
|
203
198
|
- Response:
|
|
204
199
|
- `200`: Login successful.
|
package/env.md
CHANGED
|
@@ -1,75 +1,198 @@
|
|
|
1
|
-
# Configuration Guide
|
|
1
|
+
# Environment Configuration Guide
|
|
2
2
|
|
|
3
|
-
[
|
|
3
|
+
[← Back to README](README.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This guide explains how to configure your MBKAuth application using environment variables. Create a `.env` file in your project root and set the following variables according to your deployment needs.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 📱 Application Settings
|
|
10
|
+
|
|
11
|
+
### App Name Configuration
|
|
12
|
+
```env
|
|
8
13
|
APP_NAME=mbkauthe
|
|
9
14
|
```
|
|
10
15
|
|
|
11
|
-
|
|
16
|
+
**Description:** Defines the application identifier used for user access control.
|
|
17
|
+
|
|
18
|
+
- **Purpose:** Distinguishes this application from others in your ecosystem
|
|
19
|
+
- **Security:** Users are restricted to apps they're authorized for via the `AllowedApp` column in the Users table
|
|
20
|
+
- **Required:** Yes
|
|
12
21
|
|
|
13
|
-
|
|
22
|
+
---
|
|
14
23
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
24
|
+
## 🔐 Session Management
|
|
25
|
+
|
|
26
|
+
### Session Configuration
|
|
27
|
+
```env
|
|
28
|
+
SESSION_SECRET_KEY=your-secure-random-key-here
|
|
29
|
+
IS_DEPLOYED=false
|
|
30
|
+
DOMAIN=localhost
|
|
19
31
|
```
|
|
20
32
|
|
|
21
|
-
|
|
33
|
+
#### SESSION_SECRET_KEY
|
|
34
|
+
**Description:** Cryptographic key for session security.
|
|
35
|
+
|
|
36
|
+
- **Security:** Use a strong, randomly generated key (minimum 32 characters)
|
|
37
|
+
- **Generation:** Generate securely at [Generate Secret](https://generate-secret.vercel.app/32)
|
|
38
|
+
- **Example:** `SESSION_SECRET_KEY=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6`
|
|
39
|
+
- **Required:** Yes
|
|
22
40
|
|
|
23
|
-
|
|
41
|
+
#### IS_DEPLOYED
|
|
42
|
+
**Description:** Deployment environment flag that affects session behavior.
|
|
24
43
|
|
|
25
|
-
|
|
44
|
+
**Values:**
|
|
45
|
+
- `true` - Production/deployed environment
|
|
46
|
+
- Sessions work across all subdomains of your specified domain
|
|
47
|
+
- **Important:** Login will NOT work on `localhost` when set to `true`
|
|
48
|
+
- `false` - Local development environment
|
|
49
|
+
- Sessions work on localhost for development
|
|
26
50
|
|
|
27
|
-
|
|
51
|
+
**Default:** `false`
|
|
28
52
|
|
|
53
|
+
#### DOMAIN
|
|
54
|
+
**Description:** Your application's domain name.
|
|
29
55
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
56
|
+
**Configuration:**
|
|
57
|
+
- **Production:** Set to your actual domain (e.g., `mbktechstudio.com`)
|
|
58
|
+
- **Development:** Use `localhost` or set `IS_DEPLOYED=false`
|
|
59
|
+
- **Subdomains:** When `IS_DEPLOYED=true`, sessions are shared across all subdomains
|
|
60
|
+
|
|
61
|
+
**Examples:**
|
|
62
|
+
```env
|
|
63
|
+
# Production
|
|
64
|
+
DOMAIN=yourdomain.com
|
|
33
65
|
IS_DEPLOYED=true
|
|
34
|
-
|
|
66
|
+
|
|
67
|
+
# Development
|
|
68
|
+
DOMAIN=localhost
|
|
69
|
+
IS_DEPLOYED=false
|
|
35
70
|
```
|
|
36
|
-
> **SESSION_SECRET_KEY**: Generate a secure key using [Generate Secret](https://generate-secret.vercel.app/32).
|
|
37
71
|
|
|
38
|
-
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 🗄️ Database Configuration
|
|
75
|
+
|
|
76
|
+
### PostgreSQL Connection
|
|
77
|
+
```env
|
|
78
|
+
LOGIN_DB=postgresql://username:password@host:port/database_name
|
|
79
|
+
```
|
|
39
80
|
|
|
40
|
-
|
|
81
|
+
**Description:** PostgreSQL database connection string for user authentication.
|
|
41
82
|
|
|
42
|
-
|
|
83
|
+
**Format:** `postgresql://[username]:[password]@[host]:[port]/[database]`
|
|
43
84
|
|
|
44
|
-
|
|
85
|
+
**Examples:**
|
|
86
|
+
```env
|
|
87
|
+
# Local database
|
|
88
|
+
LOGIN_DB=postgresql://admin:password123@localhost:5432/mbkauth_db
|
|
45
89
|
|
|
46
|
-
|
|
90
|
+
# Remote database
|
|
91
|
+
LOGIN_DB=postgresql://user:pass@db.example.com:5432/production_db
|
|
47
92
|
|
|
48
|
-
|
|
93
|
+
# With SSL (recommended for production)
|
|
94
|
+
LOGIN_DB=postgresql://user:pass@host:5432/db?sslmode=require
|
|
95
|
+
```
|
|
49
96
|
|
|
50
|
-
|
|
97
|
+
**Required:** Yes
|
|
51
98
|
|
|
99
|
+
---
|
|
52
100
|
|
|
53
|
-
##
|
|
101
|
+
## 🔒 Two-Factor Authentication (2FA)
|
|
54
102
|
|
|
55
|
-
|
|
56
|
-
|
|
103
|
+
### 2FA Configuration
|
|
104
|
+
```env
|
|
105
|
+
MBKAUTH_TWO_FA_ENABLE=false
|
|
57
106
|
```
|
|
58
|
-
> Replace the placeholder with your PostgreSQL connection string.
|
|
59
107
|
|
|
108
|
+
**Description:** Enables or disables Two-Factor Authentication for enhanced security.
|
|
60
109
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
110
|
+
**Values:**
|
|
111
|
+
- `true` - Enable 2FA (recommended for production)
|
|
112
|
+
- `false` - Disable 2FA (default)
|
|
113
|
+
|
|
114
|
+
**Note:** When enabled, users will need to configure an authenticator app (Google Authenticator, Authy, etc.) for login.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 🍪 Cookie Settings
|
|
119
|
+
|
|
120
|
+
### Cookie Expiration
|
|
121
|
+
```env
|
|
122
|
+
COOKIE_EXPIRE_TIME=2
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**Description:** Sets how long authentication cookies remain valid.
|
|
126
|
+
|
|
127
|
+
- **Unit:** Days
|
|
128
|
+
- **Default:** `2` days
|
|
129
|
+
- **Range:** 1-30 days (recommended)
|
|
130
|
+
- **Security:** Shorter periods are more secure but require more frequent logins
|
|
131
|
+
|
|
132
|
+
**Examples:**
|
|
133
|
+
```env
|
|
134
|
+
COOKIE_EXPIRE_TIME=1 # 1 day (high security)
|
|
135
|
+
COOKIE_EXPIRE_TIME=7 # 1 week (balanced)
|
|
136
|
+
COOKIE_EXPIRE_TIME=30 # 1 month (convenience)
|
|
64
137
|
```
|
|
65
|
-
> MBKAUTH_TWO_FA_ENABLE: Set to `true` to enable Two-Factor Authentication.
|
|
66
138
|
|
|
139
|
+
---
|
|
67
140
|
|
|
68
|
-
##
|
|
141
|
+
## 🚀 Quick Setup Examples
|
|
69
142
|
|
|
70
|
-
|
|
71
|
-
|
|
143
|
+
### Development Environment
|
|
144
|
+
```env
|
|
145
|
+
# .env file for local development
|
|
146
|
+
APP_NAME=mbkauthe
|
|
147
|
+
SESSION_SECRET_KEY=dev-secret-key-change-in-production
|
|
148
|
+
IS_DEPLOYED=false
|
|
149
|
+
DOMAIN=localhost
|
|
150
|
+
LOGIN_DB=postgresql://admin:password@localhost:5432/mbkauth_dev
|
|
151
|
+
MBKAUTH_TWO_FA_ENABLE=false
|
|
152
|
+
COOKIE_EXPIRE_TIME=7
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Production Environment
|
|
156
|
+
```env
|
|
157
|
+
# .env file for production deployment
|
|
158
|
+
APP_NAME=mbkauthe
|
|
159
|
+
SESSION_SECRET_KEY=your-super-secure-production-key-here
|
|
160
|
+
IS_DEPLOYED=true
|
|
161
|
+
DOMAIN=yourdomain.com
|
|
162
|
+
LOGIN_DB=postgresql://dbuser:securepass@prod-db.example.com:5432/mbkauth_prod
|
|
163
|
+
MBKAUTH_TWO_FA_ENABLE=true
|
|
164
|
+
COOKIE_EXPIRE_TIME=2
|
|
72
165
|
```
|
|
73
|
-
> Cookie expiration time in days. Default is `2 days`.
|
|
74
166
|
|
|
75
|
-
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## ⚠️ Important Security Notes
|
|
170
|
+
|
|
171
|
+
1. **Never commit your `.env` file** to version control
|
|
172
|
+
2. **Use strong, unique secrets** for production environments
|
|
173
|
+
3. **Enable HTTPS** when `IS_DEPLOYED=true`
|
|
174
|
+
4. **Regularly rotate** your `SESSION_SECRET_KEY`
|
|
175
|
+
5. **Use environment-specific databases** (separate dev/prod databases)
|
|
176
|
+
6. **Enable 2FA** for production environments
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## 🔧 Troubleshooting
|
|
181
|
+
|
|
182
|
+
### Common Issues
|
|
183
|
+
|
|
184
|
+
**Login not working on localhost:**
|
|
185
|
+
- Ensure `IS_DEPLOYED=false` for local development
|
|
186
|
+
- Check that `DOMAIN=localhost`
|
|
187
|
+
|
|
188
|
+
**Session not persisting:**
|
|
189
|
+
- Verify `SESSION_SECRET_KEY` is set and consistent
|
|
190
|
+
- Check cookie settings in your browser
|
|
191
|
+
|
|
192
|
+
**Database connection errors:**
|
|
193
|
+
- Verify database credentials and connection string format
|
|
194
|
+
- Ensure database server is running and accessible
|
|
195
|
+
|
|
196
|
+
**2FA issues:**
|
|
197
|
+
- Confirm authenticator app time is synchronized
|
|
198
|
+
- Verify `MBKAUTH_TWO_FA_ENABLE` setting matches your setup
|
package/index.js
CHANGED
|
@@ -20,28 +20,18 @@ try {
|
|
|
20
20
|
if (!mbkautheVar) {
|
|
21
21
|
throw new Error("mbkautheVar is not defined");
|
|
22
22
|
}
|
|
23
|
-
const requiredKeys = ["APP_NAME", "
|
|
23
|
+
const requiredKeys = ["APP_NAME", "SESSION_SECRET_KEY", "IS_DEPLOYED", "LOGIN_DB", "MBKAUTH_TWO_FA_ENABLE", "DOMAIN"];
|
|
24
24
|
requiredKeys.forEach(key => {
|
|
25
25
|
if (!mbkautheVar[key]) {
|
|
26
26
|
throw new Error(`mbkautheVar.${key} is required`);
|
|
27
27
|
}
|
|
28
28
|
});
|
|
29
|
-
if (mbkautheVar.RECAPTCHA_Enabled === "true") {
|
|
30
|
-
if (mbkautheVar.RECAPTCHA_SECRET_KEY === undefined) {
|
|
31
|
-
throw new Error("mbkautheVar.RECAPTCHA_SECRET_KEY is required");
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
29
|
if (mbkautheVar.COOKIE_EXPIRE_TIME !== undefined) {
|
|
35
30
|
const expireTime = parseFloat(mbkautheVar.COOKIE_EXPIRE_TIME);
|
|
36
31
|
if (isNaN(expireTime) || expireTime <= 0) {
|
|
37
32
|
throw new Error("mbkautheVar.COOKIE_EXPIRE_TIME must be a valid positive number");
|
|
38
33
|
}
|
|
39
34
|
}
|
|
40
|
-
if (mbkautheVar.BypassUsers !== undefined) {
|
|
41
|
-
if (!Array.isArray(mbkautheVar.BypassUsers)) {
|
|
42
|
-
throw new Error("mbkautheVar.BypassUsers must be a valid array");
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
35
|
|
|
46
36
|
const app = express();
|
|
47
37
|
if (process.env.test === "true") {
|
|
@@ -59,6 +49,7 @@ app.engine("handlebars", engine({
|
|
|
59
49
|
defaultLayout: false,
|
|
60
50
|
partialsDir: [
|
|
61
51
|
path.join(__dirname, "node_modules/mbkauthe/views"),
|
|
52
|
+
path.join(__dirname, "node_modules/mbkauthe/views/Error"),
|
|
62
53
|
],
|
|
63
54
|
}));
|
|
64
55
|
|
package/lib/main.js
CHANGED
|
@@ -144,36 +144,9 @@ router.post("/mbkauthe/api/terminateAllSessions", authenticate(mbkautheVar.Main_
|
|
|
144
144
|
router.post("/mbkauthe/api/login", LoginLimit, async (req, res) => {
|
|
145
145
|
console.log("Login request received");
|
|
146
146
|
|
|
147
|
-
const { username, password, token
|
|
147
|
+
const { username, password, token } = req.body;
|
|
148
148
|
console.log(`Login attempt for username: ${username}`);
|
|
149
149
|
|
|
150
|
-
const secretKey = mbkautheVar.RECAPTCHA_SECRET_KEY;
|
|
151
|
-
const verificationUrl = `https://www.google.com/recaptcha/api/siteverify?secret=${secretKey}&response=${recaptcha}`;
|
|
152
|
-
|
|
153
|
-
let BypassUsers = Array.isArray(mbkautheVar.BypassUsers) ? mbkautheVar.BypassUsers : JSON.parse(mbkautheVar.BypassUsers);
|
|
154
|
-
|
|
155
|
-
if (mbkautheVar.RECAPTCHA_Enabled === "true") {
|
|
156
|
-
if (!BypassUsers.includes(username)) {
|
|
157
|
-
if (!recaptcha) {
|
|
158
|
-
console.log("Missing reCAPTCHA token");
|
|
159
|
-
return res.status(400).json({ success: false, message: "Please complete the reCAPTCHA" });
|
|
160
|
-
}
|
|
161
|
-
try {
|
|
162
|
-
const response = await fetch(verificationUrl, { method: 'POST' });
|
|
163
|
-
const body = await response.json();
|
|
164
|
-
console.log("reCAPTCHA verification response:", body);
|
|
165
|
-
|
|
166
|
-
if (!body.success) {
|
|
167
|
-
console.log("Failed reCAPTCHA verification");
|
|
168
|
-
return res.status(400).json({ success: false, message: "Failed reCAPTCHA verification" });
|
|
169
|
-
}
|
|
170
|
-
} catch (err) {
|
|
171
|
-
console.log("Error during reCAPTCHA verification:", err);
|
|
172
|
-
return res.status(500).json({ success: false, message: "Internal Server Error" });
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
150
|
if (!username || !password) {
|
|
178
151
|
console.log("Missing username or password");
|
|
179
152
|
return res.status(400).json({
|
|
@@ -360,7 +333,7 @@ router.post("/mbkauthe/api/logout", async (req, res) => {
|
|
|
360
333
|
|
|
361
334
|
|
|
362
335
|
router.get("/mbkauthe/login", LoginLimit, (req, res) => {
|
|
363
|
-
return res.render("loginmbkauthe", {
|
|
336
|
+
return res.render("loginmbkauthe.handlebars", {
|
|
364
337
|
layout: false,
|
|
365
338
|
customURL: mbkautheVar.loginRedirectURL || '/home',
|
|
366
339
|
userLoggedIn: !!req.session?.user,
|
|
@@ -728,10 +701,6 @@ router.get(["/mbkauthe/info", "/mbkauthe/i"], LoginLimit, async (_, res) => {
|
|
|
728
701
|
<div class="info-label">APP_NAME:</div>
|
|
729
702
|
<div class="info-value">${mbkautheVar.APP_NAME}</div>
|
|
730
703
|
</div>
|
|
731
|
-
<div class="info-row">
|
|
732
|
-
<div class="info-label">RECAPTCHA_Enabled:</div>
|
|
733
|
-
<div class="info-value">${mbkautheVar.RECAPTCHA_Enabled}</div>
|
|
734
|
-
</div>
|
|
735
704
|
<div class="info-row">
|
|
736
705
|
<div class="info-label">MBKAUTH_TWO_FA_ENABLE:</div>
|
|
737
706
|
<div class="info-value">${mbkautheVar.MBKAUTH_TWO_FA_ENABLE}</div>
|
package/lib/pool.js
CHANGED
|
@@ -13,28 +13,18 @@ try {
|
|
|
13
13
|
if (!mbkautheVar) {
|
|
14
14
|
throw new Error("mbkautheVar is not defined");
|
|
15
15
|
}
|
|
16
|
-
const requiredKeys = ["APP_NAME", "
|
|
16
|
+
const requiredKeys = ["APP_NAME", "SESSION_SECRET_KEY", "IS_DEPLOYED", "LOGIN_DB", "MBKAUTH_TWO_FA_ENABLE", "DOMAIN"];
|
|
17
17
|
requiredKeys.forEach(key => {
|
|
18
18
|
if (!mbkautheVar[key]) {
|
|
19
19
|
throw new Error(`mbkautheVar.${key} is required`);
|
|
20
20
|
}
|
|
21
21
|
});
|
|
22
|
-
if (mbkautheVar.RECAPTCHA_Enabled === "true") {
|
|
23
|
-
if (mbkautheVar.RECAPTCHA_SECRET_KEY === undefined) {
|
|
24
|
-
throw new Error("mbkautheVar.RECAPTCHA_SECRET_KEY is required");
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
22
|
if (mbkautheVar.COOKIE_EXPIRE_TIME !== undefined) {
|
|
28
23
|
const expireTime = parseFloat(mbkautheVar.COOKIE_EXPIRE_TIME);
|
|
29
24
|
if (isNaN(expireTime) || expireTime <= 0) {
|
|
30
25
|
throw new Error("mbkautheVar.COOKIE_EXPIRE_TIME must be a valid positive number");
|
|
31
26
|
}
|
|
32
27
|
}
|
|
33
|
-
if (mbkautheVar.BypassUsers !== undefined) {
|
|
34
|
-
if (!Array.isArray(mbkautheVar.BypassUsers)) {
|
|
35
|
-
throw new Error("mbkautheVar.BypassUsers must be a valid array");
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
28
|
|
|
39
29
|
const poolConfig = {
|
|
40
30
|
connectionString: mbkautheVar.LOGIN_DB,
|
|
@@ -36,9 +36,13 @@ async function validateSession(req, res, next) {
|
|
|
36
36
|
if (!req.session.user) {
|
|
37
37
|
console.log("User not authenticated");
|
|
38
38
|
console.log(req.session.user);
|
|
39
|
-
return res.render("
|
|
40
|
-
layout:
|
|
41
|
-
|
|
39
|
+
return res.render("error.handlebars", {
|
|
40
|
+
layout: false,
|
|
41
|
+
code: 401,
|
|
42
|
+
error: "Not Logged In",
|
|
43
|
+
message: "You Are Not Logged In. Please Log In To Continue.",
|
|
44
|
+
pagename: "Login",
|
|
45
|
+
page: `/mbkauthe/login?redirect=${encodeURIComponent(req.originalUrl)}`,
|
|
42
46
|
});
|
|
43
47
|
}
|
|
44
48
|
|
|
@@ -55,9 +59,13 @@ async function validateSession(req, res, next) {
|
|
|
55
59
|
res.clearCookie("mbkauthe.sid", cookieOptions);
|
|
56
60
|
res.clearCookie("sessionId", cookieOptions);
|
|
57
61
|
res.clearCookie("username", cookieOptions);
|
|
58
|
-
return res.render("
|
|
59
|
-
layout:
|
|
60
|
-
|
|
62
|
+
return res.render("error.handlebars", {
|
|
63
|
+
layout: false,
|
|
64
|
+
code: 401,
|
|
65
|
+
error: "Session Expired",
|
|
66
|
+
message: "Your Session Has Expired. Please Log In Again.",
|
|
67
|
+
pagename: "Login",
|
|
68
|
+
page: `/mbkauthe/login?redirect=${encodeURIComponent(req.originalUrl)}`,
|
|
61
69
|
});
|
|
62
70
|
}
|
|
63
71
|
|
|
@@ -68,9 +76,13 @@ async function validateSession(req, res, next) {
|
|
|
68
76
|
res.clearCookie("mbkauthe.sid", cookieOptions);
|
|
69
77
|
res.clearCookie("sessionId", cookieOptions);
|
|
70
78
|
res.clearCookie("username", cookieOptions);
|
|
71
|
-
return res.render("
|
|
72
|
-
layout:
|
|
73
|
-
|
|
79
|
+
return res.render("error.handlebars", {
|
|
80
|
+
layout: false,
|
|
81
|
+
code: 401,
|
|
82
|
+
error: "Account Inactive",
|
|
83
|
+
message: "Your Account Is Inactive. Please Contact Support.",
|
|
84
|
+
pagename: "Support",
|
|
85
|
+
page: "https://mbktechstudio.com/Support",
|
|
74
86
|
});
|
|
75
87
|
}
|
|
76
88
|
|
|
@@ -83,9 +95,13 @@ async function validateSession(req, res, next) {
|
|
|
83
95
|
res.clearCookie("mbkauthe.sid", cookieOptions);
|
|
84
96
|
res.clearCookie("sessionId", cookieOptions);
|
|
85
97
|
res.clearCookie("username", cookieOptions);
|
|
86
|
-
return res.render("
|
|
87
|
-
layout:
|
|
88
|
-
|
|
98
|
+
return res.render("error.handlebars", {
|
|
99
|
+
layout: false,
|
|
100
|
+
code: 401,
|
|
101
|
+
error: "Unauthorized",
|
|
102
|
+
message: `You Are Not Authorized To Use The Application \"${mbkautheVar.APP_NAME}\"`,
|
|
103
|
+
pagename: "Home",
|
|
104
|
+
page: `/${mbkautheVar.loginRedirectURL}`
|
|
89
105
|
});
|
|
90
106
|
}
|
|
91
107
|
}
|
|
@@ -103,9 +119,13 @@ const checkRolePermission = (requiredRole) => {
|
|
|
103
119
|
if (!req.session || !req.session.user || !req.session.user.id) {
|
|
104
120
|
console.log("User not authenticated");
|
|
105
121
|
console.log(req.session);
|
|
106
|
-
return res.render("
|
|
107
|
-
layout:
|
|
108
|
-
|
|
122
|
+
return res.render("error.handlebars", {
|
|
123
|
+
layout: false,
|
|
124
|
+
code: 401,
|
|
125
|
+
error: "Not Logged In",
|
|
126
|
+
message: "You Are Not Logged In. Please Log In To Continue.",
|
|
127
|
+
pagename: "Login",
|
|
128
|
+
page: `/mbkauthe/login?redirect=${encodeURIComponent(req.originalUrl)}`,
|
|
109
129
|
});
|
|
110
130
|
}
|
|
111
131
|
|
|
@@ -124,10 +144,13 @@ const checkRolePermission = (requiredRole) => {
|
|
|
124
144
|
|
|
125
145
|
const userRole = result.rows[0].Role;
|
|
126
146
|
if (userRole !== requiredRole) {
|
|
127
|
-
return res.render("
|
|
128
|
-
layout:
|
|
129
|
-
|
|
130
|
-
|
|
147
|
+
return res.render("error.handlebars", {
|
|
148
|
+
layout: false,
|
|
149
|
+
code: 403,
|
|
150
|
+
error: "Access Denied",
|
|
151
|
+
message: `You do not have permission to access this resource. Required role: ${requiredRole}`,
|
|
152
|
+
pagename: "Home",
|
|
153
|
+
page: `/${mbkautheVar.loginRedirectURL}`
|
|
131
154
|
});
|
|
132
155
|
}
|
|
133
156
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,723 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
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://mbktechstudio.com/Assets/Images/Icon/dgicon.svg">
|
|
9
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
|
10
|
+
<style>
|
|
11
|
+
:root {
|
|
12
|
+
--primary: #4361ee;
|
|
13
|
+
--primary-dark: #3a0ca3;
|
|
14
|
+
--primary-light: rgba(67, 97, 238, 0.1);
|
|
15
|
+
--secondary: #f72585;
|
|
16
|
+
--secondary-light: rgba(247, 37, 133, 0.1);
|
|
17
|
+
--dark: #121212;
|
|
18
|
+
--dark-light: #1e1e1e;
|
|
19
|
+
--darker: #0a0a0a;
|
|
20
|
+
--light: #f8f9fa;
|
|
21
|
+
--lighter: #ffffff;
|
|
22
|
+
--gray: #cccccc;
|
|
23
|
+
--gray-dark: #888888;
|
|
24
|
+
--success: #4cc9f0;
|
|
25
|
+
--warning: #f8961e;
|
|
26
|
+
--danger: #ef233c;
|
|
27
|
+
--gradient: linear-gradient(135deg, var(--primary), var(--secondary));
|
|
28
|
+
--glass: rgba(30, 30, 30, 0.5);
|
|
29
|
+
--glass-border: rgba(255, 255, 255, 0.1);
|
|
30
|
+
--transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.1);
|
|
31
|
+
--shadow-sm: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
32
|
+
--shadow-md: 0 8px 30px rgba(0, 0, 0, 0.2);
|
|
33
|
+
--shadow-lg: 0 15px 40px rgba(0, 0, 0, 0.3);
|
|
34
|
+
--radius-sm: 8px;
|
|
35
|
+
--radius-md: 12px;
|
|
36
|
+
--radius-lg: 16px;
|
|
37
|
+
--radius-xl: 24px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
* {
|
|
41
|
+
margin: 0;
|
|
42
|
+
padding: 0;
|
|
43
|
+
box-sizing: border-box;
|
|
44
|
+
font-family: 'Poppins', sans-serif;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
body {
|
|
48
|
+
background: var(--dark);
|
|
49
|
+
color: var(--light);
|
|
50
|
+
line-height: 1.6;
|
|
51
|
+
overflow-x: hidden;
|
|
52
|
+
min-height: 100vh;
|
|
53
|
+
display: flex;
|
|
54
|
+
flex-direction: column;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
header {
|
|
58
|
+
position: fixed;
|
|
59
|
+
top: 0;
|
|
60
|
+
left: 0;
|
|
61
|
+
width: 100%;
|
|
62
|
+
z-index: 1000;
|
|
63
|
+
transition: var(--transition);
|
|
64
|
+
background: linear-gradient(to bottom, rgba(10, 10, 10, 0.9), rgba(10, 10, 10, 0.7));
|
|
65
|
+
backdrop-filter: blur(10px);
|
|
66
|
+
border-bottom: 1px solid var(--glass-border);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
nav {
|
|
70
|
+
padding: 10px 5%;
|
|
71
|
+
max-width: 1400px;
|
|
72
|
+
margin: 0 auto;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.navbar {
|
|
76
|
+
display: flex;
|
|
77
|
+
justify-content: space-between;
|
|
78
|
+
align-items: center;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.logo {
|
|
82
|
+
display: flex;
|
|
83
|
+
align-items: center;
|
|
84
|
+
gap: 10px;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.logo img {
|
|
88
|
+
height: 30px;
|
|
89
|
+
width: auto;
|
|
90
|
+
transition: var(--transition);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.logo:hover img {
|
|
94
|
+
transform: rotate(15deg);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.logo-text {
|
|
98
|
+
font-size: 1.8rem;
|
|
99
|
+
font-weight: 700;
|
|
100
|
+
background: var(--gradient);
|
|
101
|
+
-webkit-background-clip: text;
|
|
102
|
+
background-clip: text;
|
|
103
|
+
color: transparent;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.login-container {
|
|
107
|
+
flex: 1;
|
|
108
|
+
display: flex;
|
|
109
|
+
align-items: center;
|
|
110
|
+
justify-content: center;
|
|
111
|
+
padding: 120px 5% 80px;
|
|
112
|
+
position: relative;
|
|
113
|
+
overflow: hidden;
|
|
114
|
+
background: radial-gradient(circle at 70% 20%, rgba(67, 97, 238, 0.15), transparent 60%);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.login-box {
|
|
118
|
+
background: var(--dark-light);
|
|
119
|
+
border-radius: var(--radius-xl);
|
|
120
|
+
padding: 2rem;
|
|
121
|
+
width: 100%;
|
|
122
|
+
max-width: 500px;
|
|
123
|
+
box-shadow: var(--shadow-lg);
|
|
124
|
+
border: .125rem solid var(--glass-border);
|
|
125
|
+
position: relative;
|
|
126
|
+
z-index: 2;
|
|
127
|
+
transition: var(--transition);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.login-box:hover {
|
|
131
|
+
box-shadow: var(--shadow-lg);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.login-title {
|
|
135
|
+
text-align: center;
|
|
136
|
+
margin-bottom: 2rem;
|
|
137
|
+
font-size: 2rem;
|
|
138
|
+
position: relative;
|
|
139
|
+
color: var(--lighter);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.login-title::after {
|
|
143
|
+
content: '';
|
|
144
|
+
position: absolute;
|
|
145
|
+
bottom: -10px;
|
|
146
|
+
left: 50%;
|
|
147
|
+
transform: translateX(-50%);
|
|
148
|
+
width: 80px;
|
|
149
|
+
height: 4px;
|
|
150
|
+
background: var(--gradient);
|
|
151
|
+
border-radius: 2px;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.form-group {
|
|
155
|
+
position: relative;
|
|
156
|
+
margin-bottom: 1rem;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.form-input {
|
|
160
|
+
width: 100%;
|
|
161
|
+
padding: 12px 20px;
|
|
162
|
+
background: var(--darker);
|
|
163
|
+
border: 2px solid var(--glass-border);
|
|
164
|
+
border-radius: var(--radius-sm);
|
|
165
|
+
color: var(--light);
|
|
166
|
+
font-size: 1rem;
|
|
167
|
+
transition: var(--transition);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.form-input:focus {
|
|
171
|
+
outline: none;
|
|
172
|
+
border-color: var(--primary);
|
|
173
|
+
box-shadow: 0 0 0 3px var(--primary-light);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.form-label {
|
|
177
|
+
position: absolute;
|
|
178
|
+
top: 15px;
|
|
179
|
+
left: 20px;
|
|
180
|
+
color: var(--gray);
|
|
181
|
+
transition: var(--transition);
|
|
182
|
+
pointer-events: none;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.form-input:focus+.form-label,
|
|
186
|
+
.form-input:not(:placeholder-shown)+.form-label {
|
|
187
|
+
top: -10px;
|
|
188
|
+
left: 15px;
|
|
189
|
+
font-size: 0.8rem;
|
|
190
|
+
background: var(--dark-light);
|
|
191
|
+
padding: 0 5px;
|
|
192
|
+
color: var(--primary);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.input-icon {
|
|
196
|
+
position: absolute;
|
|
197
|
+
right: 20px;
|
|
198
|
+
top: 50%;
|
|
199
|
+
transform: translateY(-50%);
|
|
200
|
+
color: var(--gray);
|
|
201
|
+
cursor: pointer;
|
|
202
|
+
transition: var(--transition);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.input-icon:hover {
|
|
206
|
+
color: var(--primary);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.btn-login {
|
|
210
|
+
width: 100%;
|
|
211
|
+
padding: 10px;
|
|
212
|
+
border-radius: var(--radius-sm);
|
|
213
|
+
background: var(--primary);
|
|
214
|
+
color: white;
|
|
215
|
+
font-weight: 600;
|
|
216
|
+
font-size: 1rem;
|
|
217
|
+
border: none;
|
|
218
|
+
cursor: pointer;
|
|
219
|
+
transition: var(--transition);
|
|
220
|
+
box-shadow: var(--shadow-sm);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.btn-login:hover {
|
|
224
|
+
background: var(--primary-dark);
|
|
225
|
+
box-shadow: var(--shadow-md);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.btn-login:disabled {
|
|
229
|
+
background: var(--gray-dark);
|
|
230
|
+
cursor: not-allowed;
|
|
231
|
+
transform: none;
|
|
232
|
+
box-shadow: none;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.login-links {
|
|
236
|
+
display: flex;
|
|
237
|
+
justify-content: space-between;
|
|
238
|
+
margin-top: 1.5rem;
|
|
239
|
+
font-size: 0.9rem;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.login-link {
|
|
243
|
+
color: var(--gray);
|
|
244
|
+
transition: var(--transition);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.login-link:hover {
|
|
248
|
+
color: var(--primary);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.terms-info {
|
|
252
|
+
margin-top: 2rem;
|
|
253
|
+
font-size: 0.8rem;
|
|
254
|
+
color: var(--gray);
|
|
255
|
+
text-align: center;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.terms-link {
|
|
259
|
+
color: var(--primary);
|
|
260
|
+
font-weight: 500;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.token-container {
|
|
264
|
+
animation: fadeInUp 0.4s ease-out;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.token-container.disable {
|
|
268
|
+
display: none;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.token-container.enable {
|
|
272
|
+
display: block;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.ai-element {
|
|
276
|
+
position: absolute;
|
|
277
|
+
opacity: 0.1;
|
|
278
|
+
z-index: 1;
|
|
279
|
+
animation: float 6s ease-in-out infinite;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.ai-element:nth-child(1) {
|
|
283
|
+
top: 20%;
|
|
284
|
+
left: 10%;
|
|
285
|
+
font-size: 5rem;
|
|
286
|
+
animation-delay: 0s;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.ai-element:nth-child(2) {
|
|
290
|
+
top: 60%;
|
|
291
|
+
left: 80%;
|
|
292
|
+
font-size: 4rem;
|
|
293
|
+
animation-delay: 1s;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.ai-element:nth-child(3) {
|
|
297
|
+
top: 30%;
|
|
298
|
+
left: 70%;
|
|
299
|
+
font-size: 3rem;
|
|
300
|
+
animation-delay: 2s;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.ai-element:nth-child(4) {
|
|
304
|
+
top: 80%;
|
|
305
|
+
left: 20%;
|
|
306
|
+
font-size: 6rem;
|
|
307
|
+
animation-delay: 3s;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
@keyframes float {
|
|
311
|
+
|
|
312
|
+
0%,
|
|
313
|
+
100% {
|
|
314
|
+
transform: translateY(0) rotate(0deg);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
50% {
|
|
318
|
+
transform: translateY(-20px) rotate(5deg);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
@keyframes fadeInUp {
|
|
323
|
+
from {
|
|
324
|
+
opacity: 0;
|
|
325
|
+
transform: translateY(20px);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
to {
|
|
329
|
+
opacity: 1;
|
|
330
|
+
transform: translateY(0);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
@media (max-width: 768px) {
|
|
335
|
+
.login-box {
|
|
336
|
+
padding: 2rem;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.login-title {
|
|
340
|
+
font-size: 1.8rem;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
@media (max-width: 576px) {
|
|
345
|
+
.login-box {
|
|
346
|
+
padding: 1.5rem;
|
|
347
|
+
border-radius: var(--radius-lg);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.login-links {
|
|
351
|
+
flex-direction: column;
|
|
352
|
+
gap: 0.5rem;
|
|
353
|
+
align-items: center;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.remember-me {
|
|
358
|
+
display: flex;
|
|
359
|
+
align-items: center;
|
|
360
|
+
justify-content: center;
|
|
361
|
+
width: 100%;
|
|
362
|
+
margin: 1rem 0;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.remember-me input[type="checkbox"] {
|
|
366
|
+
appearance: none;
|
|
367
|
+
-webkit-appearance: none;
|
|
368
|
+
width: 18px;
|
|
369
|
+
height: 18px;
|
|
370
|
+
border: 2px solid var(--glass-border);
|
|
371
|
+
border-radius: var(--radius-sm);
|
|
372
|
+
margin-right: 10px;
|
|
373
|
+
cursor: pointer;
|
|
374
|
+
position: relative;
|
|
375
|
+
transition: var(--transition);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.remember-me input[type="checkbox"]:checked {
|
|
379
|
+
background-color: var(--primary);
|
|
380
|
+
border-color: var(--primary);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.remember-me input[type="checkbox"]:checked::after {
|
|
384
|
+
content: '\f00c';
|
|
385
|
+
font-family: 'Font Awesome 6 Free';
|
|
386
|
+
font-weight: 900;
|
|
387
|
+
position: absolute;
|
|
388
|
+
top: 50%;
|
|
389
|
+
left: 50%;
|
|
390
|
+
transform: translate(-50%, -50%);
|
|
391
|
+
color: white;
|
|
392
|
+
font-size: 10px;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.remember-me label {
|
|
396
|
+
color: var(--gray);
|
|
397
|
+
cursor: pointer;
|
|
398
|
+
transition: var(--transition);
|
|
399
|
+
font-size: 0.9rem;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.remember-me label:hover {
|
|
403
|
+
color: var(--light);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.WarningboxInfo {
|
|
407
|
+
background: var(--dark-light);
|
|
408
|
+
border: 0.5px solid var(--warning);
|
|
409
|
+
border-left: 4px solid var(--warning);
|
|
410
|
+
padding: 0.75rem 1rem;
|
|
411
|
+
border-radius: var(--radius-sm);
|
|
412
|
+
color: var(--warning);
|
|
413
|
+
font-size: 0.9rem;
|
|
414
|
+
font-weight: 500;
|
|
415
|
+
margin-top: 1rem;
|
|
416
|
+
text-align: center;
|
|
417
|
+
box-shadow: var(--shadow-sm);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.status-container {
|
|
421
|
+
flex: 1;
|
|
422
|
+
display: flex;
|
|
423
|
+
align-items: center;
|
|
424
|
+
justify-content: center;
|
|
425
|
+
padding: 120px 5% 80px;
|
|
426
|
+
position: relative;
|
|
427
|
+
overflow: hidden;
|
|
428
|
+
background: radial-gradient(circle at 70% 20%, rgba(67, 97, 238, 0.15), transparent 60%);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.status-box {
|
|
432
|
+
background: var(--dark-light);
|
|
433
|
+
border-radius: var(--radius-xl);
|
|
434
|
+
padding: 2rem;
|
|
435
|
+
width: 100%;
|
|
436
|
+
max-width: 600px;
|
|
437
|
+
box-shadow: var(--shadow-lg);
|
|
438
|
+
border: .125rem solid var(--glass-border);
|
|
439
|
+
position: relative;
|
|
440
|
+
z-index: 2;
|
|
441
|
+
transition: var(--transition);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.status-box:hover {
|
|
445
|
+
box-shadow: var(--shadow-lg);
|
|
446
|
+
transform: scale(1.05);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.status-code {
|
|
450
|
+
text-align: center;
|
|
451
|
+
margin-bottom: 1rem;
|
|
452
|
+
font-size: 2.5rem;
|
|
453
|
+
font-weight: 700;
|
|
454
|
+
color: var(--success);
|
|
455
|
+
position: relative;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.status-title {
|
|
459
|
+
text-align: center;
|
|
460
|
+
margin-bottom: 1rem;
|
|
461
|
+
font-size: 1.5rem;
|
|
462
|
+
color: var(--lighter);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
.status-code::after {
|
|
466
|
+
content: '';
|
|
467
|
+
position: absolute;
|
|
468
|
+
bottom: -10px;
|
|
469
|
+
left: 50%;
|
|
470
|
+
transform: translateX(-50%);
|
|
471
|
+
width: 100px;
|
|
472
|
+
height: 4px;
|
|
473
|
+
background: var(--gradient);
|
|
474
|
+
border-radius: 2px;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
.status-content {
|
|
478
|
+
margin-bottom: 1.5rem;
|
|
479
|
+
text-align: center;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.status-message {
|
|
483
|
+
font-size: 1.2rem;
|
|
484
|
+
margin: 0.5rem 0;
|
|
485
|
+
color: var(--warning);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.status-links {
|
|
489
|
+
display: flex;
|
|
490
|
+
justify-content: center;
|
|
491
|
+
margin-top: 1rem;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.status-link {
|
|
495
|
+
color: var(--primary);
|
|
496
|
+
text-decoration: none;
|
|
497
|
+
transition: var(--transition);
|
|
498
|
+
margin: 0 15px;
|
|
499
|
+
font-size: 1rem;
|
|
500
|
+
font-weight: 600;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
.status-link:hover {
|
|
504
|
+
text-decoration: underline;
|
|
505
|
+
color: var(--primary-dark);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
.details-wrapper {
|
|
509
|
+
margin-top: 1rem;
|
|
510
|
+
background: var(--dark-light);
|
|
511
|
+
border-radius: var(--radius-md);
|
|
512
|
+
padding: 1.5rem;
|
|
513
|
+
box-shadow: var(--shadow-md);
|
|
514
|
+
cursor: pointer;
|
|
515
|
+
transition: var(--transition);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
.details-header {
|
|
519
|
+
display: flex;
|
|
520
|
+
justify-content: space-between;
|
|
521
|
+
align-items: center;
|
|
522
|
+
color: var(--lighter);
|
|
523
|
+
font-size: 1.2rem;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
.details-header i {
|
|
527
|
+
transition: var(--transition);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
.details-wrapper:hover i {
|
|
531
|
+
transform: rotate(180deg);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
.error-details-wrapper {
|
|
535
|
+
display: none;
|
|
536
|
+
margin-top: 1rem;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
.details-wrapper.active .error-details-wrapper {
|
|
540
|
+
display: block;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
.error-details {
|
|
544
|
+
width: 100%;
|
|
545
|
+
height: 150px;
|
|
546
|
+
background: var(--darker);
|
|
547
|
+
border: 1px solid var(--glass-border);
|
|
548
|
+
border-radius: var(--radius-sm);
|
|
549
|
+
color: var(--light);
|
|
550
|
+
padding: 1rem;
|
|
551
|
+
resize: none;
|
|
552
|
+
font-size: 0.9rem;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
.copy-btn {
|
|
556
|
+
margin-top: 0.5rem;
|
|
557
|
+
background: var(--primary);
|
|
558
|
+
color: var(--lighter);
|
|
559
|
+
border: none;
|
|
560
|
+
border-radius: var(--radius-sm);
|
|
561
|
+
padding: 0.75rem 1.5rem;
|
|
562
|
+
cursor: pointer;
|
|
563
|
+
transition: var(--transition);
|
|
564
|
+
font-size: 1rem;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
.copy-btn:hover {
|
|
568
|
+
background: var(--primary-dark);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
.status-element {
|
|
572
|
+
position: absolute;
|
|
573
|
+
opacity: 0.1;
|
|
574
|
+
z-index: 1;
|
|
575
|
+
animation: float 6s ease-in-out infinite;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
.status-element:nth-child(1) {
|
|
579
|
+
top: 20%;
|
|
580
|
+
left: 10%;
|
|
581
|
+
font-size: 5rem;
|
|
582
|
+
animation-delay: 0s;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
.status-element:nth-child(2) {
|
|
586
|
+
top: 60%;
|
|
587
|
+
left: 80%;
|
|
588
|
+
font-size: 4rem;
|
|
589
|
+
animation-delay: 1s;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
.status-element:nth-child(3) {
|
|
593
|
+
top: 30%;
|
|
594
|
+
left: 70%;
|
|
595
|
+
font-size: 3rem;
|
|
596
|
+
animation-delay: 2s;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
.status-element:nth-child(4) {
|
|
600
|
+
top: 80%;
|
|
601
|
+
left: 20%;
|
|
602
|
+
font-size: 6rem;
|
|
603
|
+
animation-delay: 3s;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
.status-element:nth-child(5) {
|
|
607
|
+
top: 50%;
|
|
608
|
+
left: 40%;
|
|
609
|
+
font-size: 4.5rem;
|
|
610
|
+
animation-delay: 4s;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
.status-element:nth-child(6) {
|
|
614
|
+
top: 70%;
|
|
615
|
+
left: 60%;
|
|
616
|
+
font-size: 5.5rem;
|
|
617
|
+
animation-delay: 5s;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
@keyframes float {
|
|
621
|
+
|
|
622
|
+
0%,
|
|
623
|
+
100% {
|
|
624
|
+
transform: translateY(0) rotate(0deg);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
50% {
|
|
628
|
+
transform: translateY(-20px) rotate(5deg);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
</style>
|
|
632
|
+
</head>
|
|
633
|
+
|
|
634
|
+
<body>
|
|
635
|
+
<header>
|
|
636
|
+
<nav>
|
|
637
|
+
<div class="navbar">
|
|
638
|
+
<a class="logo">
|
|
639
|
+
<img src="https://mbktechstudio.com/Assets/Images/Icon/dgicon.svg" alt="MBK Tech Studio Logo">
|
|
640
|
+
<span class="logo-text">MBK Authe</span>
|
|
641
|
+
</a>
|
|
642
|
+
</div>
|
|
643
|
+
</nav>
|
|
644
|
+
</header>
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
<section class="status-container">
|
|
648
|
+
|
|
649
|
+
<i class="fas fa-server status-element"></i>
|
|
650
|
+
<i class="fas fa-database status-element"></i>
|
|
651
|
+
<i class="fas fa-network-wired status-element"></i>
|
|
652
|
+
<i class="fas fa-code status-element"></i>
|
|
653
|
+
<i class="fas fa-cloud status-element"></i>
|
|
654
|
+
<i class="fas fa-shield-alt status-element"></i>
|
|
655
|
+
|
|
656
|
+
<div class="status-box">
|
|
657
|
+
<h1 class="status-code">{{code}}</h1>
|
|
658
|
+
<h1 class="status-title">{{error}}</h1>
|
|
659
|
+
|
|
660
|
+
<div class="status-content">
|
|
661
|
+
<p class="status-message">{{message}}</p>
|
|
662
|
+
</div>
|
|
663
|
+
|
|
664
|
+
<div class="status-links">
|
|
665
|
+
<a href="{{page}}" class="status-link">Go to {{pagename}} page</a>
|
|
666
|
+
</div>
|
|
667
|
+
|
|
668
|
+
{{#if details}}
|
|
669
|
+
<div class="details-wrapper" onclick="toggleDetailsDropdown(this)">
|
|
670
|
+
<div class="details-header">
|
|
671
|
+
<span>Show Error Details</span>
|
|
672
|
+
<i class="fas fa-chevron-down"></i>
|
|
673
|
+
</div>
|
|
674
|
+
<div class="error-details-wrapper">
|
|
675
|
+
<textarea class="error-details" id="errorDetails" readonly>{{details}}</textarea>
|
|
676
|
+
<button class="copy-btn" type="button" aria-label="Copy error details"
|
|
677
|
+
onclick="copyErrorDetails(this); event.stopPropagation();">
|
|
678
|
+
<i class="fas fa-copy"></i> Copy
|
|
679
|
+
</button>
|
|
680
|
+
</div>
|
|
681
|
+
</div>
|
|
682
|
+
{{/if}}
|
|
683
|
+
|
|
684
|
+
</div>
|
|
685
|
+
</section>
|
|
686
|
+
<script>
|
|
687
|
+
function toggleDetailsDropdown(element) {
|
|
688
|
+
const errorDetailsWrapper = element.querySelector('.error-details-wrapper');
|
|
689
|
+
const icon = element.querySelector('.details-header i');
|
|
690
|
+
|
|
691
|
+
if (errorDetailsWrapper.style.display === 'block') {
|
|
692
|
+
errorDetailsWrapper.style.display = 'none';
|
|
693
|
+
icon.style.transform = 'rotate(0deg)';
|
|
694
|
+
} else {
|
|
695
|
+
errorDetailsWrapper.style.display = 'block';
|
|
696
|
+
icon.style.transform = 'rotate(180deg)';
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
function copyErrorDetails(button) {
|
|
701
|
+
const details = button.previousElementSibling.value;
|
|
702
|
+
navigator.clipboard.writeText(details).then(() => {
|
|
703
|
+
button.textContent = 'Copied!';
|
|
704
|
+
setTimeout(() => {
|
|
705
|
+
button.textContent = 'Copy';
|
|
706
|
+
}, 2000);
|
|
707
|
+
}).catch(err => {
|
|
708
|
+
console.error('Failed to copy: ', err);
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
713
|
+
const detailsWrappers = document.querySelectorAll('.details-wrapper');
|
|
714
|
+
detailsWrappers.forEach(wrapper => {
|
|
715
|
+
wrapper.addEventListener('click', () => {
|
|
716
|
+
wrapper.classList.toggle('active');
|
|
717
|
+
});
|
|
718
|
+
});
|
|
719
|
+
});
|
|
720
|
+
</script>
|
|
721
|
+
</body>
|
|
722
|
+
|
|
723
|
+
</html>
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
<head>
|
|
6
6
|
<meta charset="UTF-8">
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
8
|
-
<meta name="description"
|
|
8
|
+
<meta name="description"
|
|
9
|
+
content="Log in to portal.mbktechstudio.com to access your resources and manage projects securely.">
|
|
9
10
|
<meta name="keywords" content="MBK Tech Studio, Web-Portal, Web, Portal, Admin-Panel, Admin, login">
|
|
10
11
|
<meta property="og:title" content="Login | MBK Tech Studio" />
|
|
11
12
|
<meta property="og:image" content="https://www.mbktechstudio.com/Assets/Images/Icon/logo.png" />
|
|
@@ -15,8 +16,8 @@
|
|
|
15
16
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
|
16
17
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
17
18
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
18
|
-
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap"
|
|
19
|
-
|
|
19
|
+
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap"
|
|
20
|
+
rel="stylesheet">
|
|
20
21
|
<style>
|
|
21
22
|
:root {
|
|
22
23
|
--primary: #4361ee;
|
|
@@ -270,12 +271,6 @@
|
|
|
270
271
|
font-weight: 500;
|
|
271
272
|
}
|
|
272
273
|
|
|
273
|
-
.recaptcha-container {
|
|
274
|
-
margin: 1rem 0;
|
|
275
|
-
display: flex;
|
|
276
|
-
justify-content: center;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
274
|
.token-container {
|
|
280
275
|
animation: fadeInUp 0.4s ease-out;
|
|
281
276
|
}
|
|
@@ -418,7 +413,7 @@
|
|
|
418
413
|
.remember-me label:hover {
|
|
419
414
|
color: var(--light);
|
|
420
415
|
}
|
|
421
|
-
|
|
416
|
+
|
|
422
417
|
.WarningboxInfo {
|
|
423
418
|
background: var(--dark-light);
|
|
424
419
|
border: 0.5px solid var(--warning);
|
|
@@ -483,11 +478,6 @@
|
|
|
483
478
|
<i class="fas fa-info-circle input-icon" onclick="tokeninfo()"></i>
|
|
484
479
|
</div>
|
|
485
480
|
|
|
486
|
-
<div class="recaptcha-container">
|
|
487
|
-
<div class="g-recaptcha" data-theme="dark" data-sitekey="6LfhaPgqAAAAAPgOw7r3rNOHKKfh5d7K3MMJeUHo">
|
|
488
|
-
</div>
|
|
489
|
-
</div>
|
|
490
|
-
|
|
491
481
|
<div class="form-group remember-me">
|
|
492
482
|
<input type="checkbox" id="rememberMe" name="rememberMe">
|
|
493
483
|
<label for="rememberMe">Remember me</label>
|
|
@@ -553,7 +543,6 @@
|
|
|
553
543
|
const username = document.getElementById('loginUsername').value.trim();
|
|
554
544
|
const password = document.getElementById('loginPassword').value.trim();
|
|
555
545
|
const token = document.getElementById('token') ? document.getElementById('token').value.trim() : '';
|
|
556
|
-
const recaptchaResponse = grecaptcha.getResponse();
|
|
557
546
|
const loginButton = document.getElementById('loginButton');
|
|
558
547
|
const loginButtonText = document.getElementById('loginButtonText');
|
|
559
548
|
const rememberMe = document.getElementById('rememberMe').checked;
|
|
@@ -575,8 +564,7 @@
|
|
|
575
564
|
body: JSON.stringify({
|
|
576
565
|
username,
|
|
577
566
|
password,
|
|
578
|
-
token
|
|
579
|
-
recaptcha: recaptchaResponse
|
|
567
|
+
token
|
|
580
568
|
})
|
|
581
569
|
})
|
|
582
570
|
.then(response => response.json())
|
|
@@ -590,7 +578,7 @@
|
|
|
590
578
|
} else {
|
|
591
579
|
localStorage.removeItem('rememberedUsername');
|
|
592
580
|
}
|
|
593
|
-
|
|
581
|
+
|
|
594
582
|
// Redirect to the appropriate page
|
|
595
583
|
const redirectUrl = new URLSearchParams(window.location.search).get('redirect');
|
|
596
584
|
window.location.href = redirectUrl ? decodeURIComponent(redirectUrl) : '{{customURL}}';
|
|
@@ -599,7 +587,6 @@
|
|
|
599
587
|
loginButtonText.textContent = 'Login';
|
|
600
588
|
} else {
|
|
601
589
|
// Handle errors
|
|
602
|
-
grecaptcha.reset();
|
|
603
590
|
loginButton.disabled = false;
|
|
604
591
|
loginButtonText.textContent = 'Login';
|
|
605
592
|
|
|
@@ -615,7 +602,6 @@
|
|
|
615
602
|
}
|
|
616
603
|
})
|
|
617
604
|
.catch(error => {
|
|
618
|
-
grecaptcha.reset();
|
|
619
605
|
loginButton.disabled = false;
|
|
620
606
|
loginButtonText.textContent = 'Login';
|
|
621
607
|
console.error('Error:', error);
|