mbkauthe 1.0.4 → 1.0.6
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 +11 -0
- package/.github/workflows/publish.yml +7 -0
- package/README.md +123 -1
- package/docs/db.md +90 -0
- package/env.md +55 -0
- package/index.js +19 -17
- package/lib/main.js +69 -65
- package/lib/pool.js +19 -2
- package/lib/validateSessionAndRole.js +15 -1
- package/package.json +1 -2
- package/lib/auth.js +0 -13
package/.env.example
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
mbkautheVar='{
|
|
2
|
+
"RECAPTCHA_SECRET_KEY": "your-recaptcha-secret-key",
|
|
3
|
+
"SESSION_SECRET_KEY": "your-session-secret-key",
|
|
4
|
+
"IS_DEPLOYED": "true",
|
|
5
|
+
"LOGIN_DB": "postgres://username:password@host:port/database",
|
|
6
|
+
"MBKAUTH_TWO_FA_ENABLE": "false",
|
|
7
|
+
"COOKIE_EXPIRE_TIME": 2,
|
|
8
|
+
"DOMAIN": "yourdomain.com"
|
|
9
|
+
}'
|
|
10
|
+
|
|
11
|
+
# See env.md for more details
|
package/README.md
CHANGED
|
@@ -1 +1,123 @@
|
|
|
1
|
-
# mbkauthe
|
|
1
|
+
# mbkauthe
|
|
2
|
+
|
|
3
|
+
[](https://github.com/MIbnEKhalid/mbkauthe/actions/workflows/publish.yml) [](https://github.com/MIbnEKhalid/mbkauthe/actions/workflows/codeql.yml)
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Introduction](#mbkauth)
|
|
8
|
+
- [Features](#features)
|
|
9
|
+
- [Installation](#installation)
|
|
10
|
+
- [Usage](#usage)
|
|
11
|
+
- [Basic Setup](#basic-setup)
|
|
12
|
+
- [API Endpoints](#api-endpoints)
|
|
13
|
+
- [Login](#login)
|
|
14
|
+
- [Logout](#logout)
|
|
15
|
+
- [Terminate All Sessions](#terminate-all-sessions)
|
|
16
|
+
- [Database Structure](#database-structure)
|
|
17
|
+
- [License](#license)
|
|
18
|
+
- [Contact & Support](#contact--support)
|
|
19
|
+
|
|
20
|
+
`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.
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
- **Session Management**: Secure session handling using `express-session` and `connect-pg-simple`.
|
|
25
|
+
- **Role-Based Access Control**: Validate user roles and permissions with ease.
|
|
26
|
+
- **Two-Factor Authentication (2FA)**: Optional 2FA support for enhanced security.
|
|
27
|
+
- **reCAPTCHA Integration**: Protect login endpoints with Google reCAPTCHA.
|
|
28
|
+
- **Cookie Management**: Configurable cookie expiration and domain settings.
|
|
29
|
+
- **PostgreSQL Integration**: Uses a connection pool for efficient database interactions.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
Install the package via npm:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install mbkauthe
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
### Basic Setup
|
|
41
|
+
1. Import and configure the router in your Express application:
|
|
42
|
+
```javascript
|
|
43
|
+
import express from "express";
|
|
44
|
+
import mbkAuthRouter from "mbkauthe";
|
|
45
|
+
|
|
46
|
+
const app = express();
|
|
47
|
+
|
|
48
|
+
app.use(mbkAuthRouter);
|
|
49
|
+
|
|
50
|
+
app.listen(3000, () => {
|
|
51
|
+
console.log("Server is running on port 3000");
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
2. Ensure your ``.env` file is properly configured. Refer to the [Configuration Guide(env.md)](env.md) for details.
|
|
55
|
+
|
|
56
|
+
Example `.env` file:
|
|
57
|
+
```code
|
|
58
|
+
mbkautheVar='{
|
|
59
|
+
"RECAPTCHA_SECRET_KEY": "your-recaptcha-secret-key",
|
|
60
|
+
"SESSION_SECRET_KEY": "your-session-secret-key",
|
|
61
|
+
"IS_DEPLOYED": "true",
|
|
62
|
+
"LOGIN_DB": "postgres://username:password@host:port/database",
|
|
63
|
+
"MBKAUTH_TWO_FA_ENABLE": "false",
|
|
64
|
+
"COOKIE_EXPIRE_TIME": 2,
|
|
65
|
+
"DOMAIN": "yourdomain.com"
|
|
66
|
+
}'
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## API Endpoints
|
|
70
|
+
|
|
71
|
+
### Login
|
|
72
|
+
|
|
73
|
+
**POST** `/mbkauth/api/login`
|
|
74
|
+
- Request Body:
|
|
75
|
+
- `username`: User's username.
|
|
76
|
+
- `password`: User's password.
|
|
77
|
+
- `token`: (Optional) 2FA token.
|
|
78
|
+
- `recaptcha`: reCAPTCHA response.
|
|
79
|
+
|
|
80
|
+
- Response:
|
|
81
|
+
- `200`: Login successful.
|
|
82
|
+
- `400`: Missing or invalid input.
|
|
83
|
+
- `401`: Unauthorized (e.g., invalid credentials or 2FA token).
|
|
84
|
+
- `500`: Internal server error.
|
|
85
|
+
|
|
86
|
+
### Logout
|
|
87
|
+
|
|
88
|
+
**POST** `/mbkauth/api/logout`
|
|
89
|
+
- Response:
|
|
90
|
+
- `200`: Login successful.
|
|
91
|
+
- `400`: User not logged in.
|
|
92
|
+
- `500`: Internal server error.
|
|
93
|
+
|
|
94
|
+
### Terminate All Sessions
|
|
95
|
+
|
|
96
|
+
**POST** `/mbkauth/api/terminateAllSessions`
|
|
97
|
+
- Authentication: Requires a valid `Main_SECRET_TOKEN` in the `Authorization` header.
|
|
98
|
+
- Response:
|
|
99
|
+
- `200`: All sessions terminated successfully.
|
|
100
|
+
- `500`: Internal server error.
|
|
101
|
+
-
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
## Database Structure
|
|
105
|
+
|
|
106
|
+
This project utilizes three primary tables:
|
|
107
|
+
|
|
108
|
+
1. **User**: Stores the main user information.
|
|
109
|
+
2. **sess**: Contains session-related data for users.
|
|
110
|
+
3. **TwoFA**: Saves the Two-Factor Authentication (2FA) secrets for users.
|
|
111
|
+
|
|
112
|
+
For detailed information about table columns, schema, and queries to create these tables, refer to the [Database Guide (docs/db.md)](docs/db.md).
|
|
113
|
+
|
|
114
|
+
## License
|
|
115
|
+
This project is licensed under the `Mozilla Public License 2.0`. See the [LICENSE](./LICENSE) file for details.
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
## Contact & Support
|
|
120
|
+
|
|
121
|
+
For questions or contributions, please contact Muhammad Bin Khalid at [mbktechstudio.com/Support](https://mbktechstudio.com/Support/), [support@mbktechstudio.com](mailto:support@mbktechstudio.com) or [chmuhammadbinkhalid28.com](mailto:chmuhammadbinkhalid28.com).
|
|
122
|
+
|
|
123
|
+
**Developed by [Muhammad Bin Khalid](https://github.com/MIbnEKhalid)**
|
package/docs/db.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
## Database structure
|
|
2
|
+
|
|
3
|
+
[<- Back](README.md)
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
1. [Users Table](#users-table)
|
|
8
|
+
2. [Session Table](#session-table)
|
|
9
|
+
3. [Two-Factor Authentication Table](#two-factor-authentication-table)
|
|
10
|
+
4. [Query to Add a User](#query-to-add-a-user)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Users Table
|
|
14
|
+
|
|
15
|
+
- **Columns:**
|
|
16
|
+
|
|
17
|
+
- `id` (INTEGER, auto-increment, primary key): Unique identifier for each user.
|
|
18
|
+
- `UserName` (TEXT): The username of the user.
|
|
19
|
+
- `Password` (TEXT): The hashed password of the user.
|
|
20
|
+
- `Role` (ENUM): The role of the user. Possible values: `SuperAdmin`, `NormalUser`, `Guest`.
|
|
21
|
+
- `Active` (BOOLEAN): Indicates whether the user account is active.
|
|
22
|
+
- `HaveMailAccount` (BOOLEAN)(optional): Indicates if the user has a linked mail account.
|
|
23
|
+
- `SessionId` (TEXT): The session ID associated with the user.
|
|
24
|
+
- `GuestRole` (JSONB): Stores additional guest-specific role information in binary JSON format.
|
|
25
|
+
|
|
26
|
+
- **Schema:**
|
|
27
|
+
```sql
|
|
28
|
+
CREATE TABLE "Users" (
|
|
29
|
+
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
|
30
|
+
"UserName" TEXT NOT NULL,
|
|
31
|
+
"Password" TEXT NOT NULL,
|
|
32
|
+
"Role" TEXT CHECK("Role" IN ('SuperAdmin', 'NormalUser', 'Guest')) NOT NULL DEFAULT 'NormalUser'::text,
|
|
33
|
+
"Active" BOOLEAN NOT NULL DEFAULT true,
|
|
34
|
+
"HaveMailAccount" BOOLEAN NOT NULL DEFAULT false,
|
|
35
|
+
"SessionId" TEXT,
|
|
36
|
+
"GuestRole" JSONB DEFAULT '{"allowPages": [""], "NotallowPages": [""]}'::jsonb
|
|
37
|
+
);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Session Table
|
|
41
|
+
|
|
42
|
+
- **Columns:**
|
|
43
|
+
|
|
44
|
+
- `sid` (VARCHAR, primary key): Unique session identifier.
|
|
45
|
+
- `sess` (JSON): Session data stored in JSON format.
|
|
46
|
+
- `expire` (TIMESTAMP): Expiration timestamp for the session.
|
|
47
|
+
|
|
48
|
+
- **Schema:**
|
|
49
|
+
```sql
|
|
50
|
+
CREATE TABLE session (
|
|
51
|
+
sid VARCHAR PRIMARY KEY,
|
|
52
|
+
sess JSON NOT NULL,
|
|
53
|
+
expire TIMESTAMP NOT NULL
|
|
54
|
+
);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Two-Factor Authentication Table
|
|
58
|
+
|
|
59
|
+
- **Columns:**
|
|
60
|
+
|
|
61
|
+
- `UserName` (TEXT): The username of the user.
|
|
62
|
+
- `TwoFAStatus` (TEXT): The status of two-factor authentication (e.g., enabled, disabled).
|
|
63
|
+
- `TwoFASecret` (TEXT): The secret key used for two-factor authentication.
|
|
64
|
+
|
|
65
|
+
- **Schema:**
|
|
66
|
+
```sql
|
|
67
|
+
CREATE TABLE "TwoFA" (
|
|
68
|
+
"UserName" TEXT NOT NULL PRIMARY KEY,
|
|
69
|
+
"TwoFAStatus" TEXT NOT NULL DEFAULT false,
|
|
70
|
+
"TwoFASecret" TEXT NOT NULL
|
|
71
|
+
);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Query to Add a User
|
|
75
|
+
|
|
76
|
+
To add new users to the `Users` table, use the following SQL queries:
|
|
77
|
+
|
|
78
|
+
```sql
|
|
79
|
+
INSERT INTO "Users" ("UserName", "Password", "Role", "Active", "HaveMailAccount", "SessionId", "GuestRole")
|
|
80
|
+
VALUES ('support', '12345678', 'SuperAdmin', true, false, NULL, '{"allowPages": [""], "NotallowPages": [""]}'::jsonb);
|
|
81
|
+
|
|
82
|
+
INSERT INTO "Users" ("UserName", "Password", "Role", "Active", "HaveMailAccount", "SessionId", "GuestRole")
|
|
83
|
+
VALUES ('test', '12345678', 'NormalUser', true, false, NULL, '{"allowPages": [""], "NotallowPages": [""]}'::jsonb);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
- Replace `support` and `test` with the desired usernames.
|
|
87
|
+
- Replace `12345678` with the actual passwords.
|
|
88
|
+
- Adjust the `Role` values as needed (`SuperAdmin`, `NormalUser`, or `Guest`).
|
|
89
|
+
- Modify the `Active` and `HaveMailAccount` values as required.
|
|
90
|
+
- Update the `GuestRole` JSON object if specific permissions are required(this functionality is under construction).
|
package/env.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Configuration Guide
|
|
2
|
+
|
|
3
|
+
[<- Back](README.md)
|
|
4
|
+
|
|
5
|
+
## reCAPTCHA Settings
|
|
6
|
+
```properties
|
|
7
|
+
RECAPTCHA_SECRET_KEY=123
|
|
8
|
+
```
|
|
9
|
+
> Note: Obtain your secret key from Google reCAPTCHA Admin Console.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## Session Settings
|
|
13
|
+
```properties
|
|
14
|
+
SESSION_SECRET_KEY=123
|
|
15
|
+
IS_DEPLOYED=true
|
|
16
|
+
DOMAIN=mbktechstudio.com
|
|
17
|
+
```
|
|
18
|
+
> **SESSION_SECRET_KEY**: Generate a secure key using [Generate Secret](https://generate-secret.vercel.app/32).
|
|
19
|
+
|
|
20
|
+
> **IS_DEPLOYED**:
|
|
21
|
+
|
|
22
|
+
> - `true`: For deployed environments. Sessions are shared across all subDOMAINs of `.mbktechstudio.com` or the DOMAIN specified in `DOMAIN`.
|
|
23
|
+
|
|
24
|
+
> - `false`: For local development.
|
|
25
|
+
|
|
26
|
+
> - Important: If set to `true`, login functionality will not work on `localhost`. Use a valid DOMAIN for proper operation.
|
|
27
|
+
|
|
28
|
+
> **DOMAIN**:
|
|
29
|
+
|
|
30
|
+
> - Set `DOMAIN` to your DOMAIN
|
|
31
|
+
|
|
32
|
+
> - If you don't have a DOMAIN, set `IS_DEPLOYED=false`.
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
## Database Settings
|
|
36
|
+
|
|
37
|
+
```properties
|
|
38
|
+
LOGIN_DB=postgresql://username:password@server.DOMAIN/db_name
|
|
39
|
+
```
|
|
40
|
+
> Replace the placeholder with your PostgreSQL connection string.
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
## Two-Factor Authentication (2FA)
|
|
44
|
+
```properties
|
|
45
|
+
MBKAUTH_TWO_FA_ENABLE=false
|
|
46
|
+
```
|
|
47
|
+
> MBKAUTH_TWO_FA_ENABLE: Set to `true` to enable Two-Factor Authentication.
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
## Cookie Settings
|
|
51
|
+
|
|
52
|
+
```properties
|
|
53
|
+
COOKIE_EXPIRE_TIME=5
|
|
54
|
+
```
|
|
55
|
+
> Cookie expiration time in days. Default is `2 days`.
|
package/index.js
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
|
-
import dotenv from "dotenv";
|
|
2
|
-
import Joi from "joi";
|
|
3
1
|
import router from "./lib/main.js";
|
|
2
|
+
|
|
3
|
+
import dotenv from "dotenv";
|
|
4
4
|
dotenv.config();
|
|
5
|
+
const mbkautheVar = JSON.parse(process.env.mbkautheVar);
|
|
6
|
+
if (!mbkautheVar) {
|
|
7
|
+
throw new Error("mbkautheVar is not defined");
|
|
8
|
+
}
|
|
9
|
+
const requiredKeys = ["RECAPTCHA_SECRET_KEY", "SESSION_SECRET_KEY", "IS_DEPLOYED", "LOGIN_DB", "MBKAUTH_TWO_FA_ENABLE", "DOMAIN"];
|
|
10
|
+
requiredKeys.forEach(key => {
|
|
11
|
+
if (!mbkautheVar[key]) {
|
|
12
|
+
throw new Error(`mbkautheVar.${key} is required`);
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
if (mbkautheVar.COOKIE_EXPIRE_TIME !== undefined) {
|
|
16
|
+
const expireTime = parseFloat(mbkautheVar.COOKIE_EXPIRE_TIME);
|
|
17
|
+
if (isNaN(expireTime) || expireTime <= 0) {
|
|
18
|
+
throw new Error("mbkautheVar.COOKIE_EXPIRE_TIME must be a valid positive number");
|
|
19
|
+
}
|
|
20
|
+
}
|
|
5
21
|
|
|
6
|
-
const envSchema = Joi.object({
|
|
7
|
-
RECAPTCHA_SECRET_KEY: Joi.string().required(),
|
|
8
|
-
SESSION_SECRET_KEY: Joi.string().required(),
|
|
9
|
-
IS_DEPLOYED: Joi.string().valid("true", "false").required(),
|
|
10
|
-
LOGIN_DB: Joi.string().uri().required(),
|
|
11
|
-
MBKAUTH_TWO_FA_ENABLE: Joi.string().valid("true", "false").required(),
|
|
12
|
-
COOKIE_EXPIRE_TIME: Joi.number().integer().positive(),
|
|
13
|
-
DOMAIN: Joi.string().required(),
|
|
14
|
-
}).unknown(true);
|
|
15
22
|
|
|
16
|
-
|
|
17
|
-
if (error) {
|
|
18
|
-
throw new Error(`Environment variable validation error: ${error.message}`);
|
|
19
|
-
}
|
|
20
|
-
export { validateSession, checkRolePermission, validateSessionAndRole, getUserData } from "./lib/validateSessionAndRole.js";
|
|
21
|
-
export { authenticate } from "./lib/auth.js";
|
|
23
|
+
export { validateSession, checkRolePermission, validateSessionAndRole, getUserData, authenticate } from "./lib/validateSessionAndRole.js";
|
|
22
24
|
export { dblogin } from "./lib/pool.js";
|
|
23
25
|
export default router;
|
package/lib/main.js
CHANGED
|
@@ -3,42 +3,48 @@ import crypto from "crypto";
|
|
|
3
3
|
import session from "express-session";
|
|
4
4
|
import pgSession from "connect-pg-simple";
|
|
5
5
|
const PgSession = pgSession(session);
|
|
6
|
-
import dotenv from "dotenv";
|
|
7
6
|
import { dblogin } from "./pool.js";
|
|
8
|
-
import { authenticate } from "./
|
|
7
|
+
import { authenticate } from "./validateSessionAndRole.js";
|
|
9
8
|
import fetch from 'node-fetch';
|
|
10
|
-
import cookieParser from "cookie-parser";
|
|
9
|
+
import cookieParser from "cookie-parser";
|
|
10
|
+
|
|
11
|
+
|
|
11
12
|
|
|
13
|
+
import dotenv from "dotenv";
|
|
12
14
|
dotenv.config();
|
|
15
|
+
const mbkautheVar = JSON.parse(process.env.mbkautheVar);
|
|
16
|
+
if (!mbkautheVar) {
|
|
17
|
+
throw new Error("mbkautheVar is not defined");
|
|
18
|
+
}
|
|
19
|
+
const requiredKeys = ["RECAPTCHA_SECRET_KEY", "SESSION_SECRET_KEY", "IS_DEPLOYED", "LOGIN_DB", "MBKAUTH_TWO_FA_ENABLE", "DOMAIN"];
|
|
20
|
+
requiredKeys.forEach(key => {
|
|
21
|
+
if (!mbkautheVar[key]) {
|
|
22
|
+
throw new Error(`mbkautheVar.${key} is required`);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
if (mbkautheVar.COOKIE_EXPIRE_TIME !== undefined) {
|
|
26
|
+
const expireTime = parseFloat(mbkautheVar.COOKIE_EXPIRE_TIME);
|
|
27
|
+
if (isNaN(expireTime) || expireTime <= 0) {
|
|
28
|
+
throw new Error("mbkautheVar.COOKIE_EXPIRE_TIME must be a valid positive number");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
13
33
|
const router = express.Router();
|
|
14
34
|
let COOKIE_EXPIRE_TIME = 2 * 24 * 60 * 60 * 1000; //2 days
|
|
15
35
|
|
|
16
36
|
try {
|
|
17
|
-
const parsedExpireTime = parseInt(
|
|
37
|
+
const parsedExpireTime = parseInt(mbkautheVar.COOKIE_EXPIRE_TIME, 10);
|
|
18
38
|
if (!isNaN(parsedExpireTime) && parsedExpireTime > 0) {
|
|
19
39
|
COOKIE_EXPIRE_TIME = parsedExpireTime * 24 * 60 * 60 * 1000; // Convert days to milliseconds
|
|
20
40
|
} else {
|
|
21
41
|
console.warn("Invalid COOKIE_EXPIRE_TIME in environment variables, using default value");
|
|
22
42
|
}
|
|
23
|
-
|
|
43
|
+
console.log(`Cookie expiration time set to ${COOKIE_EXPIRE_TIME / (24 * 60 * 60 * 1000)} days for deployed environment`);
|
|
24
44
|
} catch (error) {
|
|
25
|
-
|
|
45
|
+
console.log("Error parsing COOKIE_EXPIRE_TIME:", error);
|
|
26
46
|
}
|
|
27
47
|
|
|
28
|
-
async function WriteConsoleLogs(message) {
|
|
29
|
-
const appName = process.env.AppName;
|
|
30
|
-
try {
|
|
31
|
-
const query = `
|
|
32
|
-
INSERT INTO mbkauthlogs (app_name, message)
|
|
33
|
-
VALUES ($1, $2)
|
|
34
|
-
`;
|
|
35
|
-
await dblogin.query(query, [appName, message]);
|
|
36
|
-
console.log(`Logged message: ${message}`);
|
|
37
|
-
} catch (error) {
|
|
38
|
-
console.error("Error logging message:", error.message);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
48
|
router.use(express.json());
|
|
43
49
|
router.use(express.urlencoded({ extended: true }));
|
|
44
50
|
|
|
@@ -48,14 +54,14 @@ router.use(
|
|
|
48
54
|
pool: dblogin, // Connection pool
|
|
49
55
|
tableName: "session", // Use another table-name than the default "session" one
|
|
50
56
|
}),
|
|
51
|
-
secret:
|
|
57
|
+
secret: mbkautheVar.SESSION_SECRET_KEY, // Replace with your secret key
|
|
52
58
|
resave: false,
|
|
53
59
|
saveUninitialized: false,
|
|
54
60
|
cookie: {
|
|
55
61
|
maxAge: COOKIE_EXPIRE_TIME,
|
|
56
|
-
DOMAIN:
|
|
62
|
+
DOMAIN: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined, // Use root DOMAIN for subDOMAIN sharing
|
|
57
63
|
httpOnly: true,
|
|
58
|
-
secure:
|
|
64
|
+
secure: mbkautheVar.IS_DEPLOYED === 'true', // Use secure cookies in production
|
|
59
65
|
},
|
|
60
66
|
})
|
|
61
67
|
);
|
|
@@ -105,7 +111,7 @@ router.use(async (req, res, next) => {
|
|
|
105
111
|
req.session.user.role = null;
|
|
106
112
|
}
|
|
107
113
|
} catch (error) {
|
|
108
|
-
|
|
114
|
+
console.log("Error fetching user role:", error.message);
|
|
109
115
|
req.session.user.role = null; // Fallback to null role
|
|
110
116
|
}
|
|
111
117
|
}
|
|
@@ -115,7 +121,7 @@ router.use(async (req, res, next) => {
|
|
|
115
121
|
router.use(async (req, res, next) => {
|
|
116
122
|
// Check for sessionId cookie if session is not initialized
|
|
117
123
|
if (!req.session.user && req.cookies && req.cookies.sessionId) {
|
|
118
|
-
|
|
124
|
+
console.log("Restoring session from sessionId cookie"); // Log session restoration
|
|
119
125
|
const sessionId = req.cookies.sessionId;
|
|
120
126
|
const query = `SELECT * FROM "Users" WHERE "SessionId" = $1`;
|
|
121
127
|
const result = await dblogin.query(query, [sessionId]);
|
|
@@ -127,7 +133,7 @@ router.use(async (req, res, next) => {
|
|
|
127
133
|
username: user.UserName,
|
|
128
134
|
sessionId,
|
|
129
135
|
};
|
|
130
|
-
|
|
136
|
+
console.log(`Session restored for user: ${user.UserName}`); // Log successful session restoration
|
|
131
137
|
} else {
|
|
132
138
|
console.warn("No matching session found for sessionId"); // Log if no session is found
|
|
133
139
|
}
|
|
@@ -137,7 +143,7 @@ router.use(async (req, res, next) => {
|
|
|
137
143
|
|
|
138
144
|
//Invoke-RestMethod -Uri http://localhost:3030/terminateAllSessions -Method POST
|
|
139
145
|
// Terminate all sessions route
|
|
140
|
-
router.post("/mbkauthe/api/terminateAllSessions", authenticate(
|
|
146
|
+
router.post("/mbkauthe/api/terminateAllSessions", authenticate(mbkautheVar.Main_SECRET_TOKEN), async (req, res) => {
|
|
141
147
|
try {
|
|
142
148
|
await dblogin.query(`UPDATE "Users" SET "SessionId" = NULL`);
|
|
143
149
|
|
|
@@ -147,19 +153,19 @@ router.post("/mbkauthe/api/terminateAllSessions", authenticate(process.env.Main_
|
|
|
147
153
|
// Destroy all sessions on the server
|
|
148
154
|
req.session.destroy((err) => {
|
|
149
155
|
if (err) {
|
|
150
|
-
|
|
156
|
+
console.log("Error destroying session:", err);
|
|
151
157
|
return res
|
|
152
158
|
.status(500)
|
|
153
159
|
.json({ success: false, message: "Failed to terminate sessions" });
|
|
154
160
|
}
|
|
155
|
-
|
|
161
|
+
console.log("All sessions terminated successfully");
|
|
156
162
|
res.status(200).json({
|
|
157
163
|
success: true,
|
|
158
164
|
message: "All sessions terminated successfully",
|
|
159
165
|
});
|
|
160
166
|
});
|
|
161
167
|
} catch (err) {
|
|
162
|
-
|
|
168
|
+
console.log("Database query error during session termination:", err);
|
|
163
169
|
res
|
|
164
170
|
.status(500)
|
|
165
171
|
.json({ success: false, message: "Internal Server Error" });
|
|
@@ -168,55 +174,53 @@ router.post("/mbkauthe/api/terminateAllSessions", authenticate(process.env.Main_
|
|
|
168
174
|
);
|
|
169
175
|
|
|
170
176
|
router.post("/mbkauthe/api/login", async (req, res) => {
|
|
171
|
-
|
|
177
|
+
console.log("Login request received"); // Log when login is initiated
|
|
172
178
|
|
|
173
179
|
const { username, password, token, recaptcha } = req.body;
|
|
174
|
-
|
|
180
|
+
console.log(`Login attempt for username: ${username}`); // Log username
|
|
175
181
|
|
|
176
|
-
const secretKey =
|
|
182
|
+
const secretKey = mbkautheVar.RECAPTCHA_SECRET_KEY;
|
|
177
183
|
const verificationUrl = `https://www.google.com/recaptcha/api/siteverify?secret=${secretKey}&response=${recaptcha}`;
|
|
178
184
|
|
|
185
|
+
let BypassUsers = ["ibnekhalid", "maaz.waheed", "support"];
|
|
186
|
+
|
|
179
187
|
// Bypass recaptcha for specific users
|
|
180
|
-
if (
|
|
188
|
+
if (!BypassUsers.includes(username)) {
|
|
189
|
+
if (!recaptcha) {
|
|
190
|
+
console.log("Missing reCAPTCHA token");
|
|
191
|
+
return res.status(400).json({ success: false, message: "Please complete the reCAPTCHA" });
|
|
192
|
+
}
|
|
181
193
|
try {
|
|
182
194
|
const response = await fetch(verificationUrl, { method: 'POST' });
|
|
183
195
|
const body = await response.json();
|
|
184
|
-
|
|
196
|
+
console.log("reCAPTCHA verification response:", body); // Log reCAPTCHA response
|
|
185
197
|
|
|
186
198
|
if (!body.success) {
|
|
187
|
-
|
|
199
|
+
console.log("Failed reCAPTCHA verification");
|
|
188
200
|
return res.status(400).json({ success: false, message: "Failed reCAPTCHA verification" });
|
|
189
201
|
}
|
|
190
202
|
} catch (err) {
|
|
191
|
-
|
|
203
|
+
console.log("Error during reCAPTCHA verification:", err);
|
|
192
204
|
return res.status(500).json({ success: false, message: "Internal Server Error" });
|
|
193
205
|
}
|
|
194
206
|
}
|
|
195
207
|
|
|
196
208
|
if (!username || !password) {
|
|
197
|
-
|
|
209
|
+
console.log("Missing username or password");
|
|
198
210
|
return res.status(400).json({
|
|
199
211
|
success: false,
|
|
200
212
|
message: "Username and password are required",
|
|
201
213
|
});
|
|
202
214
|
}
|
|
203
215
|
|
|
204
|
-
WriteConsoleLogs("RECAPTCHA_SECRET_KEY:", process.env.RECAPTCHA_SECRET_KEY); // Log reCAPTCHA secret key
|
|
205
|
-
WriteConsoleLogs("SESSION_SECRET_KEY:", process.env.SESSION_SECRET_KEY); // Log reCAPTCHA secret key
|
|
206
|
-
WriteConsoleLogs("LOGIN_DB:", process.env.LOGIN_DB); // Log reCAPTCHA secret key
|
|
207
|
-
WriteConsoleLogs("COOKIE_EXPIRE_TIME:", process.env.COOKIE_EXPIRE_TIME); // Log reCAPTCHA secret key
|
|
208
|
-
WriteConsoleLogs("DOMAIN:", process.env.DOMAIN); // Log reCAPTCHA secret key
|
|
209
|
-
WriteConsoleLogs("IS_DEPLOYED:", process.env.IS_DEPLOYED); // Log reCAPTCHA secret key
|
|
210
|
-
WriteConsoleLogs("MBKAUTH_TWO_FA_ENABLE:", process.env.MBKAUTH_TWO_FA_ENABLE); // Log reCAPTCHA secret key
|
|
211
|
-
|
|
212
216
|
try {
|
|
213
217
|
// Query to check if the username exists
|
|
214
218
|
const userQuery = `SELECT * FROM "Users" WHERE "UserName" = $1`;
|
|
215
219
|
const userResult = await dblogin.query(userQuery, [username]);
|
|
216
|
-
|
|
220
|
+
console.log("User query result:", userResult.rows); // Log user query result
|
|
217
221
|
|
|
218
222
|
if (userResult.rows.length === 0) {
|
|
219
|
-
|
|
223
|
+
console.log(`Username does not exist: ${username}`);
|
|
220
224
|
return res.status(404).json({ success: false, message: "Username does not exist" });
|
|
221
225
|
}
|
|
222
226
|
|
|
@@ -224,25 +228,25 @@ router.post("/mbkauthe/api/login", async (req, res) => {
|
|
|
224
228
|
|
|
225
229
|
// Check if the password matches
|
|
226
230
|
if (user.Password !== password) {
|
|
227
|
-
|
|
231
|
+
console.log(`Incorrect password for username: ${username}`);
|
|
228
232
|
return res.status(401).json({ success: false, message: "Incorrect password" });
|
|
229
233
|
}
|
|
230
234
|
|
|
231
235
|
// Check if the account is inactive
|
|
232
236
|
if (!user.Active) {
|
|
233
|
-
|
|
237
|
+
console.log(`Inactive account for username: ${username}`);
|
|
234
238
|
return res.status(403).json({ success: false, message: "Account is inactive" });
|
|
235
239
|
}
|
|
236
240
|
|
|
237
|
-
if ((
|
|
241
|
+
if ((mbkautheVar.MBKAUTH_TWO_FA_ENABLE || "").toLocaleLowerCase() === "true") {
|
|
238
242
|
let sharedSecret;
|
|
239
243
|
const query = `SELECT "TwoFAStatus", "TwoFASecret" FROM "TwoFA" WHERE "UserName" = $1`;
|
|
240
244
|
const twoFAResult = await dblogin.query(query, [username]);
|
|
241
|
-
|
|
245
|
+
console.log("TwoFA query result:", twoFAResult.rows); // Log TwoFA query result
|
|
242
246
|
|
|
243
247
|
sharedSecret = twoFAResult.rows[0]?.TwoFASecret;
|
|
244
248
|
if (twoFAResult.rows.length > 0 && twoFAResult.rows[0].TwoFAStatus && !token) {
|
|
245
|
-
|
|
249
|
+
console.log("2FA code required but not provided");
|
|
246
250
|
return res.status(401).json({ success: false, message: "Please Enter 2FA code" });
|
|
247
251
|
}
|
|
248
252
|
|
|
@@ -255,7 +259,7 @@ router.post("/mbkauthe/api/login", async (req, res) => {
|
|
|
255
259
|
});
|
|
256
260
|
|
|
257
261
|
if (!tokenValidates) {
|
|
258
|
-
|
|
262
|
+
console.log(`Invalid 2FA code for username: ${username}`);
|
|
259
263
|
return res.status(401).json({ success: false, message: "Invalid 2FA code" });
|
|
260
264
|
}
|
|
261
265
|
}
|
|
@@ -263,7 +267,7 @@ router.post("/mbkauthe/api/login", async (req, res) => {
|
|
|
263
267
|
|
|
264
268
|
// Generate session ID
|
|
265
269
|
const sessionId = crypto.randomBytes(256).toString("hex");
|
|
266
|
-
|
|
270
|
+
console.log(`Generated session ID for username: ${username}`); // Log session ID
|
|
267
271
|
|
|
268
272
|
await dblogin.query(`UPDATE "Users" SET "SessionId" = $1 WHERE "id" = $2`, [
|
|
269
273
|
sessionId,
|
|
@@ -276,25 +280,25 @@ router.post("/mbkauthe/api/login", async (req, res) => {
|
|
|
276
280
|
username: user.UserName,
|
|
277
281
|
sessionId,
|
|
278
282
|
};
|
|
279
|
-
|
|
283
|
+
console.log(`Session stored for user: ${user.UserName}, sessionId: ${sessionId}`); // Log session storage
|
|
280
284
|
|
|
281
285
|
// Set a cookie accessible across subDOMAINs
|
|
282
286
|
res.cookie("sessionId", sessionId, {
|
|
283
287
|
maxAge: COOKIE_EXPIRE_TIME,
|
|
284
|
-
DOMAIN:
|
|
288
|
+
DOMAIN: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined, // Use DOMAIN only in production
|
|
285
289
|
httpOnly: true,
|
|
286
|
-
secure:
|
|
290
|
+
secure: mbkautheVar.IS_DEPLOYED === 'true', // Use secure cookies in production
|
|
287
291
|
});
|
|
288
|
-
|
|
292
|
+
console.log(`Cookie set for user: ${user.UserName}, sessionId: ${sessionId}`); // Log cookie setting
|
|
289
293
|
|
|
290
|
-
|
|
294
|
+
console.log(`User "${username}" logged in successfully`);
|
|
291
295
|
res.status(200).json({
|
|
292
296
|
success: true,
|
|
293
297
|
message: "Login successful",
|
|
294
298
|
sessionId,
|
|
295
299
|
});
|
|
296
300
|
} catch (err) {
|
|
297
|
-
|
|
301
|
+
console.log("Error during login process:", err);
|
|
298
302
|
res.status(500).json({ success: false, message: "Internal Server Error" });
|
|
299
303
|
}
|
|
300
304
|
});
|
|
@@ -307,22 +311,22 @@ router.post("/mbkauthe/api/logout", async (req, res) => {
|
|
|
307
311
|
const result = await dblogin.query(query, [id]);
|
|
308
312
|
|
|
309
313
|
if (result.rows.length > 0 && !result.rows[0].Active) {
|
|
310
|
-
|
|
314
|
+
console.log("Account is inactive during logout");
|
|
311
315
|
}
|
|
312
316
|
|
|
313
317
|
req.session.destroy((err) => {
|
|
314
318
|
if (err) {
|
|
315
|
-
|
|
319
|
+
console.log("Error destroying session:", err);
|
|
316
320
|
return res.status(500).json({ success: false, message: "Logout failed" });
|
|
317
321
|
}
|
|
318
322
|
// Clear both session cookies
|
|
319
323
|
res.clearCookie("connect.sid");
|
|
320
324
|
res.clearCookie("sessionId"); // Clear the sessionId cookie used for restoration
|
|
321
|
-
|
|
325
|
+
console.log(`User "${username}" logged out successfully`);
|
|
322
326
|
res.status(200).json({ success: true, message: "Logout successful" });
|
|
323
327
|
});
|
|
324
328
|
} catch (err) {
|
|
325
|
-
|
|
329
|
+
console.log("Database query error during logout:", err);
|
|
326
330
|
res.status(500).json({ success: false, message: "Internal Server Error" });
|
|
327
331
|
}
|
|
328
332
|
} else {
|
package/lib/pool.js
CHANGED
|
@@ -1,12 +1,29 @@
|
|
|
1
1
|
import pkg from "pg";
|
|
2
2
|
const { Pool } = pkg;
|
|
3
|
-
import dotenv from "dotenv";
|
|
4
3
|
|
|
4
|
+
|
|
5
|
+
import dotenv from "dotenv";
|
|
5
6
|
dotenv.config();
|
|
7
|
+
const mbkautheVar = JSON.parse(process.env.mbkautheVar);
|
|
8
|
+
if (!mbkautheVar) {
|
|
9
|
+
throw new Error("mbkautheVar is not defined");
|
|
10
|
+
}
|
|
11
|
+
const requiredKeys = ["RECAPTCHA_SECRET_KEY", "SESSION_SECRET_KEY", "IS_DEPLOYED", "LOGIN_DB", "MBKAUTH_TWO_FA_ENABLE", "DOMAIN"];
|
|
12
|
+
requiredKeys.forEach(key => {
|
|
13
|
+
if (!mbkautheVar[key]) {
|
|
14
|
+
throw new Error(`mbkautheVar.${key} is required`);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
if (mbkautheVar.COOKIE_EXPIRE_TIME !== undefined) {
|
|
18
|
+
const expireTime = parseFloat(mbkautheVar.COOKIE_EXPIRE_TIME);
|
|
19
|
+
if (isNaN(expireTime) || expireTime <= 0) {
|
|
20
|
+
throw new Error("mbkautheVar.COOKIE_EXPIRE_TIME must be a valid positive number");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
6
23
|
|
|
7
24
|
// PostgreSQL connection pool for pool
|
|
8
25
|
const poolConfig = {
|
|
9
|
-
connectionString:
|
|
26
|
+
connectionString: mbkautheVar.LOGIN_DB,
|
|
10
27
|
ssl: {
|
|
11
28
|
rejectUnauthorized: true,
|
|
12
29
|
},
|
|
@@ -149,4 +149,18 @@ async function getUserData(UserName, parameters) {
|
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
|
|
152
|
+
const authenticate = (authentication) => {
|
|
153
|
+
return (req, res, next) => {
|
|
154
|
+
const token = req.headers["authorization"];
|
|
155
|
+
console.log(`Received token: ${token}`);
|
|
156
|
+
if (token === authentication) {
|
|
157
|
+
console.log("Authentication successful");
|
|
158
|
+
next();
|
|
159
|
+
} else {
|
|
160
|
+
console.log("Authentication failed");
|
|
161
|
+
res.status(401).send("Unauthorized");
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export { validateSession, checkRolePermission, validateSessionAndRole, getUserData , authenticate};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mbkauthe",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "MBKTechStudio's reusable authentication system for Node.js applications.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -31,7 +31,6 @@
|
|
|
31
31
|
"dotenv": "^16.4.7",
|
|
32
32
|
"express": "^5.1.0",
|
|
33
33
|
"express-session": "^1.18.1",
|
|
34
|
-
"joi": "^17.13.3",
|
|
35
34
|
"node-fetch": "^3.3.2",
|
|
36
35
|
"pg": "^8.14.1"
|
|
37
36
|
}
|
package/lib/auth.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export const authenticate = (authentication) => {
|
|
2
|
-
return (req, res, next) => {
|
|
3
|
-
const token = req.headers["authorization"];
|
|
4
|
-
console.log(`Received token: ${token}`);
|
|
5
|
-
if (token === authentication) {
|
|
6
|
-
console.log("Authentication successful");
|
|
7
|
-
next();
|
|
8
|
-
} else {
|
|
9
|
-
console.log("Authentication failed");
|
|
10
|
-
res.status(401).send("Unauthorized");
|
|
11
|
-
}
|
|
12
|
-
};
|
|
13
|
-
};
|