mbkauthe 2.0.1 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +2 -2
- package/README.md +128 -7
- package/docs/api.md +48 -1
- package/docs/db.md +53 -15
- package/{env.md → docs/env.md} +76 -2
- package/index.js +9 -29
- package/lib/config.js +199 -0
- package/lib/main.js +246 -162
- package/lib/pool.js +3 -27
- package/lib/validateSessionAndRole.js +19 -131
- package/package.json +2 -2
- package/public/bg.webp +0 -0
- package/public/icon.ico +0 -0
- package/public/icon.svg +5 -0
- package/views/2fa.handlebars +21 -41
- package/views/Error/dError.handlebars +7 -35
- package/views/backgroundElements.handlebars +6 -0
- package/views/head.handlebars +14 -0
- package/views/header.handlebars +15 -0
- package/views/info.handlebars +8 -29
- package/views/loginmbkauthe.handlebars +5 -40
- package/views/sharedStyles.handlebars +112 -1
- package/views/versionInfo.handlebars +6 -0
- package/public/bg.avif +0 -0
package/lib/config.js
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import dotenv from "dotenv";
|
|
2
|
+
import crypto from "crypto";
|
|
3
|
+
import { createRequire } from "module";
|
|
4
|
+
|
|
5
|
+
dotenv.config();
|
|
6
|
+
|
|
7
|
+
// Comprehensive validation function
|
|
8
|
+
function validateConfiguration() {
|
|
9
|
+
const errors = [];
|
|
10
|
+
|
|
11
|
+
// Parse and validate mbkautheVar
|
|
12
|
+
let mbkautheVar;
|
|
13
|
+
try {
|
|
14
|
+
if (!process.env.mbkautheVar) {
|
|
15
|
+
errors.push("process.env.mbkautheVar is not defined");
|
|
16
|
+
throw new Error("Configuration validation failed");
|
|
17
|
+
}
|
|
18
|
+
mbkautheVar = JSON.parse(process.env.mbkautheVar);
|
|
19
|
+
} catch (error) {
|
|
20
|
+
if (error.message === "Configuration validation failed") {
|
|
21
|
+
throw new Error(`[mbkauthe] Configuration Error:\n - ${errors.join('\n - ')}`);
|
|
22
|
+
}
|
|
23
|
+
errors.push("Invalid JSON in process.env.mbkautheVar");
|
|
24
|
+
throw new Error(`[mbkauthe] Configuration Error:\n - ${errors.join('\n - ')}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!mbkautheVar || typeof mbkautheVar !== 'object') {
|
|
28
|
+
errors.push("mbkautheVar must be a valid object");
|
|
29
|
+
throw new Error(`[mbkauthe] Configuration Error:\n - ${errors.join('\n - ')}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Validate required keys
|
|
33
|
+
// COOKIE_EXPIRE_TIME is not required but if provided must be valid, COOKIE_EXPIRE_TIME by default is 2 days
|
|
34
|
+
// loginRedirectURL is not required but if provided must be valid, loginRedirectURL by default is /dashboard
|
|
35
|
+
const requiredKeys = ["APP_NAME", "Main_SECRET_TOKEN", "SESSION_SECRET_KEY", "IS_DEPLOYED", "LOGIN_DB",
|
|
36
|
+
"MBKAUTH_TWO_FA_ENABLE", "DOMAIN"];
|
|
37
|
+
|
|
38
|
+
requiredKeys.forEach(key => {
|
|
39
|
+
if (!mbkautheVar[key] || (typeof mbkautheVar[key] === 'string' && mbkautheVar[key].trim() === '')) {
|
|
40
|
+
errors.push(`mbkautheVar.${key} is required and cannot be empty`);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Validate IS_DEPLOYED value
|
|
45
|
+
if (mbkautheVar.IS_DEPLOYED && !['true', 'false', 'f'].includes(mbkautheVar.IS_DEPLOYED)) {
|
|
46
|
+
errors.push("mbkautheVar.IS_DEPLOYED must be either 'true' or 'false' or 'f'");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Validate MBKAUTH_TWO_FA_ENABLE value
|
|
50
|
+
if (mbkautheVar.MBKAUTH_TWO_FA_ENABLE && !['true', 'false', 'f'].includes(mbkautheVar.MBKAUTH_TWO_FA_ENABLE.toLowerCase())) {
|
|
51
|
+
errors.push("mbkautheVar.MBKAUTH_TWO_FA_ENABLE must be either 'true' or 'false' or 'f'");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Validate GITHUB_LOGIN_ENABLED value
|
|
55
|
+
if (mbkautheVar.GITHUB_LOGIN_ENABLED && !['true', 'false', 'f'].includes(mbkautheVar.GITHUB_LOGIN_ENABLED.toLowerCase())) {
|
|
56
|
+
errors.push("mbkautheVar.GITHUB_LOGIN_ENABLED must be either 'true' or 'false' or 'f'");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Validate GitHub login configuration
|
|
60
|
+
if (mbkautheVar.GITHUB_LOGIN_ENABLED === "true") {
|
|
61
|
+
if (!mbkautheVar.GITHUB_CLIENT_ID || mbkautheVar.GITHUB_CLIENT_ID.trim() === '') {
|
|
62
|
+
errors.push("mbkautheVar.GITHUB_CLIENT_ID is required when GITHUB_LOGIN_ENABLED is 'true'");
|
|
63
|
+
}
|
|
64
|
+
if (!mbkautheVar.GITHUB_CLIENT_SECRET || mbkautheVar.GITHUB_CLIENT_SECRET.trim() === '') {
|
|
65
|
+
errors.push("mbkautheVar.GITHUB_CLIENT_SECRET is required when GITHUB_LOGIN_ENABLED is 'true'");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Validate COOKIE_EXPIRE_TIME if provided
|
|
70
|
+
if (mbkautheVar.COOKIE_EXPIRE_TIME !== undefined) {
|
|
71
|
+
const expireTime = parseFloat(mbkautheVar.COOKIE_EXPIRE_TIME);
|
|
72
|
+
if (isNaN(expireTime) || expireTime <= 0) {
|
|
73
|
+
errors.push("mbkautheVar.COOKIE_EXPIRE_TIME must be a valid positive number");
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
// Set default value
|
|
77
|
+
mbkautheVar.COOKIE_EXPIRE_TIME = 2;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Validate DEVICE_TRUST_DURATION_DAYS if provided
|
|
81
|
+
if (mbkautheVar.DEVICE_TRUST_DURATION_DAYS !== undefined) {
|
|
82
|
+
const trustDuration = parseFloat(mbkautheVar.DEVICE_TRUST_DURATION_DAYS);
|
|
83
|
+
if (isNaN(trustDuration) || trustDuration <= 0) {
|
|
84
|
+
errors.push("mbkautheVar.DEVICE_TRUST_DURATION_DAYS must be a valid positive number");
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
// Set default value
|
|
88
|
+
mbkautheVar.DEVICE_TRUST_DURATION_DAYS = 7;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Validate LOGIN_DB connection string format
|
|
92
|
+
if (mbkautheVar.LOGIN_DB && !mbkautheVar.LOGIN_DB.startsWith('postgresql://') && !mbkautheVar.LOGIN_DB.startsWith('postgres://')) {
|
|
93
|
+
errors.push("mbkautheVar.LOGIN_DB must be a valid PostgreSQL connection string");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// If there are validation errors, throw them all at once
|
|
97
|
+
if (errors.length > 0) {
|
|
98
|
+
throw new Error(`[mbkauthe] Configuration Validation Failed:\n - ${errors.join('\n - ')}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
console.log('[mbkauthe] Configuration validation passed successfully');
|
|
102
|
+
return mbkautheVar;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Parse and validate mbkautheVar once
|
|
106
|
+
const mbkautheVar = validateConfiguration();
|
|
107
|
+
|
|
108
|
+
// Shared cookie options functions
|
|
109
|
+
const getCookieOptions = () => ({
|
|
110
|
+
maxAge: mbkautheVar.COOKIE_EXPIRE_TIME * 24 * 60 * 60 * 1000,
|
|
111
|
+
domain: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
|
|
112
|
+
secure: mbkautheVar.IS_DEPLOYED === 'true',
|
|
113
|
+
sameSite: 'lax',
|
|
114
|
+
path: '/',
|
|
115
|
+
httpOnly: true
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const getClearCookieOptions = () => ({
|
|
119
|
+
domain: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
|
|
120
|
+
secure: mbkautheVar.IS_DEPLOYED === 'true',
|
|
121
|
+
sameSite: 'lax',
|
|
122
|
+
path: '/',
|
|
123
|
+
httpOnly: true
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Cache cookie options for performance
|
|
127
|
+
const cachedCookieOptions = getCookieOptions();
|
|
128
|
+
const cachedClearCookieOptions = getClearCookieOptions();
|
|
129
|
+
|
|
130
|
+
// Constants for device trust feature
|
|
131
|
+
const DEVICE_TRUST_DURATION_DAYS = mbkautheVar.DEVICE_TRUST_DURATION_DAYS;
|
|
132
|
+
const DEVICE_TRUST_DURATION_MS = DEVICE_TRUST_DURATION_DAYS * 24 * 60 * 60 * 1000;
|
|
133
|
+
|
|
134
|
+
// Device token utilities
|
|
135
|
+
const generateDeviceToken = () => {
|
|
136
|
+
return crypto.randomBytes(32).toString('hex');
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const getDeviceTokenCookieOptions = () => ({
|
|
140
|
+
maxAge: DEVICE_TRUST_DURATION_MS,
|
|
141
|
+
domain: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
|
|
142
|
+
secure: mbkautheVar.IS_DEPLOYED === 'true',
|
|
143
|
+
sameSite: 'lax',
|
|
144
|
+
path: '/',
|
|
145
|
+
httpOnly: true
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Load package.json from mbkauthe package (not parent project)
|
|
149
|
+
const require = createRequire(import.meta.url);
|
|
150
|
+
let packageJson;
|
|
151
|
+
try {
|
|
152
|
+
// Try to load from mbkauthe package directory
|
|
153
|
+
packageJson = require("mbkauthe/package.json");
|
|
154
|
+
} catch {
|
|
155
|
+
// Fallback to relative path (for development/testing)
|
|
156
|
+
packageJson = require("../package.json");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Helper function to render error pages consistently
|
|
160
|
+
const renderError = (res, { code, error, message, page, pagename, details }) => {
|
|
161
|
+
const renderData = {
|
|
162
|
+
layout: false,
|
|
163
|
+
code,
|
|
164
|
+
error,
|
|
165
|
+
message,
|
|
166
|
+
page,
|
|
167
|
+
pagename,
|
|
168
|
+
app: mbkautheVar.APP_NAME,
|
|
169
|
+
version: packageJson.version
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// Add optional parameters if provided
|
|
173
|
+
if (details !== undefined) renderData.details = details;
|
|
174
|
+
|
|
175
|
+
return res.render("Error/dError.handlebars", renderData);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// Helper to clear all session cookies
|
|
179
|
+
const clearSessionCookies = (res) => {
|
|
180
|
+
res.clearCookie("mbkauthe.sid", cachedClearCookieOptions);
|
|
181
|
+
res.clearCookie("sessionId", cachedClearCookieOptions);
|
|
182
|
+
res.clearCookie("username", cachedClearCookieOptions);
|
|
183
|
+
res.clearCookie("device_token", cachedClearCookieOptions);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
export {
|
|
187
|
+
mbkautheVar,
|
|
188
|
+
getCookieOptions,
|
|
189
|
+
getClearCookieOptions,
|
|
190
|
+
cachedCookieOptions,
|
|
191
|
+
cachedClearCookieOptions,
|
|
192
|
+
renderError,
|
|
193
|
+
clearSessionCookies,
|
|
194
|
+
packageJson,
|
|
195
|
+
DEVICE_TRUST_DURATION_DAYS,
|
|
196
|
+
DEVICE_TRUST_DURATION_MS,
|
|
197
|
+
generateDeviceToken,
|
|
198
|
+
getDeviceTokenCookieOptions
|
|
199
|
+
};
|