mbkauthe 2.5.0 → 3.1.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/index.d.ts ADDED
@@ -0,0 +1,248 @@
1
+ // Type definitions for mbkauthe
2
+ // Project: https://github.com/MIbnEKhalid/mbkauthe
3
+ // Definitions by: Muhammad Bin Khalid <https://github.com/MIbnEKhalid>
4
+
5
+ import { Request, Response, NextFunction, Router } from 'express';
6
+ import { Pool } from 'pg';
7
+
8
+ // Global augmentations must be at the top level, outside any declare module blocks
9
+ declare global {
10
+ namespace Express {
11
+ interface Request {
12
+ user?: {
13
+ username: string;
14
+ UserName: string;
15
+ role: 'SuperAdmin' | 'NormalUser' | 'Guest';
16
+ Role: 'SuperAdmin' | 'NormalUser' | 'Guest';
17
+ };
18
+ userRole?: 'SuperAdmin' | 'NormalUser' | 'Guest';
19
+ }
20
+
21
+ interface Session {
22
+ user?: {
23
+ id: number;
24
+ username: string;
25
+ UserName: string;
26
+ role: 'SuperAdmin' | 'NormalUser' | 'Guest';
27
+ Role: 'SuperAdmin' | 'NormalUser' | 'Guest';
28
+ sessionId: string;
29
+ allowedApps?: string[];
30
+ };
31
+ preAuthUser?: {
32
+ id: number;
33
+ username: string;
34
+ UserName?: string;
35
+ role: 'SuperAdmin' | 'NormalUser' | 'Guest';
36
+ Role?: 'SuperAdmin' | 'NormalUser' | 'Guest';
37
+ loginMethod?: 'password' | 'github';
38
+ redirectUrl?: string | null;
39
+ };
40
+ oauthRedirect?: string;
41
+ }
42
+ }
43
+ }
44
+
45
+ declare module 'mbkauthe' {
46
+ // Configuration Types
47
+ export interface MBKAuthConfig {
48
+ APP_NAME: string;
49
+ SESSION_SECRET_KEY: string;
50
+ Main_SECRET_TOKEN: string;
51
+ IS_DEPLOYED: 'true' | 'false' | 'f';
52
+ DOMAIN: string;
53
+ LOGIN_DB: string;
54
+ MBKAUTH_TWO_FA_ENABLE: 'true' | 'false' | 'f';
55
+ COOKIE_EXPIRE_TIME?: number;
56
+ DEVICE_TRUST_DURATION_DAYS?: number;
57
+ GITHUB_LOGIN_ENABLED?: 'true' | 'false' | 'f';
58
+ GITHUB_CLIENT_ID?: string;
59
+ GITHUB_CLIENT_SECRET?: string;
60
+ loginRedirectURL?: string;
61
+ EncPass?: 'true' | 'false' | 'f';
62
+ }
63
+
64
+ // User Types
65
+ export type UserRole = 'SuperAdmin' | 'NormalUser' | 'Guest';
66
+
67
+ export interface SessionUser {
68
+ id: number;
69
+ username: string;
70
+ UserName: string;
71
+ role: UserRole;
72
+ Role: UserRole;
73
+ sessionId: string;
74
+ allowedApps?: string[];
75
+ }
76
+
77
+ export interface PreAuthUser {
78
+ id: number;
79
+ username: string;
80
+ UserName?: string;
81
+ role: UserRole;
82
+ Role?: UserRole;
83
+ loginMethod?: 'password' | 'github';
84
+ redirectUrl?: string | null;
85
+ }
86
+
87
+ // Database Types
88
+ export interface DBUser {
89
+ id: number;
90
+ UserName: string;
91
+ Password?: string;
92
+ PasswordEnc?: string;
93
+ Role: UserRole;
94
+ Active: boolean;
95
+ AllowedApps: string[];
96
+ SessionId?: string;
97
+ created_at?: Date;
98
+ updated_at?: Date;
99
+ last_login?: Date;
100
+ }
101
+
102
+ export interface TwoFARecord {
103
+ UserName: string;
104
+ TwoFAStatus: boolean;
105
+ TwoFASecret?: string;
106
+ }
107
+
108
+ export interface TrustedDevice {
109
+ id: number;
110
+ UserName: string;
111
+ DeviceToken: string;
112
+ DeviceName?: string;
113
+ UserAgent?: string;
114
+ IpAddress?: string;
115
+ CreatedAt: Date;
116
+ ExpiresAt: Date;
117
+ LastUsed: Date;
118
+ }
119
+
120
+ export interface GitHubUser {
121
+ id: number;
122
+ user_name: string;
123
+ github_id: string;
124
+ github_username: string;
125
+ access_token: string;
126
+ created_at: Date;
127
+ updated_at: Date;
128
+ }
129
+
130
+ // API Response Types
131
+ export interface LoginResponse {
132
+ success: boolean;
133
+ message: string;
134
+ sessionId?: string;
135
+ twoFactorRequired?: boolean;
136
+ redirectUrl?: string;
137
+ errorCode?: number;
138
+ }
139
+
140
+ export interface LogoutResponse {
141
+ success: boolean;
142
+ message: string;
143
+ }
144
+
145
+ export interface TwoFAVerifyResponse {
146
+ success: boolean;
147
+ message: string;
148
+ sessionId?: string;
149
+ redirectUrl?: string;
150
+ }
151
+
152
+ export interface ErrorResponse {
153
+ success: false;
154
+ message: string;
155
+ errorCode?: number;
156
+ }
157
+
158
+ // Error Render Options
159
+ export interface ErrorRenderOptions {
160
+ layout?: boolean;
161
+ code: number | string;
162
+ error: string;
163
+ message: string;
164
+ page?: string;
165
+ pagename?: string;
166
+ details?: string;
167
+ app?: string;
168
+ version?: string;
169
+ }
170
+
171
+ // Middleware Types
172
+ export type AuthMiddleware = (
173
+ req: Request,
174
+ res: Response,
175
+ next: NextFunction
176
+ ) => void | Promise<void>;
177
+
178
+ // Middleware Functions
179
+ export function validateSession(
180
+ req: Request,
181
+ res: Response,
182
+ next: NextFunction
183
+ ): void | Promise<void>;
184
+
185
+ export function checkRolePermission(
186
+ requiredRole: UserRole | 'Any' | 'any',
187
+ notAllowed?: UserRole
188
+ ): AuthMiddleware;
189
+
190
+ export function validateSessionAndRole(
191
+ requiredRole: UserRole | 'Any' | 'any',
192
+ notAllowed?: UserRole
193
+ ): AuthMiddleware;
194
+
195
+ export function authenticate(token: string): AuthMiddleware;
196
+
197
+ // Utility Functions
198
+ export function renderError(
199
+ res: Response,
200
+ options: ErrorRenderOptions
201
+ ): Response;
202
+
203
+ export function getCookieOptions(): {
204
+ maxAge: number;
205
+ domain?: string;
206
+ secure: boolean;
207
+ sameSite: 'lax';
208
+ path: string;
209
+ httpOnly: boolean;
210
+ };
211
+
212
+ export function getClearCookieOptions(): {
213
+ domain?: string;
214
+ secure: boolean;
215
+ sameSite: 'lax';
216
+ path: string;
217
+ httpOnly: boolean;
218
+ };
219
+
220
+ export function generateDeviceToken(): string;
221
+
222
+ export function getDeviceTokenCookieOptions(): {
223
+ maxAge: number;
224
+ domain?: string;
225
+ secure: boolean;
226
+ sameSite: 'lax';
227
+ path: string;
228
+ httpOnly: boolean;
229
+ };
230
+
231
+ export function hashPassword(password: string, username: string): string;
232
+
233
+ export function clearSessionCookies(res: Response): void;
234
+
235
+ // Exports
236
+ export const dblogin: Pool;
237
+ export const mbkautheVar: MBKAuthConfig;
238
+ export const cachedCookieOptions: ReturnType<typeof getCookieOptions>;
239
+ export const cachedClearCookieOptions: ReturnType<typeof getClearCookieOptions>;
240
+ export const packageJson: { version: string; [key: string]: any };
241
+ export const appVersion: string;
242
+ export const DEVICE_TRUST_DURATION_DAYS: number;
243
+ export const DEVICE_TRUST_DURATION_MS: number;
244
+
245
+ // Default Export (Express Router)
246
+ const router: Router;
247
+ export default router;
248
+ }
package/index.js CHANGED
@@ -1,34 +1,16 @@
1
- import express from "express"; // Add this line
1
+ import express from "express";
2
2
  import router from "./lib/main.js";
3
3
  import { getLatestVersion } from "./lib/main.js";
4
4
  import { engine } from "express-handlebars";
5
5
  import path from "path";
6
6
  import { fileURLToPath } from "url";
7
- import { renderError, packageJson } from "./lib/config.js";
7
+ import { packageJson } from "./lib/config/index.js";
8
+ import { renderError } from "./lib/utils/response.js";
8
9
 
9
10
  const __filename = fileURLToPath(import.meta.url);
10
11
  const __dirname = path.dirname(__filename);
11
12
 
12
13
  const app = express();
13
- if (process.env.test === "dev") {
14
- console.log("[mbkauthe] Dev mode is enabled. Starting server in dev mode.");
15
- const port = 5555;
16
- app.use(router);
17
- app.use((req, res) => {
18
- console.log(`Path not found: ${req.method} ${req.url}`);
19
- return renderError(res, {
20
- layout: false,
21
- code: 404,
22
- error: "Not Found",
23
- message: "The requested page was not found.",
24
- pagename: "Home",
25
- page: "/mbkauthe/login",
26
- });
27
- });
28
- app.listen(port, () => {
29
- console.log(`[mbkauthe] Server running on http://localhost:${port}`);
30
- });
31
- }
32
14
 
33
15
  app.set("views", [
34
16
  path.join(__dirname, "views"),
@@ -44,11 +26,6 @@ app.engine("handlebars", engine({
44
26
  path.join(__dirname, "node_modules/mbkauthe/views/Error"),
45
27
  ],
46
28
  helpers: {
47
- concat: function (...args) {
48
- // Remove the handlebars options object from args
49
- args.pop();
50
- return args.join('');
51
- },
52
29
  eq: function (a, b) {
53
30
  return a === b;
54
31
  },
@@ -74,12 +51,46 @@ app.engine("handlebars", engine({
74
51
 
75
52
  app.set("view engine", "handlebars");
76
53
 
77
- const latestVersion = await getLatestVersion();
78
- if (latestVersion !== packageJson.version) {
79
- console.warn(`[mbkauthe] Warning: The current version (${packageJson.version}) is not the latest version (${latestVersion}). Please update mbkauthe.`);
54
+ // Version check with error handling
55
+ async function checkVersion() {
56
+ try {
57
+ const latestVersion = await getLatestVersion();
58
+ if (latestVersion && latestVersion !== packageJson.version) {
59
+ console.warn(`[mbkauthe] Current version (${packageJson.version}) is outdated. Latest version: ${latestVersion}. Consider updating mbkauthe.`);
60
+ } else if (latestVersion) {
61
+ console.info(`[mbkauthe] Running latest version (${packageJson.version}).`);
62
+ }
63
+ } catch (error) {
64
+ console.warn(`[mbkauthe] Failed to check for updates: ${error.message}`);
65
+ }
66
+ }
67
+
68
+ if (process.env.test === "dev") {
69
+ console.log("[mbkauthe] Dev mode is enabled. Starting server in dev mode.");
70
+ const port = 5555;
71
+ app.use(router);
72
+ app.use((req, res) => {
73
+ console.log(`[mbkauthe] Path not found: ${req.method} ${req.url}`);
74
+ return renderError(res, {
75
+ layout: false,
76
+ code: 404,
77
+ error: "Not Found",
78
+ message: "The requested page was not found.",
79
+ pagename: "Home",
80
+ page: "/mbkauthe/login",
81
+ });
82
+ });
83
+ app.listen(port, () => {
84
+ console.log(`[mbkauthe] Server running on http://localhost:${port}`);
85
+ });
86
+ }
87
+
88
+ if (process.env.test !== "dev") {
89
+ await checkVersion();
80
90
  }
81
91
 
82
- export { validateSession, checkRolePermission, validateSessionAndRole, authenticate } from "./lib/validateSessionAndRole.js";
83
- export { renderError } from "./lib/config.js";
84
- export { dblogin } from "./lib/pool.js";
92
+ export { validateSession, checkRolePermission, validateSessionAndRole, authenticate } from "./lib/middleware/auth.js";
93
+ export { renderError } from "./lib/utils/response.js";
94
+ export { dblogin } from "./lib/database/pool.js";
95
+ export { ErrorCodes, ErrorMessages, getErrorByCode, createErrorResponse, logError } from "./lib/utils/errors.js";
85
96
  export default router;
@@ -0,0 +1,52 @@
1
+ import crypto from "crypto";
2
+ import { mbkautheVar } from "./index.js";
3
+
4
+ // Shared cookie options functions
5
+ const getCookieOptions = () => ({
6
+ maxAge: mbkautheVar.COOKIE_EXPIRE_TIME * 24 * 60 * 60 * 1000,
7
+ domain: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
8
+ secure: mbkautheVar.IS_DEPLOYED === 'true',
9
+ sameSite: 'lax',
10
+ path: '/',
11
+ httpOnly: true
12
+ });
13
+
14
+ const getClearCookieOptions = () => ({
15
+ domain: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
16
+ secure: mbkautheVar.IS_DEPLOYED === 'true',
17
+ sameSite: 'lax',
18
+ path: '/',
19
+ httpOnly: true
20
+ });
21
+
22
+ // Cache cookie options for performance
23
+ export const cachedCookieOptions = getCookieOptions();
24
+ export const cachedClearCookieOptions = getClearCookieOptions();
25
+
26
+ // Constants for device trust feature
27
+ export const DEVICE_TRUST_DURATION_DAYS = mbkautheVar.DEVICE_TRUST_DURATION_DAYS;
28
+ export const DEVICE_TRUST_DURATION_MS = DEVICE_TRUST_DURATION_DAYS * 24 * 60 * 60 * 1000;
29
+
30
+ // Device token utilities
31
+ export const generateDeviceToken = () => {
32
+ return crypto.randomBytes(32).toString('hex');
33
+ };
34
+
35
+ export const getDeviceTokenCookieOptions = () => ({
36
+ maxAge: DEVICE_TRUST_DURATION_MS,
37
+ domain: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
38
+ secure: mbkautheVar.IS_DEPLOYED === 'true',
39
+ sameSite: 'lax',
40
+ path: '/',
41
+ httpOnly: true
42
+ });
43
+
44
+ // Helper to clear all session cookies
45
+ export const clearSessionCookies = (res) => {
46
+ res.clearCookie("mbkauthe.sid", cachedClearCookieOptions);
47
+ res.clearCookie("sessionId", cachedClearCookieOptions);
48
+ res.clearCookie("username", cachedClearCookieOptions);
49
+ res.clearCookie("device_token", cachedClearCookieOptions);
50
+ };
51
+
52
+ export { getCookieOptions, getClearCookieOptions };
@@ -1,5 +1,4 @@
1
1
  import dotenv from "dotenv";
2
- import crypto from "crypto";
3
2
  import { createRequire } from "module";
4
3
 
5
4
  dotenv.config();
@@ -108,47 +107,7 @@ function validateConfiguration() {
108
107
  }
109
108
 
110
109
  // Parse and validate mbkautheVar once
111
- const mbkautheVar = validateConfiguration();
112
-
113
- // Shared cookie options functions
114
- const getCookieOptions = () => ({
115
- maxAge: mbkautheVar.COOKIE_EXPIRE_TIME * 24 * 60 * 60 * 1000,
116
- domain: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
117
- secure: mbkautheVar.IS_DEPLOYED === 'true',
118
- sameSite: 'lax',
119
- path: '/',
120
- httpOnly: true
121
- });
122
-
123
- const getClearCookieOptions = () => ({
124
- domain: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
125
- secure: mbkautheVar.IS_DEPLOYED === 'true',
126
- sameSite: 'lax',
127
- path: '/',
128
- httpOnly: true
129
- });
130
-
131
- // Cache cookie options for performance
132
- const cachedCookieOptions = getCookieOptions();
133
- const cachedClearCookieOptions = getClearCookieOptions();
134
-
135
- // Constants for device trust feature
136
- const DEVICE_TRUST_DURATION_DAYS = mbkautheVar.DEVICE_TRUST_DURATION_DAYS;
137
- const DEVICE_TRUST_DURATION_MS = DEVICE_TRUST_DURATION_DAYS * 24 * 60 * 60 * 1000;
138
-
139
- // Device token utilities
140
- const generateDeviceToken = () => {
141
- return crypto.randomBytes(32).toString('hex');
142
- };
143
-
144
- const getDeviceTokenCookieOptions = () => ({
145
- maxAge: DEVICE_TRUST_DURATION_MS,
146
- domain: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined,
147
- secure: mbkautheVar.IS_DEPLOYED === 'true',
148
- sameSite: 'lax',
149
- path: '/',
150
- httpOnly: true
151
- });
110
+ export const mbkautheVar = validateConfiguration();
152
111
 
153
112
  // Load package.json from mbkauthe package (not parent project)
154
113
  const require = createRequire(import.meta.url);
@@ -158,59 +117,20 @@ try {
158
117
  packageJson = require("mbkauthe/package.json");
159
118
  } catch {
160
119
  // Fallback to relative path (for development/testing)
161
- packageJson = require("../package.json");
120
+ packageJson = require("../../package.json");
162
121
  }
163
122
 
164
123
  // Parent project version
165
- const appVersion = require("../package.json") ?.version || "unknown";
166
-
167
- // Helper function to render error pages consistently
168
- const renderError = (res, { code, error, message, page, pagename, details }) => {
169
- res.status(code);
170
- const renderData = {
171
- layout: false,
172
- code,
173
- error,
174
- message,
175
- page,
176
- pagename,
177
- app: mbkautheVar.APP_NAME,
178
- version: packageJson.version
179
- };
180
-
181
- // Add optional parameters if provided
182
- if (details !== undefined) renderData.details = details;
183
-
184
- return res.render("Error/dError.handlebars", renderData);
185
- };
186
-
187
- // Helper to clear all session cookies
188
- const clearSessionCookies = (res) => {
189
- res.clearCookie("mbkauthe.sid", cachedClearCookieOptions);
190
- res.clearCookie("sessionId", cachedClearCookieOptions);
191
- res.clearCookie("username", cachedClearCookieOptions);
192
- res.clearCookie("device_token", cachedClearCookieOptions);
193
- };
194
-
195
- const hashPassword = (password, username) => {
196
- const salt = username;
197
- // 128 characters returned
198
- return crypto.pbkdf2Sync(password, salt, 100000, 64, 'sha512').toString('hex');
199
- };
200
-
201
- export {
202
- mbkautheVar,
203
- getCookieOptions,
204
- getClearCookieOptions,
205
- cachedCookieOptions,
206
- cachedClearCookieOptions,
207
- renderError,
208
- clearSessionCookies,
209
- packageJson,
210
- appVersion,
211
- DEVICE_TRUST_DURATION_DAYS,
212
- DEVICE_TRUST_DURATION_MS,
213
- generateDeviceToken,
214
- getDeviceTokenCookieOptions,
215
- hashPassword
216
- };
124
+ let appVersion;
125
+ try {
126
+ appVersion = require("../../../../package.json")?.version || "unknown";
127
+ } catch {
128
+ // Fallback if path doesn't work
129
+ try {
130
+ appVersion = require(process.cwd() + "/package.json")?.version || "unknown";
131
+ } catch {
132
+ appVersion = "unknown";
133
+ }
134
+ }
135
+
136
+ export { packageJson, appVersion };
@@ -0,0 +1,8 @@
1
+ import crypto from "crypto";
2
+
3
+ // Password hashing using PBKDF2
4
+ export const hashPassword = (password, username) => {
5
+ const salt = username;
6
+ // 128 characters returned
7
+ return crypto.pbkdf2Sync(password, salt, 100000, 64, 'sha512').toString('hex');
8
+ };
@@ -1,6 +1,6 @@
1
1
  import pkg from "pg";
2
2
  const { Pool } = pkg;
3
- import { mbkautheVar } from "./config.js";
3
+ import { mbkautheVar } from "../config/index.js";
4
4
 
5
5
  const poolConfig = {
6
6
  connectionString: mbkautheVar.LOGIN_DB,