mbkauthe 3.0.0 → 3.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/README.md +98 -16
- package/docs/api.md +212 -117
- package/docs/db.md +71 -16
- package/docs/env.md +91 -8
- package/index.d.ts +59 -21
- package/lib/config/index.js +48 -1
- package/lib/main.js +27 -1
- package/lib/middleware/index.js +4 -2
- package/lib/routes/auth.js +25 -3
- package/lib/routes/misc.js +29 -17
- package/lib/routes/oauth.js +379 -264
- package/lib/utils/response.js +2 -2
- package/package.json +30 -10
- package/test.spec.js +196 -0
- package/views/2fa.handlebars +2 -2
- package/views/loginmbkauthe.handlebars +35 -5
- package/views/sharedStyles.handlebars +5 -0
- package/views/showmessage.handlebars +27 -1
- package/.env.example +0 -3
- package/.github/PACKAGE.md +0 -17
- package/.github/workflows/codeql.yml +0 -98
- package/.github/workflows/publish.yml +0 -87
package/docs/db.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
#
|
|
1
|
+
# OAuth Login Setup Guide
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
|
-
This
|
|
4
|
+
This OAuth login feature allows users to authenticate using their GitHub or Google account if it's already linked to their account in the system. Users must first connect their OAuth account through the regular account linking process, then they can use it to log in directly.
|
|
5
5
|
|
|
6
6
|
## Setup Instructions
|
|
7
7
|
|
|
@@ -12,6 +12,10 @@ Add these to your `.env` file:
|
|
|
12
12
|
# GitHub OAuth App Configuration
|
|
13
13
|
GITHUB_CLIENT_ID=your_github_client_id
|
|
14
14
|
GITHUB_CLIENT_SECRET=your_github_client_secret
|
|
15
|
+
|
|
16
|
+
# Google OAuth App Configuration
|
|
17
|
+
GOOGLE_CLIENT_ID=your_google_client_id
|
|
18
|
+
GOOGLE_CLIENT_SECRET=your_google_client_secret
|
|
15
19
|
```
|
|
16
20
|
|
|
17
21
|
### 2. GitHub OAuth App Setup
|
|
@@ -20,10 +24,20 @@ GITHUB_CLIENT_SECRET=your_github_client_secret
|
|
|
20
24
|
3. Set the Authorization callback URL to: `https://yourdomain.com/mbkauthe/api/github/login/callback`
|
|
21
25
|
4. Copy the Client ID and Client Secret to your `.env` file
|
|
22
26
|
|
|
23
|
-
### 3.
|
|
24
|
-
|
|
27
|
+
### 3. Google OAuth App Setup
|
|
28
|
+
1. Go to Google Cloud Console (https://console.cloud.google.com/)
|
|
29
|
+
2. Create a new project or select an existing one
|
|
30
|
+
3. Enable the Google+ API
|
|
31
|
+
4. Go to Credentials > Create Credentials > OAuth 2.0 Client ID
|
|
32
|
+
5. Set the application type to "Web application"
|
|
33
|
+
6. Add authorized redirect URI: `https://yourdomain.com/mbkauthe/api/google/login/callback`
|
|
34
|
+
7. Copy the Client ID and Client Secret to your `.env` file
|
|
35
|
+
|
|
36
|
+
### 4. Database Schema
|
|
37
|
+
Ensure your OAuth tables exist with these columns:
|
|
25
38
|
|
|
26
39
|
```sql
|
|
40
|
+
-- GitHub users table
|
|
27
41
|
CREATE TABLE user_github (
|
|
28
42
|
id SERIAL PRIMARY KEY,
|
|
29
43
|
user_name VARCHAR(50) REFERENCES "Users"("UserName"),
|
|
@@ -37,57 +51,98 @@ CREATE TABLE user_github (
|
|
|
37
51
|
-- Add indexes for performance optimization
|
|
38
52
|
CREATE INDEX IF NOT EXISTS idx_user_github_github_id ON user_github (github_id);
|
|
39
53
|
CREATE INDEX IF NOT EXISTS idx_user_github_user_name ON user_github (user_name);
|
|
54
|
+
|
|
55
|
+
-- Google users table
|
|
56
|
+
CREATE TABLE user_google (
|
|
57
|
+
id SERIAL PRIMARY KEY,
|
|
58
|
+
user_name VARCHAR(50) REFERENCES "Users"("UserName"),
|
|
59
|
+
google_id VARCHAR(255) UNIQUE,
|
|
60
|
+
google_email VARCHAR(255),
|
|
61
|
+
access_token VARCHAR(255),
|
|
62
|
+
created_at TimeStamp WITH TIME ZONE DEFAULT NOW(),
|
|
63
|
+
updated_at TimeStamp WITH TIME ZONE DEFAULT NOW()
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
-- Add indexes for performance optimization
|
|
67
|
+
CREATE INDEX IF NOT EXISTS idx_user_google_google_id ON user_google (google_id);
|
|
68
|
+
CREATE INDEX IF NOT EXISTS idx_user_google_user_name ON user_google (user_name);
|
|
40
69
|
```
|
|
41
70
|
|
|
42
71
|
## How It Works
|
|
43
72
|
|
|
44
|
-
### Login Flow
|
|
45
|
-
1. User clicks "Login with GitHub" on the login page
|
|
46
|
-
2. User is redirected to
|
|
47
|
-
3.
|
|
48
|
-
4. System checks if the
|
|
73
|
+
### Login Flow (GitHub/Google)
|
|
74
|
+
1. User clicks "Login with GitHub" or "Login with Google" on the login page
|
|
75
|
+
2. User is redirected to the OAuth provider for authentication
|
|
76
|
+
3. Provider redirects back to `/mbkauthe/api/{provider}/login/callback`
|
|
77
|
+
4. System checks if the OAuth ID exists in the respective `user_{provider}` table
|
|
49
78
|
5. If found and user is active/authorized:
|
|
50
79
|
- If 2FA is enabled, redirect to 2FA page
|
|
51
80
|
- If no 2FA, complete login and redirect to home
|
|
52
81
|
6. If not found, redirect to login page with error
|
|
53
82
|
|
|
54
83
|
### Account Linking
|
|
55
|
-
Users must first link their
|
|
84
|
+
Users must first link their OAuth account through your existing connection system (likely in user settings) before they can use OAuth login.
|
|
56
85
|
|
|
57
86
|
## API Routes Added
|
|
58
87
|
|
|
59
|
-
###
|
|
88
|
+
### GitHub Routes
|
|
89
|
+
|
|
90
|
+
#### `/mbkauthe/api/github/login`
|
|
60
91
|
- **Method**: GET
|
|
61
92
|
- **Description**: Initiates GitHub OAuth flow
|
|
62
93
|
- **Redirects to**: GitHub authorization page
|
|
63
94
|
|
|
64
|
-
|
|
95
|
+
#### `/mbkauthe/api/github/login/callback`
|
|
65
96
|
- **Method**: GET
|
|
66
97
|
- **Description**: Handles GitHub OAuth callback
|
|
67
98
|
- **Parameters**: `code` (from GitHub), `state` (optional)
|
|
68
99
|
- **Success**: Redirects to home page or configured redirect URL
|
|
69
100
|
- **Error**: Redirects to login page with error parameter
|
|
70
101
|
|
|
102
|
+
### Google Routes
|
|
103
|
+
|
|
104
|
+
#### `/mbkauthe/api/google/login`
|
|
105
|
+
- **Method**: GET
|
|
106
|
+
- **Description**: Initiates Google OAuth flow
|
|
107
|
+
- **Redirects to**: Google authorization page
|
|
108
|
+
|
|
109
|
+
#### `/mbkauthe/api/google/login/callback`
|
|
110
|
+
- **Method**: GET
|
|
111
|
+
- **Description**: Handles Google OAuth callback
|
|
112
|
+
- **Parameters**: `code` (from Google), `state` (optional)
|
|
113
|
+
- **Success**: Redirects to home page or configured redirect URL
|
|
114
|
+
- **Error**: Redirects to login page with error parameter
|
|
115
|
+
|
|
71
116
|
## Error Handling
|
|
72
117
|
|
|
73
118
|
The system handles various error cases:
|
|
74
|
-
- `github_auth_failed`:
|
|
75
|
-
- `user_not_found`:
|
|
119
|
+
- `github_auth_failed` / `google_auth_failed`: OAuth authentication failed
|
|
120
|
+
- `user_not_found`: OAuth account not linked to any user
|
|
121
|
+
- `account_inactive`: User account is deactivated
|
|
122
|
+
- `not_authorized`: User not authorized for this app
|
|
76
123
|
- `session_error`: Session save failed
|
|
77
124
|
- `internal_error`: General server error
|
|
78
125
|
|
|
79
126
|
## Testing
|
|
80
127
|
|
|
128
|
+
### GitHub Login
|
|
81
129
|
1. Create a test user in your `Users` table
|
|
82
130
|
2. Link a GitHub account to that user using your existing connection system
|
|
83
131
|
3. Try logging in with GitHub using the new login button
|
|
84
132
|
4. Check console logs for debugging information
|
|
85
133
|
|
|
134
|
+
### Google Login
|
|
135
|
+
1. Create a test user in your `Users` table
|
|
136
|
+
2. Link a Google account to that user using your existing connection system
|
|
137
|
+
3. Try logging in with Google using the new login button
|
|
138
|
+
4. Check console logs for debugging information
|
|
139
|
+
|
|
86
140
|
## Login Page Updates
|
|
87
141
|
|
|
88
142
|
The login page now includes:
|
|
89
143
|
- A "Continue with GitHub" button
|
|
90
|
-
- A
|
|
144
|
+
- A "Continue with Google" button
|
|
145
|
+
- A divider ("or") between regular and OAuth login
|
|
91
146
|
- Proper styling that matches your existing design
|
|
92
147
|
|
|
93
148
|
## Security Notes
|
|
@@ -96,7 +151,7 @@ The login page now includes:
|
|
|
96
151
|
- App authorization is checked (same as regular login)
|
|
97
152
|
- 2FA is respected if enabled
|
|
98
153
|
- Session management is handled the same way as regular login
|
|
99
|
-
-
|
|
154
|
+
- OAuth access tokens are stored securely
|
|
100
155
|
|
|
101
156
|
## Troubleshooting
|
|
102
157
|
|
package/docs/env.md
CHANGED
|
@@ -230,16 +230,18 @@ DEVICE_TRUST_DURATION_DAYS=30 # 30 days (convenience)
|
|
|
230
230
|
|
|
231
231
|
---
|
|
232
232
|
|
|
233
|
-
##
|
|
233
|
+
## 🔐 OAuth Authentication
|
|
234
234
|
|
|
235
|
-
### GitHub
|
|
235
|
+
### GitHub OAuth Authentication
|
|
236
|
+
|
|
237
|
+
#### GitHub Login Configuration
|
|
236
238
|
```env
|
|
237
239
|
GITHUB_LOGIN_ENABLED=false
|
|
238
240
|
GITHUB_CLIENT_ID=your-github-client-id
|
|
239
241
|
GITHUB_CLIENT_SECRET=your-github-client-secret
|
|
240
242
|
```
|
|
241
243
|
|
|
242
|
-
|
|
244
|
+
##### GITHUB_LOGIN_ENABLED
|
|
243
245
|
**Description:** Enables or disables GitHub OAuth login functionality.
|
|
244
246
|
|
|
245
247
|
**Values:**
|
|
@@ -248,7 +250,7 @@ GITHUB_CLIENT_SECRET=your-github-client-secret
|
|
|
248
250
|
|
|
249
251
|
**Required:** Yes (if using GitHub authentication)
|
|
250
252
|
|
|
251
|
-
|
|
253
|
+
##### GITHUB_CLIENT_ID
|
|
252
254
|
**Description:** OAuth application client ID from GitHub.
|
|
253
255
|
|
|
254
256
|
- **Purpose:** Identifies your application to GitHub's OAuth service
|
|
@@ -258,7 +260,7 @@ GITHUB_CLIENT_SECRET=your-github-client-secret
|
|
|
258
260
|
|
|
259
261
|
**Example:** `GITHUB_CLIENT_ID=Iv1.a1b2c3d4e5f6g7h8`
|
|
260
262
|
|
|
261
|
-
|
|
263
|
+
##### GITHUB_CLIENT_SECRET
|
|
262
264
|
**Description:** OAuth application client secret from GitHub.
|
|
263
265
|
|
|
264
266
|
- **Purpose:** Authenticates your application with GitHub's OAuth service
|
|
@@ -269,7 +271,7 @@ GITHUB_CLIENT_SECRET=your-github-client-secret
|
|
|
269
271
|
|
|
270
272
|
**Example:** `GITHUB_CLIENT_SECRET=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0`
|
|
271
273
|
|
|
272
|
-
|
|
274
|
+
#### Setting Up GitHub OAuth
|
|
273
275
|
|
|
274
276
|
1. **Create GitHub OAuth App:**
|
|
275
277
|
- Go to [GitHub Developer Settings](https://github.com/settings/developers)
|
|
@@ -277,7 +279,7 @@ GITHUB_CLIENT_SECRET=your-github-client-secret
|
|
|
277
279
|
- Fill in application details:
|
|
278
280
|
- **Application name:** Your app name
|
|
279
281
|
- **Homepage URL:** `https://yourdomain.com` (or `http://localhost:3000` for dev)
|
|
280
|
-
- **Authorization callback URL:** `https://yourdomain.com/
|
|
282
|
+
- **Authorization callback URL:** `https://yourdomain.com/mbkauthe/api/github/login/callback`
|
|
281
283
|
- Click "Register application"
|
|
282
284
|
|
|
283
285
|
2. **Copy Credentials:**
|
|
@@ -291,10 +293,91 @@ GITHUB_CLIENT_SECRET=your-github-client-secret
|
|
|
291
293
|
GITHUB_CLIENT_SECRET=your-copied-client-secret
|
|
292
294
|
```
|
|
293
295
|
|
|
294
|
-
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
### Google OAuth Authentication
|
|
299
|
+
|
|
300
|
+
#### Google Login Configuration
|
|
301
|
+
```env
|
|
302
|
+
GOOGLE_LOGIN_ENABLED=false
|
|
303
|
+
GOOGLE_CLIENT_ID=your-google-client-id
|
|
304
|
+
GOOGLE_CLIENT_SECRET=your-google-client-secret
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
##### GOOGLE_LOGIN_ENABLED
|
|
308
|
+
**Description:** Enables or disables Google OAuth login functionality.
|
|
309
|
+
|
|
310
|
+
**Values:**
|
|
311
|
+
- `true` - Enable Google login (users can authenticate via Google)
|
|
312
|
+
- `false` - Disable Google login (default)
|
|
313
|
+
|
|
314
|
+
**Required:** Yes (if using Google authentication)
|
|
315
|
+
|
|
316
|
+
##### GOOGLE_CLIENT_ID
|
|
317
|
+
**Description:** OAuth 2.0 client ID from Google Cloud Console.
|
|
318
|
+
|
|
319
|
+
- **Purpose:** Identifies your application to Google's OAuth service
|
|
320
|
+
- **Format:** String ending in `.apps.googleusercontent.com`
|
|
321
|
+
- **Setup:** Obtain from [Google Cloud Console](https://console.cloud.google.com/)
|
|
322
|
+
- **Required:** Yes (when `GOOGLE_LOGIN_ENABLED=true`)
|
|
323
|
+
|
|
324
|
+
**Example:** `GOOGLE_CLIENT_ID=123456789-abc123def456.apps.googleusercontent.com`
|
|
325
|
+
|
|
326
|
+
##### GOOGLE_CLIENT_SECRET
|
|
327
|
+
**Description:** OAuth 2.0 client secret from Google Cloud Console.
|
|
328
|
+
|
|
329
|
+
- **Purpose:** Authenticates your application with Google's OAuth service
|
|
330
|
+
- **Security:** Keep this secret secure and never commit to version control
|
|
331
|
+
- **Format:** Alphanumeric string provided by Google
|
|
332
|
+
- **Setup:** Generated when creating OAuth credentials in Google Cloud Console
|
|
333
|
+
- **Required:** Yes (when `GOOGLE_LOGIN_ENABLED=true`)
|
|
334
|
+
|
|
335
|
+
**Example:** `GOOGLE_CLIENT_SECRET=GOCSPX-abc123def456ghi789jkl012mno`
|
|
336
|
+
|
|
337
|
+
#### Setting Up Google OAuth
|
|
338
|
+
|
|
339
|
+
1. **Create Google Cloud Project:**
|
|
340
|
+
- Go to [Google Cloud Console](https://console.cloud.google.com/)
|
|
341
|
+
- Create a new project or select an existing one
|
|
342
|
+
- Enable the Google+ API (or People API)
|
|
343
|
+
|
|
344
|
+
2. **Configure OAuth Consent Screen:**
|
|
345
|
+
- Navigate to "OAuth consent screen" in the sidebar
|
|
346
|
+
- Choose "External" user type (or "Internal" for Google Workspace)
|
|
347
|
+
- Fill in required app information
|
|
348
|
+
- Add your domain to authorized domains
|
|
349
|
+
- Save and continue
|
|
350
|
+
|
|
351
|
+
3. **Create OAuth Credentials:**
|
|
352
|
+
- Navigate to "Credentials" in the sidebar
|
|
353
|
+
- Click "Create Credentials" > "OAuth 2.0 Client ID"
|
|
354
|
+
- Choose "Web application" as application type
|
|
355
|
+
- Add authorized JavaScript origins:
|
|
356
|
+
- `https://yourdomain.com`
|
|
357
|
+
- `http://localhost:3000` (for development)
|
|
358
|
+
- Add authorized redirect URIs:
|
|
359
|
+
- `https://yourdomain.com/mbkauthe/api/google/login/callback`
|
|
360
|
+
- `http://localhost:3000/mbkauthe/api/google/login/callback` (for development)
|
|
361
|
+
- Click "Create"
|
|
362
|
+
|
|
363
|
+
4. **Copy Credentials:**
|
|
364
|
+
- Copy the **Client ID**
|
|
365
|
+
- Copy the **Client Secret**
|
|
366
|
+
|
|
367
|
+
5. **Configure Environment:**
|
|
368
|
+
```env
|
|
369
|
+
GOOGLE_LOGIN_ENABLED=true
|
|
370
|
+
GOOGLE_CLIENT_ID=your-copied-client-id
|
|
371
|
+
GOOGLE_CLIENT_SECRET=your-copied-client-secret
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### OAuth Security Notes
|
|
375
|
+
|
|
295
376
|
- Use separate OAuth apps for development and production environments
|
|
296
377
|
- Rotate client secrets periodically
|
|
297
378
|
- Never expose client secrets in client-side code
|
|
379
|
+
- Ensure callback URLs match exactly with OAuth provider configuration
|
|
380
|
+
- Users must link their OAuth accounts before they can use OAuth login
|
|
298
381
|
|
|
299
382
|
---
|
|
300
383
|
|
package/index.d.ts
CHANGED
|
@@ -5,6 +5,44 @@
|
|
|
5
5
|
import { Request, Response, NextFunction, Router } from 'express';
|
|
6
6
|
import { Pool } from 'pg';
|
|
7
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' | 'google';
|
|
38
|
+
redirectUrl?: string | null;
|
|
39
|
+
};
|
|
40
|
+
oauthRedirect?: string;
|
|
41
|
+
oauthCsrfToken?: string;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
8
46
|
declare module 'mbkauthe' {
|
|
9
47
|
// Configuration Types
|
|
10
48
|
export interface MBKAuthConfig {
|
|
@@ -20,10 +58,22 @@ declare module 'mbkauthe' {
|
|
|
20
58
|
GITHUB_LOGIN_ENABLED?: 'true' | 'false' | 'f';
|
|
21
59
|
GITHUB_CLIENT_ID?: string;
|
|
22
60
|
GITHUB_CLIENT_SECRET?: string;
|
|
61
|
+
GOOGLE_LOGIN_ENABLED?: 'true' | 'false' | 'f';
|
|
62
|
+
GOOGLE_CLIENT_ID?: string;
|
|
63
|
+
GOOGLE_CLIENT_SECRET?: string;
|
|
23
64
|
loginRedirectURL?: string;
|
|
24
65
|
EncPass?: 'true' | 'false' | 'f';
|
|
25
66
|
}
|
|
26
67
|
|
|
68
|
+
export interface OAuthConfig {
|
|
69
|
+
GITHUB_LOGIN_ENABLED?: 'true' | 'false' | 'f';
|
|
70
|
+
GITHUB_CLIENT_ID?: string;
|
|
71
|
+
GITHUB_CLIENT_SECRET?: string;
|
|
72
|
+
GOOGLE_LOGIN_ENABLED?: 'true' | 'false' | 'f';
|
|
73
|
+
GOOGLE_CLIENT_ID?: string;
|
|
74
|
+
GOOGLE_CLIENT_SECRET?: string;
|
|
75
|
+
}
|
|
76
|
+
|
|
27
77
|
// User Types
|
|
28
78
|
export type UserRole = 'SuperAdmin' | 'NormalUser' | 'Guest';
|
|
29
79
|
|
|
@@ -43,7 +93,7 @@ declare module 'mbkauthe' {
|
|
|
43
93
|
UserName?: string;
|
|
44
94
|
role: UserRole;
|
|
45
95
|
Role?: UserRole;
|
|
46
|
-
loginMethod?: 'password' | 'github';
|
|
96
|
+
loginMethod?: 'password' | 'github' | 'google';
|
|
47
97
|
redirectUrl?: string | null;
|
|
48
98
|
}
|
|
49
99
|
|
|
@@ -90,24 +140,14 @@ declare module 'mbkauthe' {
|
|
|
90
140
|
updated_at: Date;
|
|
91
141
|
}
|
|
92
142
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
Role: UserRole;
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
interface Session {
|
|
106
|
-
user?: SessionUser;
|
|
107
|
-
preAuthUser?: PreAuthUser;
|
|
108
|
-
oauthRedirect?: string;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
143
|
+
export interface GoogleUser {
|
|
144
|
+
id: number;
|
|
145
|
+
user_name: string;
|
|
146
|
+
google_id: string;
|
|
147
|
+
google_email: string;
|
|
148
|
+
access_token: string;
|
|
149
|
+
created_at: Date;
|
|
150
|
+
updated_at: Date;
|
|
111
151
|
}
|
|
112
152
|
|
|
113
153
|
// API Response Types
|
|
@@ -177,8 +217,6 @@ declare module 'mbkauthe' {
|
|
|
177
217
|
|
|
178
218
|
export function authenticate(token: string): AuthMiddleware;
|
|
179
219
|
|
|
180
|
-
export function authapi(requiredRole?: UserRole[]): AuthMiddleware;
|
|
181
|
-
|
|
182
220
|
// Utility Functions
|
|
183
221
|
export function renderError(
|
|
184
222
|
res: Response,
|
package/lib/config/index.js
CHANGED
|
@@ -28,6 +28,38 @@ function validateConfiguration() {
|
|
|
28
28
|
throw new Error(`[mbkauthe] Configuration Error:\n - ${errors.join('\n - ')}`);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
// Parse and validate oAuthVar (optional fallback for OAuth settings)
|
|
32
|
+
let oAuthVar = null;
|
|
33
|
+
try {
|
|
34
|
+
if (process.env.oAuthVar) {
|
|
35
|
+
oAuthVar = JSON.parse(process.env.oAuthVar);
|
|
36
|
+
if (oAuthVar && typeof oAuthVar !== 'object') {
|
|
37
|
+
console.warn('[mbkauthe] oAuthVar is not a valid object, ignoring it');
|
|
38
|
+
oAuthVar = null;
|
|
39
|
+
} else {
|
|
40
|
+
console.log('[mbkauthe] oAuthVar detected and parsed successfully');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.warn('[mbkauthe] Invalid JSON in process.env.oAuthVar, ignoring it');
|
|
45
|
+
oAuthVar = null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Merge OAuth settings: use oAuthVar as fallback if values not in mbkautheVar
|
|
49
|
+
const oAuthKeys = [
|
|
50
|
+
'GITHUB_LOGIN_ENABLED', 'GITHUB_CLIENT_ID', 'GITHUB_CLIENT_SECRET',
|
|
51
|
+
'GOOGLE_LOGIN_ENABLED', 'GOOGLE_CLIENT_ID', 'GOOGLE_CLIENT_SECRET'
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
if (oAuthVar) {
|
|
55
|
+
oAuthKeys.forEach(key => {
|
|
56
|
+
if ((!mbkautheVar[key] || (typeof mbkautheVar[key] === 'string' && mbkautheVar[key].trim() === '')) && oAuthVar[key]) {
|
|
57
|
+
mbkautheVar[key] = oAuthVar[key];
|
|
58
|
+
console.log(`[mbkauthe] Using ${key} from oAuthVar`);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
31
63
|
// Validate required keys
|
|
32
64
|
// COOKIE_EXPIRE_TIME is not required but if provided must be valid, COOKIE_EXPIRE_TIME by default is 2 days
|
|
33
65
|
// loginRedirectURL is not required but if provided must be valid, loginRedirectURL by default is /dashboard
|
|
@@ -55,6 +87,11 @@ function validateConfiguration() {
|
|
|
55
87
|
errors.push("mbkautheVar.GITHUB_LOGIN_ENABLED must be either 'true' or 'false' or 'f'");
|
|
56
88
|
}
|
|
57
89
|
|
|
90
|
+
// Validate GOOGLE_LOGIN_ENABLED value
|
|
91
|
+
if (mbkautheVar.GOOGLE_LOGIN_ENABLED && !['true', 'false', 'f'].includes(mbkautheVar.GOOGLE_LOGIN_ENABLED.toLowerCase())) {
|
|
92
|
+
errors.push("mbkautheVar.GOOGLE_LOGIN_ENABLED must be either 'true' or 'false' or 'f'");
|
|
93
|
+
}
|
|
94
|
+
|
|
58
95
|
// Validate EncPass value if provided
|
|
59
96
|
if (mbkautheVar.EncPass && !['true', 'false', 'f'].includes(mbkautheVar.EncPass.toLowerCase())) {
|
|
60
97
|
errors.push("mbkautheVar.EncPass must be either 'true' or 'false' or 'f'");
|
|
@@ -70,6 +107,16 @@ function validateConfiguration() {
|
|
|
70
107
|
}
|
|
71
108
|
}
|
|
72
109
|
|
|
110
|
+
// Validate Google login configuration
|
|
111
|
+
if (mbkautheVar.GOOGLE_LOGIN_ENABLED === "true") {
|
|
112
|
+
if (!mbkautheVar.GOOGLE_CLIENT_ID || mbkautheVar.GOOGLE_CLIENT_ID.trim() === '') {
|
|
113
|
+
errors.push("mbkautheVar.GOOGLE_CLIENT_ID is required when GOOGLE_LOGIN_ENABLED is 'true'");
|
|
114
|
+
}
|
|
115
|
+
if (!mbkautheVar.GOOGLE_CLIENT_SECRET || mbkautheVar.GOOGLE_CLIENT_SECRET.trim() === '') {
|
|
116
|
+
errors.push("mbkautheVar.GOOGLE_CLIENT_SECRET is required when GOOGLE_LOGIN_ENABLED is 'true'");
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
73
120
|
// Validate COOKIE_EXPIRE_TIME if provided
|
|
74
121
|
if (mbkautheVar.COOKIE_EXPIRE_TIME !== undefined) {
|
|
75
122
|
const expireTime = parseFloat(mbkautheVar.COOKIE_EXPIRE_TIME);
|
|
@@ -133,4 +180,4 @@ try {
|
|
|
133
180
|
}
|
|
134
181
|
}
|
|
135
182
|
|
|
136
|
-
export { packageJson, appVersion };
|
|
183
|
+
export { packageJson, appVersion };
|
package/lib/main.js
CHANGED
|
@@ -2,7 +2,6 @@ import express from "express";
|
|
|
2
2
|
import session from "express-session";
|
|
3
3
|
import cookieParser from "cookie-parser";
|
|
4
4
|
import passport from 'passport';
|
|
5
|
-
import { packageJson } from "./config/index.js";
|
|
6
5
|
import {
|
|
7
6
|
sessionConfig,
|
|
8
7
|
corsMiddleware,
|
|
@@ -12,9 +11,26 @@ import {
|
|
|
12
11
|
import authRoutes from "./routes/auth.js";
|
|
13
12
|
import oauthRoutes from "./routes/oauth.js";
|
|
14
13
|
import miscRoutes from "./routes/misc.js";
|
|
14
|
+
import { fileURLToPath } from "url";
|
|
15
|
+
import path from "path";
|
|
16
|
+
|
|
17
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
|
|
15
19
|
|
|
16
20
|
const router = express.Router();
|
|
17
21
|
|
|
22
|
+
// Configure Express to trust proxy headers for rate limiting in dev mode only
|
|
23
|
+
// This prevents conflicts with parent project proxy settings in production
|
|
24
|
+
if (process.env.test === "dev") {
|
|
25
|
+
router.use((req, res, next) => {
|
|
26
|
+
// Set trust proxy to true for the app instance if not already set
|
|
27
|
+
if (!req.app.get('trust proxy')) {
|
|
28
|
+
req.app.set('trust proxy', true);
|
|
29
|
+
}
|
|
30
|
+
next();
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
18
34
|
// Basic middleware
|
|
19
35
|
router.use(express.json());
|
|
20
36
|
router.use(express.urlencoded({ extended: true }));
|
|
@@ -48,5 +64,15 @@ router.get(["/login", "/signin"], async (req, res) => {
|
|
|
48
64
|
return res.redirect(redirectUrl);
|
|
49
65
|
});
|
|
50
66
|
|
|
67
|
+
router.get('/icon.svg', (req, res) => {
|
|
68
|
+
res.setHeader('Cache-Control', 'public, max-age=31536000');
|
|
69
|
+
res.sendFile(path.join(__dirname, '..', 'public', 'icon.svg'));
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
router.get(['/favicon.ico', '/icon.ico'], (req, res) => {
|
|
73
|
+
res.setHeader('Cache-Control', 'public, max-age=31536000');
|
|
74
|
+
res.sendFile(path.join(__dirname, '..', 'public', 'icon.ico'));
|
|
75
|
+
});
|
|
76
|
+
|
|
51
77
|
export { getLatestVersion } from "./routes/misc.js";
|
|
52
78
|
export default router;
|
package/lib/middleware/index.js
CHANGED
|
@@ -18,9 +18,11 @@ export const sessionConfig = {
|
|
|
18
18
|
proxy: true,
|
|
19
19
|
cookie: {
|
|
20
20
|
maxAge: mbkautheVar.COOKIE_EXPIRE_TIME * 24 * 60 * 60 * 1000,
|
|
21
|
-
domain
|
|
21
|
+
// Don't set domain in development/localhost to avoid cookie issues
|
|
22
|
+
domain: (mbkautheVar.IS_DEPLOYED === 'true' && process.env.test !== 'dev') ? `.${mbkautheVar.DOMAIN}` : undefined,
|
|
22
23
|
httpOnly: true,
|
|
23
|
-
secure
|
|
24
|
+
// Only use secure cookies in production with HTTPS
|
|
25
|
+
secure: mbkautheVar.IS_DEPLOYED === 'true' && process.env.test !== 'dev',
|
|
24
26
|
sameSite: 'lax',
|
|
25
27
|
path: '/'
|
|
26
28
|
},
|
package/lib/routes/auth.js
CHANGED
|
@@ -22,19 +22,31 @@ const LoginLimit = rateLimit({
|
|
|
22
22
|
message: { success: false, message: "Too many attempts, please try again later" },
|
|
23
23
|
skip: (req) => {
|
|
24
24
|
return !!req.session.user;
|
|
25
|
+
},
|
|
26
|
+
validate: {
|
|
27
|
+
trustProxy: false,
|
|
28
|
+
xForwardedForHeader: false
|
|
25
29
|
}
|
|
26
30
|
});
|
|
27
31
|
|
|
28
32
|
const LogoutLimit = rateLimit({
|
|
29
33
|
windowMs: 1 * 60 * 1000,
|
|
30
34
|
max: 10,
|
|
31
|
-
message: { success: false, message: "Too many logout attempts, please try again later" }
|
|
35
|
+
message: { success: false, message: "Too many logout attempts, please try again later" },
|
|
36
|
+
validate: {
|
|
37
|
+
trustProxy: false,
|
|
38
|
+
xForwardedForHeader: false
|
|
39
|
+
}
|
|
32
40
|
});
|
|
33
41
|
|
|
34
42
|
const TwoFALimit = rateLimit({
|
|
35
43
|
windowMs: 1 * 60 * 1000,
|
|
36
44
|
max: 5,
|
|
37
|
-
message: { success: false, message: "Too many 2FA attempts, please try again later" }
|
|
45
|
+
message: { success: false, message: "Too many 2FA attempts, please try again later" },
|
|
46
|
+
validate: {
|
|
47
|
+
trustProxy: false,
|
|
48
|
+
xForwardedForHeader: false
|
|
49
|
+
}
|
|
38
50
|
});
|
|
39
51
|
|
|
40
52
|
// CSRF protection middleware
|
|
@@ -117,8 +129,17 @@ export async function completeLoginProcess(req, res, user, redirectUrl = null, t
|
|
|
117
129
|
const sessionId = crypto.randomBytes(32).toString("hex");
|
|
118
130
|
console.log(`[mbkauthe] Generated session ID for username: ${username}`);
|
|
119
131
|
|
|
120
|
-
//
|
|
132
|
+
// Fix session fixation: Delete old session BEFORE regenerating to prevent timing window
|
|
121
133
|
const oldSessionId = req.sessionID;
|
|
134
|
+
|
|
135
|
+
// Delete old session first to prevent session fixation attacks
|
|
136
|
+
await dblogin.query({
|
|
137
|
+
name: 'login-delete-old-session-before-regen',
|
|
138
|
+
text: 'DELETE FROM "session" WHERE sid = $1',
|
|
139
|
+
values: [oldSessionId]
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Now regenerate with new session ID (timing window closed)
|
|
122
143
|
await new Promise((resolve, reject) => {
|
|
123
144
|
req.session.regenerate((err) => {
|
|
124
145
|
if (err) reject(err);
|
|
@@ -509,6 +530,7 @@ router.get("/login", LoginLimit, csrfProtection, (req, res) => {
|
|
|
509
530
|
return res.render("loginmbkauthe.handlebars", {
|
|
510
531
|
layout: false,
|
|
511
532
|
githubLoginEnabled: mbkautheVar.GITHUB_LOGIN_ENABLED,
|
|
533
|
+
googleLoginEnabled: mbkautheVar.GOOGLE_LOGIN_ENABLED,
|
|
512
534
|
customURL: mbkautheVar.loginRedirectURL || '/dashboard',
|
|
513
535
|
userLoggedIn: !!req.session?.user,
|
|
514
536
|
username: req.session?.user?.username || '',
|