mbkauthe 2.0.0 → 2.0.1
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 +1 -10
- package/docs/db.md +0 -6
- package/env.md +68 -0
- package/lib/main.js +273 -257
- package/package.json +1 -1
- package/views/loginmbkauthe.handlebars +5 -1
package/.env.example
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
mbkautheVar=
|
|
2
|
-
"APP_NAME": "MBKAUTH",
|
|
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
|
-
"loginRedirectURL": "/admin"
|
|
10
|
-
}'
|
|
1
|
+
mbkautheVar={"APP_NAME":"mbkauthe","Main_SECRET_TOKEN": 123,"SESSION_SECRET_KEY":"123","IS_DEPLOYED":"true","LOGIN_DB":"postgres://","MBKAUTH_TWO_FA_ENABLE":"true","COOKIE_EXPIRE_TIME":2,"DOMAIN":"mbktech.org","loginRedirectURL":"/mbkauthe/test","GITHUB_LOGIN_ENABLED":"true","GITHUB_CLIENT_ID":"","GITHUB_CLIENT_SECRET":""}
|
|
11
2
|
|
|
12
3
|
# See env.md for more details
|
package/docs/db.md
CHANGED
|
@@ -12,8 +12,6 @@ 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
|
-
GITHUB_LOGIN_CALLBACK_URL=https://yourdomain.com/mbkauthe/api/github/login/callback
|
|
16
|
-
BASE_URL=https://yourdomain.com
|
|
17
15
|
```
|
|
18
16
|
|
|
19
17
|
### 2. GitHub OAuth App Setup
|
|
@@ -109,10 +107,6 @@ The login page now includes:
|
|
|
109
107
|
# Required for GitHub Login
|
|
110
108
|
GITHUB_CLIENT_ID=your_github_client_id
|
|
111
109
|
GITHUB_CLIENT_SECRET=your_github_client_secret
|
|
112
|
-
GITHUB_LOGIN_CALLBACK_URL=https://yourdomain.com/mbkauthe/api/github/login/callback
|
|
113
|
-
|
|
114
|
-
# Optional (used as fallback)
|
|
115
|
-
BASE_URL=https://yourdomain.com
|
|
116
110
|
```
|
|
117
111
|
|
|
118
112
|
The GitHub login feature is now fully integrated into your mbkauthe system and ready to use!
|
package/env.md
CHANGED
|
@@ -138,6 +138,74 @@ COOKIE_EXPIRE_TIME=30 # 1 month (convenience)
|
|
|
138
138
|
|
|
139
139
|
---
|
|
140
140
|
|
|
141
|
+
## 🐙 GitHub OAuth Authentication
|
|
142
|
+
|
|
143
|
+
### GitHub Login Configuration
|
|
144
|
+
```env
|
|
145
|
+
GITHUB_LOGIN_ENABLED=false
|
|
146
|
+
GITHUB_CLIENT_ID=your-github-client-id
|
|
147
|
+
GITHUB_CLIENT_SECRET=your-github-client-secret
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
#### GITHUB_LOGIN_ENABLED
|
|
151
|
+
**Description:** Enables or disables GitHub OAuth login functionality.
|
|
152
|
+
|
|
153
|
+
**Values:**
|
|
154
|
+
- `true` - Enable GitHub login (users can authenticate via GitHub)
|
|
155
|
+
- `false` - Disable GitHub login (default)
|
|
156
|
+
|
|
157
|
+
**Required:** Yes (if using GitHub authentication)
|
|
158
|
+
|
|
159
|
+
#### GITHUB_CLIENT_ID
|
|
160
|
+
**Description:** OAuth application client ID from GitHub.
|
|
161
|
+
|
|
162
|
+
- **Purpose:** Identifies your application to GitHub's OAuth service
|
|
163
|
+
- **Format:** Alphanumeric string provided by GitHub
|
|
164
|
+
- **Setup:** Obtain from [GitHub Developer Settings](https://github.com/settings/developers)
|
|
165
|
+
- **Required:** Yes (when `GITHUB_LOGIN_ENABLED=true`)
|
|
166
|
+
|
|
167
|
+
**Example:** `GITHUB_CLIENT_ID=Iv1.a1b2c3d4e5f6g7h8`
|
|
168
|
+
|
|
169
|
+
#### GITHUB_CLIENT_SECRET
|
|
170
|
+
**Description:** OAuth application client secret from GitHub.
|
|
171
|
+
|
|
172
|
+
- **Purpose:** Authenticates your application with GitHub's OAuth service
|
|
173
|
+
- **Security:** Keep this secret secure and never commit to version control
|
|
174
|
+
- **Format:** Alphanumeric string provided by GitHub
|
|
175
|
+
- **Setup:** Generated when creating OAuth app in GitHub Developer Settings
|
|
176
|
+
- **Required:** Yes (when `GITHUB_LOGIN_ENABLED=true`)
|
|
177
|
+
|
|
178
|
+
**Example:** `GITHUB_CLIENT_SECRET=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0`
|
|
179
|
+
|
|
180
|
+
### Setting Up GitHub OAuth
|
|
181
|
+
|
|
182
|
+
1. **Create GitHub OAuth App:**
|
|
183
|
+
- Go to [GitHub Developer Settings](https://github.com/settings/developers)
|
|
184
|
+
- Click "New OAuth App"
|
|
185
|
+
- Fill in application details:
|
|
186
|
+
- **Application name:** Your app name
|
|
187
|
+
- **Homepage URL:** `https://yourdomain.com` (or `http://localhost:3000` for dev)
|
|
188
|
+
- **Authorization callback URL:** `https://yourdomain.com/auth/github/callback`
|
|
189
|
+
- Click "Register application"
|
|
190
|
+
|
|
191
|
+
2. **Copy Credentials:**
|
|
192
|
+
- Copy the **Client ID**
|
|
193
|
+
- Generate and copy the **Client Secret**
|
|
194
|
+
|
|
195
|
+
3. **Configure Environment:**
|
|
196
|
+
```env
|
|
197
|
+
GITHUB_LOGIN_ENABLED=true
|
|
198
|
+
GITHUB_CLIENT_ID=your-copied-client-id
|
|
199
|
+
GITHUB_CLIENT_SECRET=your-copied-client-secret
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Security Notes:**
|
|
203
|
+
- Use separate OAuth apps for development and production environments
|
|
204
|
+
- Rotate client secrets periodically
|
|
205
|
+
- Never expose client secrets in client-side code
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
141
209
|
## 🚀 Quick Setup Examples
|
|
142
210
|
|
|
143
211
|
### Development Environment
|
package/lib/main.js
CHANGED
|
@@ -46,8 +46,8 @@ router.use((req, res, next) => {
|
|
|
46
46
|
const originUrl = new URL(origin);
|
|
47
47
|
const allowedDomain = `.${mbkautheVar.DOMAIN}`;
|
|
48
48
|
// Exact match or subdomain match (must end with .domain.com, not just domain.com)
|
|
49
|
-
if (originUrl.hostname === mbkautheVar.DOMAIN ||
|
|
50
|
-
|
|
49
|
+
if (originUrl.hostname === mbkautheVar.DOMAIN ||
|
|
50
|
+
(originUrl.hostname.endsWith(allowedDomain) && originUrl.hostname.charAt(originUrl.hostname.length - allowedDomain.length - 1) !== '.')) {
|
|
51
51
|
res.header('Access-Control-Allow-Origin', origin);
|
|
52
52
|
res.header('Access-Control-Allow-Credentials', 'true');
|
|
53
53
|
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
|
|
@@ -162,8 +162,8 @@ const getClearCookieOptions = () => ({
|
|
|
162
162
|
httpOnly: true
|
|
163
163
|
});
|
|
164
164
|
|
|
165
|
-
router.get('/mbkauthe/test', validateSession, LoginLimit, async(req, res) => {
|
|
166
|
-
if(req.session?.user) {
|
|
165
|
+
router.get('/mbkauthe/test', validateSession, LoginLimit, async (req, res) => {
|
|
166
|
+
if (req.session?.user) {
|
|
167
167
|
return res.send(`
|
|
168
168
|
<head> <script src="/mbkauthe/main.js"></script> </head>
|
|
169
169
|
<p>if you are seeing this page than User is logged in.</p>
|
|
@@ -197,16 +197,18 @@ async function completeLoginProcess(req, res, user, redirectUrl = null) {
|
|
|
197
197
|
});
|
|
198
198
|
|
|
199
199
|
// Delete all old sessions for this user from session table
|
|
200
|
-
await dblogin.query({
|
|
201
|
-
name: 'login-delete-old-user-sessions',
|
|
202
|
-
text: 'DELETE FROM "session" WHERE sess::text LIKE $1',
|
|
203
|
-
values: [`%"username":"${username}"%`]
|
|
200
|
+
await dblogin.query({
|
|
201
|
+
name: 'login-delete-old-user-sessions',
|
|
202
|
+
text: 'DELETE FROM "session" WHERE sess::text LIKE $1',
|
|
203
|
+
values: [`%"username":"${username}"%`]
|
|
204
204
|
});
|
|
205
205
|
|
|
206
|
-
await dblogin.query({
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
206
|
+
await dblogin.query({
|
|
207
|
+
name: 'login-update-session-id', text: `UPDATE "Users" SET "SessionId" = $1 WHERE "id" = $2`, values: [
|
|
208
|
+
sessionId,
|
|
209
|
+
user.id,
|
|
210
|
+
]
|
|
211
|
+
});
|
|
210
212
|
|
|
211
213
|
req.session.user = {
|
|
212
214
|
id: user.id,
|
|
@@ -541,11 +543,11 @@ router.get(["/mbkauthe/info", "/mbkauthe/i"], LoginLimit, async (req, res) => {
|
|
|
541
543
|
let latestVersion;
|
|
542
544
|
const parameters = req.query;
|
|
543
545
|
let authorized = false;
|
|
544
|
-
|
|
546
|
+
|
|
545
547
|
if (parameters.password && mbkautheVar.Main_SECRET_TOKEN) {
|
|
546
548
|
authorized = String(parameters.password) === String(mbkautheVar.Main_SECRET_TOKEN);
|
|
547
549
|
}
|
|
548
|
-
|
|
550
|
+
|
|
549
551
|
try {
|
|
550
552
|
latestVersion = await getLatestVersion();
|
|
551
553
|
//latestVersion = "Under Development"; // Placeholder for the latest version
|
|
@@ -579,69 +581,69 @@ router.get(["/mbkauthe/info", "/mbkauthe/i"], LoginLimit, async (req, res) => {
|
|
|
579
581
|
|
|
580
582
|
// Configure GitHub Strategy for login
|
|
581
583
|
passport.use('github-login', new GitHubStrategy({
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
584
|
+
clientID: mbkautheVar.GITHUB_CLIENT_ID,
|
|
585
|
+
clientSecret: mbkautheVar.GITHUB_CLIENT_SECRET,
|
|
586
|
+
callbackURL: '/mbkauthe/api/github/login/callback',
|
|
587
|
+
scope: ['user:email']
|
|
586
588
|
},
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
589
|
+
async (accessToken, refreshToken, profile, done) => {
|
|
590
|
+
try {
|
|
591
|
+
// Check if this GitHub account is linked to any user
|
|
592
|
+
const githubUser = await dblogin.query({
|
|
593
|
+
name: 'github-login-get-user',
|
|
594
|
+
text: 'SELECT ug.*, u."UserName", u."Role", u."Active", u."AllowedApps", u."id" FROM user_github ug JOIN "Users" u ON ug.user_name = u."UserName" WHERE ug.github_id = $1',
|
|
595
|
+
values: [profile.id]
|
|
596
|
+
});
|
|
595
597
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
// Return user data for login
|
|
623
|
-
return done(null, {
|
|
624
|
-
id: user.id, // This should be the user ID from the Users table
|
|
625
|
-
username: user.UserName,
|
|
626
|
-
role: user.Role,
|
|
627
|
-
githubId: user.github_id,
|
|
628
|
-
githubUsername: user.github_username
|
|
629
|
-
});
|
|
630
|
-
} catch (err) {
|
|
631
|
-
console.error('[mbkauthe] GitHub login error:', err);
|
|
632
|
-
err.code = err.code || 'GITHUB_AUTH_ERROR';
|
|
633
|
-
return done(err);
|
|
598
|
+
if (githubUser.rows.length === 0) {
|
|
599
|
+
// GitHub account is not linked to any user
|
|
600
|
+
const error = new Error('GitHub account not linked to any user');
|
|
601
|
+
error.code = 'GITHUB_NOT_LINKED';
|
|
602
|
+
return done(error);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
const user = githubUser.rows[0];
|
|
606
|
+
|
|
607
|
+
// Check if the user account is active
|
|
608
|
+
if (!user.Active) {
|
|
609
|
+
const error = new Error('Account is inactive');
|
|
610
|
+
error.code = 'ACCOUNT_INACTIVE';
|
|
611
|
+
return done(error);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// Check if user is authorized for this app (same logic as regular login)
|
|
615
|
+
if (user.Role !== "SuperAdmin") {
|
|
616
|
+
const allowedApps = user.AllowedApps;
|
|
617
|
+
if (!allowedApps || !allowedApps.some(app => app.toLowerCase() === mbkautheVar.APP_NAME.toLowerCase())) {
|
|
618
|
+
const error = new Error(`Not authorized to use ${mbkautheVar.APP_NAME}`);
|
|
619
|
+
error.code = 'NOT_AUTHORIZED';
|
|
620
|
+
return done(error);
|
|
634
621
|
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// Return user data for login
|
|
625
|
+
return done(null, {
|
|
626
|
+
id: user.id, // This should be the user ID from the Users table
|
|
627
|
+
username: user.UserName,
|
|
628
|
+
role: user.Role,
|
|
629
|
+
githubId: user.github_id,
|
|
630
|
+
githubUsername: user.github_username
|
|
631
|
+
});
|
|
632
|
+
} catch (err) {
|
|
633
|
+
console.error('[mbkauthe] GitHub login error:', err);
|
|
634
|
+
err.code = err.code || 'GITHUB_AUTH_ERROR';
|
|
635
|
+
return done(err);
|
|
635
636
|
}
|
|
637
|
+
}
|
|
636
638
|
));
|
|
637
639
|
|
|
638
640
|
// Serialize/Deserialize user for GitHub login
|
|
639
641
|
passport.serializeUser((user, done) => {
|
|
640
|
-
|
|
642
|
+
done(null, user);
|
|
641
643
|
});
|
|
642
644
|
|
|
643
645
|
passport.deserializeUser((user, done) => {
|
|
644
|
-
|
|
646
|
+
done(null, user);
|
|
645
647
|
});
|
|
646
648
|
|
|
647
649
|
// Initialize passport
|
|
@@ -650,212 +652,226 @@ router.use(passport.session());
|
|
|
650
652
|
|
|
651
653
|
// GitHub login initiation
|
|
652
654
|
router.get('/mbkauthe/api/github/login', GitHubOAuthLimit, (req, res, next) => {
|
|
655
|
+
if (mbkautheVar.GITHUB_LOGIN_ENABLED) {
|
|
653
656
|
// Store redirect parameter in session before OAuth flow (validate to prevent open redirect)
|
|
654
657
|
const redirect = req.query.redirect;
|
|
655
658
|
if (redirect && typeof redirect === 'string') {
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
659
|
+
// Only allow relative URLs or same-origin URLs to prevent open redirect attacks
|
|
660
|
+
if (redirect.startsWith('/') && !redirect.startsWith('//')) {
|
|
661
|
+
req.session.oauthRedirect = redirect;
|
|
662
|
+
} else {
|
|
663
|
+
console.warn(`[mbkauthe] Invalid redirect parameter rejected: ${redirect}`);
|
|
664
|
+
}
|
|
662
665
|
}
|
|
663
666
|
passport.authenticate('github-login')(req, res, next);
|
|
667
|
+
}
|
|
668
|
+
else {
|
|
669
|
+
res.status(403).render('Error/dError.handlebars', {
|
|
670
|
+
layout: false,
|
|
671
|
+
code: '403',
|
|
672
|
+
error: 'GitHub Login Disabled',
|
|
673
|
+
message: 'GitHub login is currently disabled. Please use your username and password to log in.',
|
|
674
|
+
page: '/mbkauthe/login',
|
|
675
|
+
pagename: 'Login',
|
|
676
|
+
version: packageJson.version,
|
|
677
|
+
app: mbkautheVar.APP_NAME
|
|
678
|
+
});
|
|
679
|
+
}
|
|
664
680
|
});
|
|
665
681
|
|
|
666
682
|
// GitHub login callback
|
|
667
683
|
router.get('/mbkauthe/api/github/login/callback',
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
});
|
|
702
|
-
|
|
703
|
-
case 'NOT_AUTHORIZED':
|
|
704
|
-
return res.status(403).render('Error/dError.handlebars', {
|
|
705
|
-
layout: false,
|
|
706
|
-
code: '403',
|
|
707
|
-
error: 'Not Authorized',
|
|
708
|
-
message: `You are not authorized to access ${mbkautheVar.APP_NAME}. Please contact your administrator.`,
|
|
709
|
-
page: '/mbkauthe/login',
|
|
710
|
-
pagename: 'Login',
|
|
711
|
-
version: packageJson.version,
|
|
712
|
-
app: mbkautheVar.APP_NAME
|
|
713
|
-
});
|
|
714
|
-
|
|
715
|
-
default:
|
|
716
|
-
return res.status(500).render('Error/dError.handlebars', {
|
|
717
|
-
layout: false,
|
|
718
|
-
code: '500',
|
|
719
|
-
error: 'Authentication Error',
|
|
720
|
-
message: 'An error occurred during GitHub authentication. Please try again.',
|
|
721
|
-
page: '/mbkauthe/login',
|
|
722
|
-
pagename: 'Login',
|
|
723
|
-
version: packageJson.version,
|
|
724
|
-
app: mbkautheVar.APP_NAME,
|
|
725
|
-
details: process.env.NODE_ENV === 'development' ? `${err.message}\n${err.stack}` : 'Error details hidden in production'
|
|
726
|
-
});
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
if (!user) {
|
|
731
|
-
console.error('[mbkauthe] GitHub callback: No user data received');
|
|
732
|
-
return res.status(401).render('Error/dError.handlebars', {
|
|
733
|
-
layout: false,
|
|
734
|
-
code: '401',
|
|
735
|
-
error: 'Authentication Failed',
|
|
736
|
-
message: 'GitHub authentication failed. Please try again.',
|
|
737
|
-
page: '/mbkauthe/login',
|
|
738
|
-
pagename: 'Login',
|
|
739
|
-
version: packageJson.version,
|
|
740
|
-
app: mbkautheVar.APP_NAME
|
|
741
|
-
});
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
// Authentication successful, attach user to request
|
|
745
|
-
req.user = user;
|
|
746
|
-
next();
|
|
747
|
-
})(req, res, next);
|
|
748
|
-
},
|
|
749
|
-
async (req, res) => {
|
|
750
|
-
try {
|
|
751
|
-
const githubUser = req.user;
|
|
752
|
-
|
|
753
|
-
// Find the actual user record with named query
|
|
754
|
-
const userQuery = `SELECT id, "UserName", "Active", "Role", "AllowedApps" FROM "Users" WHERE "UserName" = $1`;
|
|
755
|
-
const userResult = await dblogin.query({
|
|
756
|
-
name: 'github-callback-get-user',
|
|
757
|
-
text: userQuery,
|
|
758
|
-
values: [githubUser.username]
|
|
684
|
+
GitHubOAuthLimit,
|
|
685
|
+
(req, res, next) => {
|
|
686
|
+
passport.authenticate('github-login', {
|
|
687
|
+
session: false // We'll handle session manually
|
|
688
|
+
}, (err, user, info) => {
|
|
689
|
+
// Custom error handling for passport authentication
|
|
690
|
+
if (err) {
|
|
691
|
+
console.error('[mbkauthe] GitHub authentication error:', err);
|
|
692
|
+
|
|
693
|
+
// Map error codes to user-friendly messages
|
|
694
|
+
switch (err.code) {
|
|
695
|
+
case 'GITHUB_NOT_LINKED':
|
|
696
|
+
return res.status(403).render('Error/dError.handlebars', {
|
|
697
|
+
layout: false,
|
|
698
|
+
code: '403',
|
|
699
|
+
error: 'GitHub Account Not Linked',
|
|
700
|
+
message: 'Your GitHub account is not linked to any user in our system. To link your GitHub account, a User must connect their GitHub account to mbktech account through the user settings.',
|
|
701
|
+
page: '/mbkauthe/login',
|
|
702
|
+
pagename: 'Login',
|
|
703
|
+
version: packageJson.version,
|
|
704
|
+
app: mbkautheVar.APP_NAME
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
case 'ACCOUNT_INACTIVE':
|
|
708
|
+
return res.status(403).render('Error/dError.handlebars', {
|
|
709
|
+
layout: false,
|
|
710
|
+
code: '403',
|
|
711
|
+
error: 'Account Inactive',
|
|
712
|
+
message: 'Your account has been deactivated. Please contact your administrator.',
|
|
713
|
+
page: '/mbkauthe/login',
|
|
714
|
+
pagename: 'Login',
|
|
715
|
+
version: packageJson.version,
|
|
716
|
+
app: mbkautheVar.APP_NAME
|
|
759
717
|
});
|
|
760
718
|
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
const user = userResult.rows[0];
|
|
777
|
-
|
|
778
|
-
// Check 2FA if enabled
|
|
779
|
-
if ((mbkautheVar.MBKAUTH_TWO_FA_ENABLE || "").toLowerCase() === "true") {
|
|
780
|
-
const twoFAQuery = `SELECT "TwoFAStatus" FROM "TwoFA" WHERE "UserName" = $1`;
|
|
781
|
-
const twoFAResult = await dblogin.query({
|
|
782
|
-
name: 'github-check-2fa-status',
|
|
783
|
-
text: twoFAQuery,
|
|
784
|
-
values: [githubUser.username]
|
|
785
|
-
});
|
|
786
|
-
|
|
787
|
-
if (twoFAResult.rows.length > 0 && twoFAResult.rows[0].TwoFAStatus) {
|
|
788
|
-
// 2FA is enabled, store pre-auth user and redirect to 2FA
|
|
789
|
-
req.session.preAuthUser = {
|
|
790
|
-
id: user.id,
|
|
791
|
-
username: user.UserName,
|
|
792
|
-
UserName: user.UserName,
|
|
793
|
-
role: user.Role,
|
|
794
|
-
Role: user.Role,
|
|
795
|
-
loginMethod: 'github'
|
|
796
|
-
};
|
|
797
|
-
console.log(`[mbkauthe] GitHub login: 2FA required for user: ${githubUser.username}`);
|
|
798
|
-
return res.redirect('/mbkauthe/2fa');
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
// Complete login process using the shared function
|
|
803
|
-
const userForSession = {
|
|
804
|
-
id: user.id,
|
|
805
|
-
username: user.UserName,
|
|
806
|
-
UserName: user.UserName,
|
|
807
|
-
role: user.Role,
|
|
808
|
-
Role: user.Role
|
|
809
|
-
};
|
|
810
|
-
|
|
811
|
-
// For OAuth redirect flow, we need to handle redirect differently
|
|
812
|
-
// Store the redirect URL before calling completeLoginProcess
|
|
813
|
-
const oauthRedirect = req.session.oauthRedirect;
|
|
814
|
-
delete req.session.oauthRedirect;
|
|
815
|
-
|
|
816
|
-
// Custom response handler for OAuth flow - wrap the response object
|
|
817
|
-
const originalJson = res.json.bind(res);
|
|
818
|
-
const originalStatus = res.status.bind(res);
|
|
819
|
-
let statusCode = 200;
|
|
820
|
-
|
|
821
|
-
res.status = function(code) {
|
|
822
|
-
statusCode = code;
|
|
823
|
-
return originalStatus(code);
|
|
824
|
-
};
|
|
825
|
-
|
|
826
|
-
res.json = function(data) {
|
|
827
|
-
if (data.success && statusCode === 200) {
|
|
828
|
-
// If login successful, redirect instead of sending JSON
|
|
829
|
-
const redirectUrl = oauthRedirect || mbkautheVar.loginRedirectURL || '/home';
|
|
830
|
-
console.log(`[mbkauthe] GitHub login: Redirecting to ${redirectUrl}`);
|
|
831
|
-
// Restore original methods before redirect
|
|
832
|
-
res.json = originalJson;
|
|
833
|
-
res.status = originalStatus;
|
|
834
|
-
return res.redirect(redirectUrl);
|
|
835
|
-
}
|
|
836
|
-
// Restore original methods for error responses
|
|
837
|
-
res.json = originalJson;
|
|
838
|
-
res.status = originalStatus;
|
|
839
|
-
return originalJson(data);
|
|
840
|
-
};
|
|
841
|
-
|
|
842
|
-
await completeLoginProcess(req, res, userForSession);
|
|
843
|
-
|
|
844
|
-
} catch (err) {
|
|
845
|
-
console.error('[mbkauthe] GitHub login callback error:', err);
|
|
719
|
+
case 'NOT_AUTHORIZED':
|
|
720
|
+
return res.status(403).render('Error/dError.handlebars', {
|
|
721
|
+
layout: false,
|
|
722
|
+
code: '403',
|
|
723
|
+
error: 'Not Authorized',
|
|
724
|
+
message: `You are not authorized to access ${mbkautheVar.APP_NAME}. Please contact your administrator.`,
|
|
725
|
+
page: '/mbkauthe/login',
|
|
726
|
+
pagename: 'Login',
|
|
727
|
+
version: packageJson.version,
|
|
728
|
+
app: mbkautheVar.APP_NAME
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
default:
|
|
846
732
|
return res.status(500).render('Error/dError.handlebars', {
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
733
|
+
layout: false,
|
|
734
|
+
code: '500',
|
|
735
|
+
error: 'Authentication Error',
|
|
736
|
+
message: 'An error occurred during GitHub authentication. Please try again.',
|
|
737
|
+
page: '/mbkauthe/login',
|
|
738
|
+
pagename: 'Login',
|
|
739
|
+
version: packageJson.version,
|
|
740
|
+
app: mbkautheVar.APP_NAME,
|
|
741
|
+
details: process.env.NODE_ENV === 'development' ? `${err.message}\n${err.stack}` : 'Error details hidden in production'
|
|
856
742
|
});
|
|
857
743
|
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
if (!user) {
|
|
747
|
+
console.error('[mbkauthe] GitHub callback: No user data received');
|
|
748
|
+
return res.status(401).render('Error/dError.handlebars', {
|
|
749
|
+
layout: false,
|
|
750
|
+
code: '401',
|
|
751
|
+
error: 'Authentication Failed',
|
|
752
|
+
message: 'GitHub authentication failed. Please try again.',
|
|
753
|
+
page: '/mbkauthe/login',
|
|
754
|
+
pagename: 'Login',
|
|
755
|
+
version: packageJson.version,
|
|
756
|
+
app: mbkautheVar.APP_NAME
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
// Authentication successful, attach user to request
|
|
761
|
+
req.user = user;
|
|
762
|
+
next();
|
|
763
|
+
})(req, res, next);
|
|
764
|
+
},
|
|
765
|
+
async (req, res) => {
|
|
766
|
+
try {
|
|
767
|
+
const githubUser = req.user;
|
|
768
|
+
|
|
769
|
+
// Find the actual user record with named query
|
|
770
|
+
const userQuery = `SELECT id, "UserName", "Active", "Role", "AllowedApps" FROM "Users" WHERE "UserName" = $1`;
|
|
771
|
+
const userResult = await dblogin.query({
|
|
772
|
+
name: 'github-callback-get-user',
|
|
773
|
+
text: userQuery,
|
|
774
|
+
values: [githubUser.username]
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
if (userResult.rows.length === 0) {
|
|
778
|
+
console.error(`[mbkauthe] GitHub login: User not found: ${githubUser.username}`);
|
|
779
|
+
return res.status(404).render('Error/dError.handlebars', {
|
|
780
|
+
layout: false,
|
|
781
|
+
code: '404',
|
|
782
|
+
error: 'User Not Found',
|
|
783
|
+
message: 'Your GitHub account is linked, but the user account no longer exists in our system.',
|
|
784
|
+
page: '/mbkauthe/login',
|
|
785
|
+
pagename: 'Login',
|
|
786
|
+
version: packageJson.version,
|
|
787
|
+
app: mbkautheVar.APP_NAME,
|
|
788
|
+
details: `GitHub username: ${githubUser.username}\nPlease contact your administrator.`
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
const user = userResult.rows[0];
|
|
793
|
+
|
|
794
|
+
// Check 2FA if enabled
|
|
795
|
+
if ((mbkautheVar.MBKAUTH_TWO_FA_ENABLE || "").toLowerCase() === "true") {
|
|
796
|
+
const twoFAQuery = `SELECT "TwoFAStatus" FROM "TwoFA" WHERE "UserName" = $1`;
|
|
797
|
+
const twoFAResult = await dblogin.query({
|
|
798
|
+
name: 'github-check-2fa-status',
|
|
799
|
+
text: twoFAQuery,
|
|
800
|
+
values: [githubUser.username]
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
if (twoFAResult.rows.length > 0 && twoFAResult.rows[0].TwoFAStatus) {
|
|
804
|
+
// 2FA is enabled, store pre-auth user and redirect to 2FA
|
|
805
|
+
req.session.preAuthUser = {
|
|
806
|
+
id: user.id,
|
|
807
|
+
username: user.UserName,
|
|
808
|
+
UserName: user.UserName,
|
|
809
|
+
role: user.Role,
|
|
810
|
+
Role: user.Role,
|
|
811
|
+
loginMethod: 'github'
|
|
812
|
+
};
|
|
813
|
+
console.log(`[mbkauthe] GitHub login: 2FA required for user: ${githubUser.username}`);
|
|
814
|
+
return res.redirect('/mbkauthe/2fa');
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
// Complete login process using the shared function
|
|
819
|
+
const userForSession = {
|
|
820
|
+
id: user.id,
|
|
821
|
+
username: user.UserName,
|
|
822
|
+
UserName: user.UserName,
|
|
823
|
+
role: user.Role,
|
|
824
|
+
Role: user.Role
|
|
825
|
+
};
|
|
826
|
+
|
|
827
|
+
// For OAuth redirect flow, we need to handle redirect differently
|
|
828
|
+
// Store the redirect URL before calling completeLoginProcess
|
|
829
|
+
const oauthRedirect = req.session.oauthRedirect;
|
|
830
|
+
delete req.session.oauthRedirect;
|
|
831
|
+
|
|
832
|
+
// Custom response handler for OAuth flow - wrap the response object
|
|
833
|
+
const originalJson = res.json.bind(res);
|
|
834
|
+
const originalStatus = res.status.bind(res);
|
|
835
|
+
let statusCode = 200;
|
|
836
|
+
|
|
837
|
+
res.status = function (code) {
|
|
838
|
+
statusCode = code;
|
|
839
|
+
return originalStatus(code);
|
|
840
|
+
};
|
|
841
|
+
|
|
842
|
+
res.json = function (data) {
|
|
843
|
+
if (data.success && statusCode === 200) {
|
|
844
|
+
// If login successful, redirect instead of sending JSON
|
|
845
|
+
const redirectUrl = oauthRedirect || mbkautheVar.loginRedirectURL || '/home';
|
|
846
|
+
console.log(`[mbkauthe] GitHub login: Redirecting to ${redirectUrl}`);
|
|
847
|
+
// Restore original methods before redirect
|
|
848
|
+
res.json = originalJson;
|
|
849
|
+
res.status = originalStatus;
|
|
850
|
+
return res.redirect(redirectUrl);
|
|
851
|
+
}
|
|
852
|
+
// Restore original methods for error responses
|
|
853
|
+
res.json = originalJson;
|
|
854
|
+
res.status = originalStatus;
|
|
855
|
+
return originalJson(data);
|
|
856
|
+
};
|
|
857
|
+
|
|
858
|
+
await completeLoginProcess(req, res, userForSession);
|
|
859
|
+
|
|
860
|
+
} catch (err) {
|
|
861
|
+
console.error('[mbkauthe] GitHub login callback error:', err);
|
|
862
|
+
return res.status(500).render('Error/dError.handlebars', {
|
|
863
|
+
layout: false,
|
|
864
|
+
code: '500',
|
|
865
|
+
error: 'Internal Server Error',
|
|
866
|
+
message: 'An error occurred during GitHub authentication. Please try again.',
|
|
867
|
+
page: '/mbkauthe/login',
|
|
868
|
+
pagename: 'Login',
|
|
869
|
+
version: packageJson.version,
|
|
870
|
+
app: mbkautheVar.APP_NAME,
|
|
871
|
+
details: process.env.NODE_ENV === 'development' ? `${err.message}\n${err.stack}` : 'Error details hidden in production'
|
|
872
|
+
});
|
|
858
873
|
}
|
|
874
|
+
}
|
|
859
875
|
);
|
|
860
876
|
|
|
861
877
|
export { getLatestVersion };
|
package/package.json
CHANGED
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
<button type="submit" class="btn-login" id="loginButton">
|
|
69
69
|
<span id="loginButtonText">Login</span>
|
|
70
70
|
</button>
|
|
71
|
+
{{#if githubLoginEnabled }}
|
|
71
72
|
<div class="social-login">
|
|
72
73
|
<div class="divider">
|
|
73
74
|
<span>or</span>
|
|
@@ -78,7 +79,7 @@
|
|
|
78
79
|
<span>Continue with GitHub</span>
|
|
79
80
|
</a>
|
|
80
81
|
</div>
|
|
81
|
-
|
|
82
|
+
{{/if }}
|
|
82
83
|
<div class="remember-me">
|
|
83
84
|
<input type="checkbox" id="rememberMe" name="rememberMe">
|
|
84
85
|
<label for="rememberMe">Remember me</label>
|
|
@@ -272,6 +273,8 @@
|
|
|
272
273
|
}
|
|
273
274
|
});
|
|
274
275
|
|
|
276
|
+
{{#if githubLoginEnabled }}
|
|
277
|
+
|
|
275
278
|
// GitHub login: Attempt to POST redirect to backend, fallback to direct navigation
|
|
276
279
|
async function startGithubLogin() {
|
|
277
280
|
const urlParams = new URLSearchParams(window.location.search);
|
|
@@ -310,6 +313,7 @@
|
|
|
310
313
|
|
|
311
314
|
const githubBtn = document.getElementById('githubLoginBtn');
|
|
312
315
|
if (githubBtn) githubBtn.addEventListener('click', startGithubLogin);
|
|
316
|
+
{{/if }}
|
|
313
317
|
</script>
|
|
314
318
|
</body>
|
|
315
319
|
|