mbkauthe 1.1.13 → 1.1.14

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 CHANGED
@@ -1,15 +1,13 @@
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
- "layout": false
9
+ "layout": false,
10
+ "loginRedirectURL": "/admin"
13
11
  }'
14
12
 
15
13
  # 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), session restoration, and reCAPTCHA verification.
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,14 @@ 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
- "layout": false
94
+ "layout": false,
95
+ "loginRedirectURL": "/admin"
99
96
  }'
100
97
  ```
101
98
 
@@ -198,7 +195,6 @@ router.post(["/terminateAllSessions"], authenticate(mbkautheVar.Password), (req,
198
195
  - `username`: User's username.
199
196
  - `password`: User's password.
200
197
  - `token`: (Optional) 2FA token.
201
- - `recaptcha`: reCAPTCHA response.
202
198
 
203
199
  - Response:
204
200
  - `200`: Login successful.
package/env.md CHANGED
@@ -1,75 +1,198 @@
1
- # Configuration Guide
1
+ # Environment Configuration Guide
2
2
 
3
- [<- Back](README.md)
3
+ [ Back to README](README.md)
4
4
 
5
- ## Application Settings
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
- ```properties
7
+ ---
8
+
9
+ ## 📱 Application Settings
10
+
11
+ ### App Name Configuration
12
+ ```env
8
13
  APP_NAME=mbkauthe
9
14
  ```
10
15
 
11
- > **APP_NAME**: Specifies the name of the application. This is used to distinguish one project from another and is critical for ensuring users are restricted to specific apps. It corresponds to the `AllowedApp` column in the Users table.
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
- ## reCAPTCHA Settings
22
+ ---
14
23
 
15
- ```properties
16
- RECAPTCHA_ENABLED=true
17
- RECAPTCHA_SECRET_KEY=your-secret-key
18
- BYPASS_USERS=["demo", "user1"]
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
- > **RECAPTCHA_ENABLED**: Set to `true` to enable reCAPTCHA verification.
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
- > **RECAPTCHA_SECRET_KEY**: Provide the secret key obtained from the [Google reCAPTCHA Admin Console](https://www.google.com/recaptcha/admin).
41
+ #### IS_DEPLOYED
42
+ **Description:** Deployment environment flag that affects session behavior.
24
43
 
25
- > **BYPASS_USERS**: Specify an array of usernames (e.g., `["demo", "user1"]`) that will bypass reCAPTCHA verification.
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
- > **Note**: Ensure `RECAPTCHA_SECRET_KEY` is set when `RECAPTCHA_ENABLED=true`.
51
+ **Default:** `false`
28
52
 
53
+ #### DOMAIN
54
+ **Description:** Your application's domain name.
29
55
 
30
- ## Session Settings
31
- ```properties
32
- SESSION_SECRET_KEY=123
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
- DOMAIN=mbktechstudio.com
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
- > **IS_DEPLOYED**:
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
- > - `true`: For deployed environments. Sessions are shared across all subDOMAINs of `.mbktechstudio.com` or the DOMAIN specified in `DOMAIN`.
81
+ **Description:** PostgreSQL database connection string for user authentication.
41
82
 
42
- > - `false`: For local development.
83
+ **Format:** `postgresql://[username]:[password]@[host]:[port]/[database]`
43
84
 
44
- > - Important: If set to `true`, login functionality will not work on `localhost`. Use a valid DOMAIN for proper operation.
85
+ **Examples:**
86
+ ```env
87
+ # Local database
88
+ LOGIN_DB=postgresql://admin:password123@localhost:5432/mbkauth_db
45
89
 
46
- > **DOMAIN**:
90
+ # Remote database
91
+ LOGIN_DB=postgresql://user:pass@db.example.com:5432/production_db
47
92
 
48
- > - Set `DOMAIN` to your DOMAIN
93
+ # With SSL (recommended for production)
94
+ LOGIN_DB=postgresql://user:pass@host:5432/db?sslmode=require
95
+ ```
49
96
 
50
- > - If you don't have a DOMAIN, set `IS_DEPLOYED=false`.
97
+ **Required:** Yes
51
98
 
99
+ ---
52
100
 
53
- ## Database Settings
101
+ ## 🔒 Two-Factor Authentication (2FA)
54
102
 
55
- ```properties
56
- LOGIN_DB=postgresql://username:password@server.DOMAIN/db_name
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
- ## Two-Factor Authentication (2FA)
62
- ```properties
63
- MBKAUTH_TWO_FA_ENABLE=false
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
- ## Cookie Settings
141
+ ## 🚀 Quick Setup Examples
69
142
 
70
- ```properties
71
- COOKIE_EXPIRE_TIME=5
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
- "layout": false
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", "RECAPTCHA_Enabled", "SESSION_SECRET_KEY", "IS_DEPLOYED", "LOGIN_DB", "MBKAUTH_TWO_FA_ENABLE", "DOMAIN"];
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") {
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, recaptcha } = req.body;
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({
@@ -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", "RECAPTCHA_Enabled", "SESSION_SECRET_KEY", "IS_DEPLOYED", "LOGIN_DB", "MBKAUTH_TWO_FA_ENABLE", "DOMAIN"];
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mbkauthe",
3
- "version": "1.1.13",
3
+ "version": "1.1.14",
4
4
  "description": "MBKTechStudio's reusable authentication system for Node.js applications.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -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" content="Log in to portal.mbktechstudio.com to access your resources and manage projects securely.">
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" rel="stylesheet">
19
- <script src="https://www.google.com/recaptcha/api.js" async defer></script>
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);