mbkauthe 2.4.0 → 3.0.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,233 @@
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
+ declare module 'mbkauthe' {
9
+ // Configuration Types
10
+ export interface MBKAuthConfig {
11
+ APP_NAME: string;
12
+ SESSION_SECRET_KEY: string;
13
+ Main_SECRET_TOKEN: string;
14
+ IS_DEPLOYED: 'true' | 'false' | 'f';
15
+ DOMAIN: string;
16
+ LOGIN_DB: string;
17
+ MBKAUTH_TWO_FA_ENABLE: 'true' | 'false' | 'f';
18
+ COOKIE_EXPIRE_TIME?: number;
19
+ DEVICE_TRUST_DURATION_DAYS?: number;
20
+ GITHUB_LOGIN_ENABLED?: 'true' | 'false' | 'f';
21
+ GITHUB_CLIENT_ID?: string;
22
+ GITHUB_CLIENT_SECRET?: string;
23
+ loginRedirectURL?: string;
24
+ EncPass?: 'true' | 'false' | 'f';
25
+ }
26
+
27
+ // User Types
28
+ export type UserRole = 'SuperAdmin' | 'NormalUser' | 'Guest';
29
+
30
+ export interface SessionUser {
31
+ id: number;
32
+ username: string;
33
+ UserName: string;
34
+ role: UserRole;
35
+ Role: UserRole;
36
+ sessionId: string;
37
+ allowedApps?: string[];
38
+ }
39
+
40
+ export interface PreAuthUser {
41
+ id: number;
42
+ username: string;
43
+ UserName?: string;
44
+ role: UserRole;
45
+ Role?: UserRole;
46
+ loginMethod?: 'password' | 'github';
47
+ redirectUrl?: string | null;
48
+ }
49
+
50
+ // Database Types
51
+ export interface DBUser {
52
+ id: number;
53
+ UserName: string;
54
+ Password?: string;
55
+ PasswordEnc?: string;
56
+ Role: UserRole;
57
+ Active: boolean;
58
+ AllowedApps: string[];
59
+ SessionId?: string;
60
+ created_at?: Date;
61
+ updated_at?: Date;
62
+ last_login?: Date;
63
+ }
64
+
65
+ export interface TwoFARecord {
66
+ UserName: string;
67
+ TwoFAStatus: boolean;
68
+ TwoFASecret?: string;
69
+ }
70
+
71
+ export interface TrustedDevice {
72
+ id: number;
73
+ UserName: string;
74
+ DeviceToken: string;
75
+ DeviceName?: string;
76
+ UserAgent?: string;
77
+ IpAddress?: string;
78
+ CreatedAt: Date;
79
+ ExpiresAt: Date;
80
+ LastUsed: Date;
81
+ }
82
+
83
+ export interface GitHubUser {
84
+ id: number;
85
+ user_name: string;
86
+ github_id: string;
87
+ github_username: string;
88
+ access_token: string;
89
+ created_at: Date;
90
+ updated_at: Date;
91
+ }
92
+
93
+ // Request Extensions
94
+ declare global {
95
+ namespace Express {
96
+ interface Request {
97
+ user?: {
98
+ username: string;
99
+ UserName: string;
100
+ role: UserRole;
101
+ Role: UserRole;
102
+ };
103
+ }
104
+
105
+ interface Session {
106
+ user?: SessionUser;
107
+ preAuthUser?: PreAuthUser;
108
+ oauthRedirect?: string;
109
+ }
110
+ }
111
+ }
112
+
113
+ // API Response Types
114
+ export interface LoginResponse {
115
+ success: boolean;
116
+ message: string;
117
+ sessionId?: string;
118
+ twoFactorRequired?: boolean;
119
+ redirectUrl?: string;
120
+ errorCode?: number;
121
+ }
122
+
123
+ export interface LogoutResponse {
124
+ success: boolean;
125
+ message: string;
126
+ }
127
+
128
+ export interface TwoFAVerifyResponse {
129
+ success: boolean;
130
+ message: string;
131
+ sessionId?: string;
132
+ redirectUrl?: string;
133
+ }
134
+
135
+ export interface ErrorResponse {
136
+ success: false;
137
+ message: string;
138
+ errorCode?: number;
139
+ }
140
+
141
+ // Error Render Options
142
+ export interface ErrorRenderOptions {
143
+ layout?: boolean;
144
+ code: number | string;
145
+ error: string;
146
+ message: string;
147
+ page?: string;
148
+ pagename?: string;
149
+ details?: string;
150
+ app?: string;
151
+ version?: string;
152
+ }
153
+
154
+ // Middleware Types
155
+ export type AuthMiddleware = (
156
+ req: Request,
157
+ res: Response,
158
+ next: NextFunction
159
+ ) => void | Promise<void>;
160
+
161
+ // Middleware Functions
162
+ export function validateSession(
163
+ req: Request,
164
+ res: Response,
165
+ next: NextFunction
166
+ ): void | Promise<void>;
167
+
168
+ export function checkRolePermission(
169
+ requiredRole: UserRole | 'Any' | 'any',
170
+ notAllowed?: UserRole
171
+ ): AuthMiddleware;
172
+
173
+ export function validateSessionAndRole(
174
+ requiredRole: UserRole | 'Any' | 'any',
175
+ notAllowed?: UserRole
176
+ ): AuthMiddleware;
177
+
178
+ export function authenticate(token: string): AuthMiddleware;
179
+
180
+ export function authapi(requiredRole?: UserRole[]): AuthMiddleware;
181
+
182
+ // Utility Functions
183
+ export function renderError(
184
+ res: Response,
185
+ options: ErrorRenderOptions
186
+ ): Response;
187
+
188
+ export function getCookieOptions(): {
189
+ maxAge: number;
190
+ domain?: string;
191
+ secure: boolean;
192
+ sameSite: 'lax';
193
+ path: string;
194
+ httpOnly: boolean;
195
+ };
196
+
197
+ export function getClearCookieOptions(): {
198
+ domain?: string;
199
+ secure: boolean;
200
+ sameSite: 'lax';
201
+ path: string;
202
+ httpOnly: boolean;
203
+ };
204
+
205
+ export function generateDeviceToken(): string;
206
+
207
+ export function getDeviceTokenCookieOptions(): {
208
+ maxAge: number;
209
+ domain?: string;
210
+ secure: boolean;
211
+ sameSite: 'lax';
212
+ path: string;
213
+ httpOnly: boolean;
214
+ };
215
+
216
+ export function hashPassword(password: string, username: string): string;
217
+
218
+ export function clearSessionCookies(res: Response): void;
219
+
220
+ // Exports
221
+ export const dblogin: Pool;
222
+ export const mbkautheVar: MBKAuthConfig;
223
+ export const cachedCookieOptions: ReturnType<typeof getCookieOptions>;
224
+ export const cachedClearCookieOptions: ReturnType<typeof getClearCookieOptions>;
225
+ export const packageJson: { version: string; [key: string]: any };
226
+ export const appVersion: string;
227
+ export const DEVICE_TRUST_DURATION_DAYS: number;
228
+ export const DEVICE_TRUST_DURATION_MS: number;
229
+
230
+ // Default Export (Express Router)
231
+ const router: Router;
232
+ export default router;
233
+ }
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();
@@ -56,6 +55,11 @@ function validateConfiguration() {
56
55
  errors.push("mbkautheVar.GITHUB_LOGIN_ENABLED must be either 'true' or 'false' or 'f'");
57
56
  }
58
57
 
58
+ // Validate EncPass value if provided
59
+ if (mbkautheVar.EncPass && !['true', 'false', 'f'].includes(mbkautheVar.EncPass.toLowerCase())) {
60
+ errors.push("mbkautheVar.EncPass must be either 'true' or 'false' or 'f'");
61
+ }
62
+
59
63
  // Validate GitHub login configuration
60
64
  if (mbkautheVar.GITHUB_LOGIN_ENABLED === "true") {
61
65
  if (!mbkautheVar.GITHUB_CLIENT_ID || mbkautheVar.GITHUB_CLIENT_ID.trim() === '') {
@@ -103,47 +107,7 @@ function validateConfiguration() {
103
107
  }
104
108
 
105
109
  // 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
- });
110
+ export const mbkautheVar = validateConfiguration();
147
111
 
148
112
  // Load package.json from mbkauthe package (not parent project)
149
113
  const require = createRequire(import.meta.url);
@@ -153,48 +117,20 @@ try {
153
117
  packageJson = require("mbkauthe/package.json");
154
118
  } catch {
155
119
  // Fallback to relative path (for development/testing)
156
- packageJson = require("../package.json");
120
+ packageJson = require("../../package.json");
121
+ }
122
+
123
+ // Parent project version
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
+ }
157
134
  }
158
135
 
159
- // Helper function to render error pages consistently
160
- const renderError = (res, { code, error, message, page, pagename, details }) => {
161
- res.status(code);
162
- const renderData = {
163
- layout: false,
164
- code,
165
- error,
166
- message,
167
- page,
168
- pagename,
169
- app: mbkautheVar.APP_NAME,
170
- version: packageJson.version
171
- };
172
-
173
- // Add optional parameters if provided
174
- if (details !== undefined) renderData.details = details;
175
-
176
- return res.render("Error/dError.handlebars", renderData);
177
- };
178
-
179
- // Helper to clear all session cookies
180
- const clearSessionCookies = (res) => {
181
- res.clearCookie("mbkauthe.sid", cachedClearCookieOptions);
182
- res.clearCookie("sessionId", cachedClearCookieOptions);
183
- res.clearCookie("username", cachedClearCookieOptions);
184
- res.clearCookie("device_token", cachedClearCookieOptions);
185
- };
186
-
187
- export {
188
- mbkautheVar,
189
- getCookieOptions,
190
- getClearCookieOptions,
191
- cachedCookieOptions,
192
- cachedClearCookieOptions,
193
- renderError,
194
- clearSessionCookies,
195
- packageJson,
196
- DEVICE_TRUST_DURATION_DAYS,
197
- DEVICE_TRUST_DURATION_MS,
198
- generateDeviceToken,
199
- getDeviceTokenCookieOptions
200
- };
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,