mbkauthe 4.8.2 → 4.8.4
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/README.md +25 -6
- package/docs/api.md +45 -27
- package/docs/auth-flows.mmd +9 -9
- package/docs/auth-processes.mmd +71 -0
- package/docs/db.md +36 -276
- package/docs/db.sql +6 -9
- package/docs/env.md +2 -8
- package/docs/error-messages.md +3 -3
- package/docs/images/auth-flows.svg +1 -1
- package/docs/images/auth-process.svg +1 -0
- package/index.d.ts +1 -2
- package/index.js +1 -1
- package/lib/config/cookies.js +6 -6
- package/lib/config/index.js +4 -10
- package/lib/createTable.js +5 -5
- package/lib/middleware/auth.js +30 -20
- package/lib/middleware/index.js +2 -2
- package/lib/pool.js +2 -2
- package/lib/routes/auth.js +26 -38
- package/lib/routes/dbLogs.js +3 -3
- package/lib/routes/misc.js +22 -22
- package/lib/utils/timingSafeToken.js +35 -0
- package/package.json +1 -1
- package/public/main.js +3 -3
- package/views/Error/dError.handlebars +1 -1
- package/views/pages/2fa.handlebars +1 -0
- package/views/pages/accountSwitch.handlebars +4 -4
- package/views/pages/loginmbkauthe.handlebars +2 -1
- package/views/profilemenu.handlebars +2 -2
package/docs/db.md
CHANGED
|
@@ -1,241 +1,55 @@
|
|
|
1
1
|
# Database Schema
|
|
2
2
|
|
|
3
|
-
This
|
|
3
|
+
**Executable DDL lives only in [`docs/db.sql`](db.sql).** This file explains what that script creates and how the app uses it. Run the script against Postgres when bootstrapping or aligning a database (for example `psql $DATABASE_URL -f docs/db.sql`). The app can also apply it via `lib/createTable.js`, which reads `docs/db.sql`.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## 1. Roles
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
```sql
|
|
12
|
-
DO $$
|
|
13
|
-
BEGIN
|
|
14
|
-
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'role') THEN
|
|
15
|
-
CREATE TYPE role AS ENUM ('SuperAdmin', 'NormalUser', 'Guest', 'member');
|
|
16
|
-
END IF;
|
|
17
|
-
END
|
|
18
|
-
$$;
|
|
19
|
-
```
|
|
9
|
+
Postgres enum `role`: `SuperAdmin`, `NormalUser`, `Guest`, `member`. The script creates the type only if it does not already exist. `Users."Role"` defaults to `NormalUser`.
|
|
20
10
|
|
|
21
11
|
---
|
|
22
12
|
|
|
23
|
-
## 2. Users
|
|
24
|
-
|
|
25
|
-
Stores user accounts and profile metadata.
|
|
26
|
-
|
|
27
|
-
```sql
|
|
28
|
-
CREATE TABLE IF NOT EXISTS "Users" (
|
|
29
|
-
id SERIAL PRIMARY KEY,
|
|
30
|
-
"UserName" VARCHAR(50) NOT NULL UNIQUE,
|
|
31
|
-
"Password" VARCHAR(255) NOT NULL,
|
|
32
|
-
"Active" BOOLEAN DEFAULT FALSE,
|
|
33
|
-
"Role" role DEFAULT 'NormalUser' NOT NULL,
|
|
34
|
-
"HaveMailAccount" BOOLEAN DEFAULT FALSE,
|
|
35
|
-
"AllowedApps" JSONB DEFAULT '["mbkauthe", "portal"]',
|
|
36
|
-
"created_at" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
37
|
-
"updated_at" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
38
|
-
"last_login" TIMESTAMP WITH TIME ZONE,
|
|
39
|
-
"PasswordEnc" VARCHAR(128),
|
|
40
|
-
|
|
41
|
-
"FullName" VARCHAR(255),
|
|
42
|
-
"email" TEXT DEFAULT 'support@mbktech.org',
|
|
43
|
-
"Image" TEXT DEFAULT 'https://portal.mbktech.org/Assets/Images/M.png',
|
|
44
|
-
"Bio" TEXT DEFAULT 'I am ....',
|
|
45
|
-
"SocialAccounts" TEXT DEFAULT '{}',
|
|
46
|
-
"Positions" jsonb DEFAULT '{"Not_Permanent":"Member Is Not Permanent"}',
|
|
47
|
-
"resetToken" TEXT,
|
|
48
|
-
"resetTokenExpires" TimeStamp,
|
|
49
|
-
"resetAttempts" INTEGER DEFAULT '0',
|
|
50
|
-
"lastResetAttempt" TimeStamp WITH TIME ZONE
|
|
51
|
-
);
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### Indexes
|
|
55
|
-
|
|
56
|
-
```sql
|
|
57
|
-
CREATE INDEX IF NOT EXISTS idx_users_username ON "Users" USING BTREE ("UserName");
|
|
58
|
-
CREATE INDEX IF NOT EXISTS idx_users_role ON "Users" USING BTREE ("Role");
|
|
59
|
-
CREATE INDEX IF NOT EXISTS idx_users_active ON "Users" USING BTREE ("Active");
|
|
60
|
-
CREATE INDEX IF NOT EXISTS idx_users_email ON "Users" USING BTREE ("email");
|
|
61
|
-
CREATE INDEX IF NOT EXISTS idx_users_last_login ON "Users" USING BTREE (last_login);
|
|
62
|
-
-- JSONB GIN indexes for common filters/queries on JSON fields
|
|
63
|
-
CREATE INDEX IF NOT EXISTS idx_users_allowedapps_gin ON "Users" USING GIN ("AllowedApps");
|
|
64
|
-
CREATE INDEX IF NOT EXISTS idx_users_positions_gin ON "Users" USING GIN ("Positions");
|
|
65
|
-
```
|
|
13
|
+
## 2. Users
|
|
66
14
|
|
|
67
|
-
|
|
15
|
+
Core accounts table (`"Users"`): username, activation flag, role, mail flag, `AllowedApps` and `Positions` as JSONB, timestamps, optional `last_login`, and password hash column `PasswordEnc` (no plaintext passwords).
|
|
68
16
|
|
|
69
|
-
-
|
|
70
|
-
- `PasswordEnc` is used when `EncPass=true` (PBKDF2 hashed, stored as a 128-character hex string).
|
|
71
|
-
- Only one of the two columns should be populated depending on the configuration.
|
|
17
|
+
Profile-style columns include `FullName`, `email`, `Image`, `Bio`, `SocialAccounts`, and password-reset fields (`resetToken`, `resetTokenExpires`, `resetAttempts`, `lastResetAttempt`).
|
|
72
18
|
|
|
19
|
+
Indexes cover username, role, active, email, last login, and GIN indexes on JSONB for `AllowedApps` and `Positions`. The SQL file also adds optional covering indexes used on hot auth paths.
|
|
73
20
|
|
|
74
21
|
---
|
|
75
22
|
|
|
76
|
-
## 3. OAuth
|
|
77
|
-
|
|
78
|
-
OAuth link tables store associations between an existing user account and an OAuth provider.
|
|
23
|
+
## 3. OAuth: `user_github` and `user_google`
|
|
79
24
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
```sql
|
|
83
|
-
CREATE TABLE IF NOT EXISTS user_github (
|
|
84
|
-
id SERIAL PRIMARY KEY,
|
|
85
|
-
user_name VARCHAR(50) REFERENCES "Users"("UserName"),
|
|
86
|
-
github_id VARCHAR(255) UNIQUE,
|
|
87
|
-
github_username VARCHAR(255),
|
|
88
|
-
installation_id BIGINT,
|
|
89
|
-
installation_target_type VARCHAR(32),
|
|
90
|
-
access_token VARCHAR(255),
|
|
91
|
-
created_at TimeStamp WITH TIME ZONE DEFAULT NOW(),
|
|
92
|
-
updated_at TimeStamp WITH TIME ZONE DEFAULT NOW()
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
CREATE INDEX IF NOT EXISTS idx_user_github_github_id ON user_github (github_id);
|
|
96
|
-
CREATE INDEX IF NOT EXISTS idx_user_github_user_name ON user_github (user_name);
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### Google
|
|
100
|
-
|
|
101
|
-
```sql
|
|
102
|
-
CREATE TABLE IF NOT EXISTS user_google (
|
|
103
|
-
id SERIAL PRIMARY KEY,
|
|
104
|
-
user_name VARCHAR(50) REFERENCES "Users"("UserName"),
|
|
105
|
-
google_id VARCHAR(255) UNIQUE,
|
|
106
|
-
google_email VARCHAR(255),
|
|
107
|
-
access_token TEXT,
|
|
108
|
-
created_at TimeStamp WITH TIME ZONE DEFAULT NOW(),
|
|
109
|
-
updated_at TimeStamp WITH TIME ZONE DEFAULT NOW()
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
CREATE INDEX IF NOT EXISTS idx_user_google_google_id ON user_google (google_id);
|
|
113
|
-
CREATE INDEX IF NOT EXISTS idx_user_google_user_name ON user_google (user_name);
|
|
114
|
-
```
|
|
25
|
+
Link rows from `"Users"("UserName")` to GitHub or Google identities (provider ids, usernames/emails, tokens, timestamps). `user_github` may be altered by the script to add `installation_id` and `installation_target_type` if missing (idempotent migrations).
|
|
115
26
|
|
|
116
27
|
---
|
|
117
28
|
|
|
118
|
-
## 4.
|
|
119
|
-
|
|
120
|
-
### Application Sessions (`Sessions`)
|
|
29
|
+
## 4. Sessions
|
|
121
30
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
```sql
|
|
125
|
-
CREATE TABLE IF NOT EXISTS "Sessions" (
|
|
126
|
-
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- requires pgcrypto or uuid-ossp
|
|
127
|
-
"UserName" VARCHAR(50) NOT NULL REFERENCES "Users"("UserName") ON DELETE CASCADE,
|
|
128
|
-
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
129
|
-
expires_at TIMESTAMP WITH TIME ZONE,
|
|
130
|
-
meta JSONB
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
CREATE INDEX IF NOT EXISTS idx_sessions_username ON "Sessions" ("UserName");
|
|
134
|
-
CREATE INDEX IF NOT EXISTS idx_sessions_user_created ON "Sessions" ("UserName", created_at);
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### Express Session Store (`session`)
|
|
138
|
-
|
|
139
|
-
Used by `express-session` when configured to store sessions in Postgres.
|
|
140
|
-
|
|
141
|
-
```sql
|
|
142
|
-
CREATE TABLE IF NOT EXISTS "session" (
|
|
143
|
-
sid VARCHAR(33) PRIMARY KEY NOT NULL,
|
|
144
|
-
sess JSONB NOT NULL,
|
|
145
|
-
expire TimeStamp WITH TIME ZONE NOT NULL
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
CREATE INDEX IF NOT EXISTS idx_session_expire ON "session" ("expire");
|
|
149
|
-
CREATE INDEX IF NOT EXISTS idx_session_user_id ON "session" ((sess->'user'->>'id'));
|
|
150
|
-
```
|
|
31
|
+
- **`"Sessions"`** — App session rows: UUID `id`, `UserName`, `created_at`, optional `expires_at`, optional `meta` JSONB. Requires `gen_random_uuid()` (e.g. `pgcrypto`). Extra indexes support expiry cleanup and middleware lookups.
|
|
32
|
+
- **`"session"`** — `express-session` Postgres store: `sid`, `sess` JSONB, `expire`, plus `username` and `last_activity` as in `db.sql`.
|
|
151
33
|
|
|
152
34
|
---
|
|
153
35
|
|
|
154
|
-
## 5. Two-
|
|
155
|
-
|
|
156
|
-
```sql
|
|
157
|
-
CREATE TABLE IF NOT EXISTS "TwoFA" (
|
|
158
|
-
"UserName" VARCHAR(50) primary key REFERENCES "Users"("UserName"),
|
|
159
|
-
"TwoFAStatus" boolean NOT NULL,
|
|
160
|
-
"TwoFASecret" TEXT
|
|
161
|
-
);
|
|
162
|
-
|
|
163
|
-
CREATE INDEX IF NOT EXISTS idx_twofa_username ON "TwoFA" ("UserName");
|
|
164
|
-
CREATE INDEX IF NOT EXISTS idx_twofa_username_status ON "TwoFA" ("UserName", "TwoFAStatus");
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
---
|
|
36
|
+
## 5. Two-factor: `TwoFA`
|
|
168
37
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
Stores trusted device tokens used to remember a device and bypass 2FA challenges.
|
|
172
|
-
|
|
173
|
-
```sql
|
|
174
|
-
CREATE TABLE IF NOT EXISTS "TrustedDevices" (
|
|
175
|
-
"id" SERIAL PRIMARY KEY,
|
|
176
|
-
"UserName" VARCHAR(50) NOT NULL REFERENCES "Users"("UserName") ON DELETE CASCADE,
|
|
177
|
-
"DeviceToken" VARCHAR(64) UNIQUE NOT NULL,
|
|
178
|
-
"DeviceName" VARCHAR(255),
|
|
179
|
-
"UserAgent" TEXT,
|
|
180
|
-
"IpAddress" VARCHAR(45),
|
|
181
|
-
"CreatedAt" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
182
|
-
"ExpiresAt" TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
183
|
-
"LastUsed" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
|
184
|
-
);
|
|
185
|
-
|
|
186
|
-
CREATE INDEX IF NOT EXISTS idx_trusted_devices_token ON "TrustedDevices"("DeviceToken");
|
|
187
|
-
CREATE INDEX IF NOT EXISTS idx_trusted_devices_username ON "TrustedDevices"("UserName");
|
|
188
|
-
CREATE INDEX IF NOT EXISTS idx_trusted_devices_expires ON "TrustedDevices"("ExpiresAt");
|
|
189
|
-
```
|
|
38
|
+
Per-user 2FA flag and secret, keyed by `UserName`.
|
|
190
39
|
|
|
191
40
|
---
|
|
192
41
|
|
|
193
|
-
##
|
|
194
|
-
|
|
195
|
-
Stores long-lived API tokens used for programmatic access.
|
|
196
|
-
|
|
197
|
-
```sql
|
|
198
|
-
CREATE TABLE IF NOT EXISTS "ApiTokens" (
|
|
199
|
-
"id" SERIAL PRIMARY KEY,
|
|
200
|
-
"UserName" VARCHAR(50) NOT NULL REFERENCES "Users"("UserName") ON DELETE CASCADE,
|
|
201
|
-
"Name" VARCHAR(255) NOT NULL CHECK (LENGTH(TRIM("Name")) > 0),
|
|
202
|
-
"TokenHash" VARCHAR(128) NOT NULL UNIQUE,
|
|
203
|
-
"Prefix" VARCHAR(32) NOT NULL,
|
|
204
|
-
"Permissions" JSONB NOT NULL DEFAULT '{"scope":"read-only","allowedApps":null}'::jsonb
|
|
205
|
-
CHECK ("Permissions"->>'scope' IN ('read-only', 'write')),
|
|
206
|
-
"LastUsed" TIMESTAMP WITH TIME ZONE,
|
|
207
|
-
"CreatedAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
208
|
-
"ExpiresAt" TIMESTAMP WITH TIME ZONE
|
|
209
|
-
CHECK ("ExpiresAt" IS NULL OR "ExpiresAt" > "CreatedAt")
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
CREATE INDEX IF NOT EXISTS idx_apitokens_tokenhash
|
|
213
|
-
ON "ApiTokens" ("TokenHash");
|
|
42
|
+
## 6. Trusted devices: `TrustedDevices`
|
|
214
43
|
|
|
215
|
-
|
|
216
|
-
ON "ApiTokens" ("UserName");
|
|
44
|
+
Remembered devices (token, optional name, user agent, IP, created/expires/last-used) to skip repeated 2FA when valid.
|
|
217
45
|
|
|
218
|
-
|
|
219
|
-
ON "ApiTokens" ("TokenHash", "ExpiresAt")
|
|
220
|
-
WHERE "ExpiresAt" IS NOT NULL;
|
|
221
|
-
|
|
222
|
-
CREATE INDEX IF NOT EXISTS idx_apitokens_username_created
|
|
223
|
-
ON "ApiTokens" ("UserName", "CreatedAt" DESC);
|
|
224
|
-
|
|
225
|
-
CREATE INDEX IF NOT EXISTS idx_apitokens_expires
|
|
226
|
-
ON "ApiTokens" ("ExpiresAt")
|
|
227
|
-
WHERE "ExpiresAt" IS NOT NULL;
|
|
228
|
-
|
|
229
|
-
CREATE INDEX IF NOT EXISTS idx_apitokens_permissions_gin
|
|
230
|
-
ON "ApiTokens" USING GIN ("Permissions");
|
|
46
|
+
---
|
|
231
47
|
|
|
232
|
-
|
|
233
|
-
ON "ApiTokens" (("Permissions"->>'scope'));
|
|
234
|
-
```
|
|
48
|
+
## 7. API tokens: `ApiTokens`
|
|
235
49
|
|
|
236
|
-
|
|
50
|
+
Named tokens per user: hash and prefix for lookup, optional expiry, `LastUsed`, and `Permissions` JSONB with constraints defined in SQL.
|
|
237
51
|
|
|
238
|
-
|
|
52
|
+
### `Permissions` shape (JSONB)
|
|
239
53
|
|
|
240
54
|
```json
|
|
241
55
|
{
|
|
@@ -244,88 +58,34 @@ The `Permissions` column stores both scope and allowed apps in a single JSONB st
|
|
|
244
58
|
}
|
|
245
59
|
```
|
|
246
60
|
|
|
247
|
-
|
|
248
|
-
- `
|
|
249
|
-
- `write`: Allows all HTTP methods (GET, POST, PUT, DELETE, PATCH, etc.)
|
|
250
|
-
|
|
251
|
-
**AllowedApps Values:**
|
|
252
|
-
- `null` (default): Token inherits allowed apps from user's `AllowedApps` in Users table
|
|
253
|
-
- `["app1", "app2"]`: Token is restricted to specific apps (must be subset of user's apps)
|
|
254
|
-
- `["*"]`: Token has access to all user's apps (for non-SuperAdmin) or all apps in system (SuperAdmin only)
|
|
255
|
-
- `[]` (empty array): Token has no app access (effectively disabled)
|
|
61
|
+
- **`scope`:** `read-only` limits to safe methods (GET, HEAD, OPTIONS); `write` allows mutating methods.
|
|
62
|
+
- **`allowedApps`:** `null` inherits the user’s `AllowedApps` from `"Users"`; a string array restricts to those apps (subset of the user’s apps); `["*"]` means all of the user’s apps (SuperAdmin: system-wide); `[]` effectively disables app access.
|
|
256
63
|
|
|
257
|
-
|
|
64
|
+
SuperAdmin users bypass app checks in the app layer; token `allowedApps` still matters for non–SuperAdmin users.
|
|
258
65
|
|
|
259
66
|
---
|
|
260
67
|
|
|
261
|
-
## 8. Seed
|
|
68
|
+
## 8. Seed data
|
|
262
69
|
|
|
263
|
-
|
|
70
|
+
`db.sql` inserts a default `support` user with a precomputed hash (documented there). Change that password immediately in production.
|
|
264
71
|
|
|
265
|
-
|
|
266
|
-
INSERT INTO "Users" ("UserName", "Password", "Role", "Active", "HaveMailAccount", "FullName")
|
|
267
|
-
VALUES ('support', '12345678', 'SuperAdmin', true, false, 'Support User')
|
|
268
|
-
ON CONFLICT ("UserName") DO NOTHING;
|
|
269
|
-
|
|
270
|
-
SELECT * FROM "Users" WHERE "UserName" = 'support';
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
- **Schema:**
|
|
275
|
-
```sql
|
|
276
|
-
CREATE TABLE "TrustedDevices" (
|
|
277
|
-
"id" SERIAL PRIMARY KEY,
|
|
278
|
-
"UserName" VARCHAR(50) NOT NULL REFERENCES "Users"("UserName") ON DELETE CASCADE,
|
|
279
|
-
"DeviceToken" VARCHAR(64) UNIQUE NOT NULL,
|
|
280
|
-
"DeviceName" VARCHAR(255),
|
|
281
|
-
"UserAgent" TEXT,
|
|
282
|
-
"IpAddress" VARCHAR(45),
|
|
283
|
-
"CreatedAt" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
284
|
-
"ExpiresAt" TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
285
|
-
"LastUsed" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
|
286
|
-
);
|
|
287
|
-
|
|
288
|
-
-- Add indexes for performance optimization
|
|
289
|
-
CREATE INDEX IF NOT EXISTS idx_trusted_devices_token ON "TrustedDevices"("DeviceToken");
|
|
290
|
-
CREATE INDEX IF NOT EXISTS idx_trusted_devices_username ON "TrustedDevices"("UserName");
|
|
291
|
-
CREATE INDEX IF NOT EXISTS idx_trusted_devices_expires ON "TrustedDevices"("ExpiresAt");
|
|
292
|
-
```
|
|
72
|
+
---
|
|
293
73
|
|
|
294
|
-
|
|
74
|
+
## 9. Other tables in `db.sql`
|
|
295
75
|
|
|
296
|
-
|
|
76
|
+
- **`todos`** — Tasks keyed by `username` → `"Users"`, with type (`personal` / `admin`), completion, assignment fields, and several btree indexes for listing/filtering.
|
|
77
|
+
- **`plan_upgrade_requests`** — Role/plan upgrade workflow: requester, requested role/plan, reason, optional links, status (`pending` / `approved` / `rejected`), admin review fields, timestamps, and indexes for admin queues.
|
|
297
78
|
|
|
298
|
-
|
|
299
|
-
```sql
|
|
300
|
-
INSERT INTO "Users" ("UserName", "Password", "Role", "Active", "HaveMailAccount")
|
|
301
|
-
VALUES ('support', '12345678', 'SuperAdmin', true, false);
|
|
79
|
+
---
|
|
302
80
|
|
|
303
|
-
|
|
304
|
-
VALUES ('test', '12345678', 'NormalUser', true, false);
|
|
305
|
-
```
|
|
81
|
+
## Adding users without duplicating SQL
|
|
306
82
|
|
|
307
|
-
|
|
308
|
-
```sql
|
|
309
|
-
-- Note: You'll need to hash the password using the hashPassword function
|
|
310
|
-
-- Example with pre-hashed password (PBKDF2 with username as salt)
|
|
311
|
-
INSERT INTO "Users" ("UserName", "PasswordEnc", "Role", "Active", "HaveMailAccount")
|
|
312
|
-
VALUES ('support', 'your_hashed_password_here', 'SuperAdmin', true, false);
|
|
83
|
+
Use `hashPassword(password, username)` from the library so `PasswordEnc` matches login verification (username participates as salt input).
|
|
313
84
|
|
|
314
|
-
|
|
315
|
-
|
|
85
|
+
```javascript
|
|
86
|
+
import { hashPassword } from "mbkauthe";
|
|
87
|
+
const encryptedPassword = hashPassword("your-password", "newusername");
|
|
88
|
+
// INSERT ... "PasswordEnc" = encryptedPassword (see column list in db.sql)
|
|
316
89
|
```
|
|
317
90
|
|
|
318
|
-
|
|
319
|
-
- Replace `support` and `test` with the desired usernames.
|
|
320
|
-
- For raw passwords: Replace `12345678` with the actual plain text passwords.
|
|
321
|
-
- For encrypted passwords: Use the hashPassword function to generate the hash before inserting.
|
|
322
|
-
- Adjust the `Role` values as needed (`SuperAdmin`, `NormalUser`, `Guest`, or `member`).
|
|
323
|
-
- Modify the `Active` and `HaveMailAccount` values as required.
|
|
324
|
-
|
|
325
|
-
**Generating Encrypted Passwords:**
|
|
326
|
-
If you're using `EncPass=true`, you can generate encrypted passwords using the hashPassword function:
|
|
327
|
-
```javascript
|
|
328
|
-
import { hashPassword } from 'mbkauthe';
|
|
329
|
-
const encryptedPassword = hashPassword('12345678', 'support');
|
|
330
|
-
console.log(encryptedPassword); // Use this value for PasswordEnc column
|
|
331
|
-
```
|
|
91
|
+
Replace usernames, roles (`SuperAdmin`, `NormalUser`, `Guest`, `member`), and flags (`Active`, `HaveMailAccount`) to match your needs.
|
package/docs/db.sql
CHANGED
|
@@ -10,7 +10,6 @@ $$;
|
|
|
10
10
|
CREATE TABLE IF NOT EXISTS "Users" (
|
|
11
11
|
id SERIAL PRIMARY KEY,
|
|
12
12
|
"UserName" VARCHAR(50) NOT NULL UNIQUE,
|
|
13
|
-
"Password" VARCHAR(255) NOT NULL,
|
|
14
13
|
"Active" BOOLEAN DEFAULT FALSE,
|
|
15
14
|
"Role" role DEFAULT 'NormalUser' NOT NULL,
|
|
16
15
|
"HaveMailAccount" BOOLEAN DEFAULT FALSE,
|
|
@@ -18,7 +17,8 @@ CREATE TABLE IF NOT EXISTS "Users" (
|
|
|
18
17
|
"created_at" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
19
18
|
"updated_at" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
20
19
|
"last_login" TIMESTAMP WITH TIME ZONE,
|
|
21
|
-
|
|
20
|
+
-- Store password hashes only (PBKDF2 / future algorithms). Do not store plaintext passwords.
|
|
21
|
+
"PasswordEnc" VARCHAR(255),
|
|
22
22
|
|
|
23
23
|
"FullName" VARCHAR(255),
|
|
24
24
|
"email" TEXT DEFAULT 'support@mbktech.org',
|
|
@@ -157,13 +157,10 @@ CREATE INDEX IF NOT EXISTS idx_trusted_devices_username ON "TrustedDevices"("Use
|
|
|
157
157
|
CREATE INDEX IF NOT EXISTS idx_trusted_devices_expires ON "TrustedDevices"("ExpiresAt");
|
|
158
158
|
|
|
159
159
|
|
|
160
|
-
--
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
INSERT INTO "Users" ("UserName", "Password", "Role", "Active", "HaveMailAccount", "FullName")
|
|
166
|
-
VALUES ('admin', '12345678', 'SuperAdmin', true, false, 'Admin User')
|
|
160
|
+
-- Seed users (hash-only). Default passwords are "12345678" for the sample accounts below.
|
|
161
|
+
-- Hashes were generated using mbkauthe's `hashPassword(password, username)`.
|
|
162
|
+
INSERT INTO "Users" ("UserName", "PasswordEnc", "Role", "Active", "HaveMailAccount", "FullName")
|
|
163
|
+
VALUES ('support', 'b8b10c1c9006d8c30ab81c412463c65ff6dae3293d9bfbaf5fd8e275081d0947f000a828004e2fbd3a8f6ef5a35ae3eddd4c57b00ecab376b12e607a16a57459', 'SuperAdmin', true, false, 'Support User')
|
|
167
164
|
ON CONFLICT ("UserName") DO NOTHING;
|
|
168
165
|
|
|
169
166
|
|
package/docs/env.md
CHANGED
|
@@ -50,12 +50,6 @@ This document describes the environment variables MBKAuth expects and keeps brie
|
|
|
50
50
|
- Example: `"MBKAUTH_TWO_FA_ENABLE":"true"`
|
|
51
51
|
- Required: Yes
|
|
52
52
|
|
|
53
|
-
- EncPass
|
|
54
|
-
- Description: When `true`, use hashed password column (`PasswordEnc`) instead of plain `Password`.
|
|
55
|
-
- Default: `false` (recommended `true` in production)
|
|
56
|
-
- Example: `"EncPass":"true"`
|
|
57
|
-
- Required: No
|
|
58
|
-
|
|
59
53
|
- COOKIE_EXPIRE_TIME
|
|
60
54
|
- Description: Session cookie lifetime (days).
|
|
61
55
|
- Default: `2`
|
|
@@ -123,14 +117,14 @@ This document describes the environment variables MBKAuth expects and keeps brie
|
|
|
123
117
|
Development (.env):
|
|
124
118
|
|
|
125
119
|
```env
|
|
126
|
-
mbkautheVar={"APP_NAME":"mbkauthe","Main_SECRET_TOKEN":"dev-token","SESSION_SECRET_KEY":"dev-secret","IS_DEPLOYED":"false","DOMAIN":"localhost","
|
|
120
|
+
mbkautheVar={"APP_NAME":"mbkauthe","Main_SECRET_TOKEN":"dev-token","SESSION_SECRET_KEY":"dev-secret","IS_DEPLOYED":"false","DOMAIN":"localhost","LOGIN_DB":"postgresql://user:pass@localhost:5432/mbkauth_dev","MBKAUTH_TWO_FA_ENABLE":"false"}
|
|
127
121
|
mbkauthShared={"GITHUB_LOGIN_ENABLED":"false"}
|
|
128
122
|
```
|
|
129
123
|
|
|
130
124
|
Production (short):
|
|
131
125
|
|
|
132
126
|
```env
|
|
133
|
-
mbkautheVar={"APP_NAME":"mbkauthe","Main_SECRET_TOKEN":"prod-token","SESSION_SECRET_KEY":"prod-secret","IS_DEPLOYED":"true","DOMAIN":"yourdomain.com","
|
|
127
|
+
mbkautheVar={"APP_NAME":"mbkauthe","Main_SECRET_TOKEN":"prod-token","SESSION_SECRET_KEY":"prod-secret","IS_DEPLOYED":"true","DOMAIN":"yourdomain.com","LOGIN_DB":"postgresql://dbuser:secure@db:5432/mbkauth_prod","MBKAUTH_TWO_FA_ENABLE":"true"}
|
|
134
128
|
```
|
|
135
129
|
|
|
136
130
|
---
|
package/docs/error-messages.md
CHANGED
|
@@ -455,9 +455,9 @@ function LoginForm() {
|
|
|
455
455
|
You can create custom error responses while maintaining the standard format:
|
|
456
456
|
|
|
457
457
|
```javascript
|
|
458
|
-
import { createErrorResponse } from 'mbkauthe';
|
|
458
|
+
import { createErrorResponse, sessVal } from 'mbkauthe';
|
|
459
459
|
|
|
460
|
-
app.post('/custom-action',
|
|
460
|
+
app.post('/custom-action', sessVal, async (req, res) => {
|
|
461
461
|
try {
|
|
462
462
|
// Your logic...
|
|
463
463
|
|
|
@@ -558,7 +558,7 @@ router.post('/mbkauthe/api/login', async (req, res) => {
|
|
|
558
558
|
res.json({ success: true, sessionId: '...' });
|
|
559
559
|
|
|
560
560
|
} catch (error) {
|
|
561
|
-
console.error(
|
|
561
|
+
console.error(`[mbkauthe] Login error:`, error);
|
|
562
562
|
return res.status(500).json(
|
|
563
563
|
createErrorResponse(500, ErrorCodes.INTERNAL_SERVER_ERROR)
|
|
564
564
|
);
|