mbkauthe 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.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
@@ -15,6 +15,7 @@ import passport from 'passport';
15
15
  import GitHubStrategy from 'passport-github2';
16
16
 
17
17
  import { createRequire } from "module";
18
+ import { fileURLToPath } from "url";
18
19
  import fs from "fs";
19
20
  import path from "path";
20
21
 
@@ -31,8 +32,22 @@ router.use(express.json());
31
32
  router.use(express.urlencoded({ extended: true }));
32
33
  router.use(cookieParser());
33
34
 
35
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
36
+
34
37
  router.get('/mbkauthe/main.js', (req, res) => {
35
- res.sendFile(path.join(process.cwd(), 'public', 'main.js'));
38
+ res.sendFile(path.join(__dirname, '..', 'public', 'main.js'));
39
+ });
40
+
41
+ router.get("/mbkauthe/bg.avif", (req, res) => {
42
+ const imgPath = path.join(__dirname, "..", "public", "bg.avif");
43
+ res.setHeader('Content-Type', 'image/avif');
44
+ res.setHeader('Cache-Control', 'public, max-age=31536000');
45
+ const stream = fs.createReadStream(imgPath);
46
+ stream.on('error', (err) => {
47
+ console.error('[mbkauthe] Error streaming bg.avif:', err);
48
+ res.status(404).send('Image not found');
49
+ });
50
+ stream.pipe(res);
36
51
  });
37
52
 
38
53
  // CSRF protection middleware
@@ -46,8 +61,8 @@ router.use((req, res, next) => {
46
61
  const originUrl = new URL(origin);
47
62
  const allowedDomain = `.${mbkautheVar.DOMAIN}`;
48
63
  // Exact match or subdomain match (must end with .domain.com, not just domain.com)
49
- if (originUrl.hostname === mbkautheVar.DOMAIN ||
50
- (originUrl.hostname.endsWith(allowedDomain) && originUrl.hostname.charAt(originUrl.hostname.length - allowedDomain.length - 1) !== '.')) {
64
+ if (originUrl.hostname === mbkautheVar.DOMAIN ||
65
+ (originUrl.hostname.endsWith(allowedDomain) && originUrl.hostname.charAt(originUrl.hostname.length - allowedDomain.length - 1) !== '.')) {
51
66
  res.header('Access-Control-Allow-Origin', origin);
52
67
  res.header('Access-Control-Allow-Credentials', 'true');
53
68
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
@@ -162,8 +177,8 @@ const getClearCookieOptions = () => ({
162
177
  httpOnly: true
163
178
  });
164
179
 
165
- router.get('/mbkauthe/test', validateSession, LoginLimit, async(req, res) => {
166
- if(req.session?.user) {
180
+ router.get('/mbkauthe/test', validateSession, LoginLimit, async (req, res) => {
181
+ if (req.session?.user) {
167
182
  return res.send(`
168
183
  <head> <script src="/mbkauthe/main.js"></script> </head>
169
184
  <p>if you are seeing this page than User is logged in.</p>
@@ -197,16 +212,18 @@ async function completeLoginProcess(req, res, user, redirectUrl = null) {
197
212
  });
198
213
 
199
214
  // 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}"%`]
215
+ await dblogin.query({
216
+ name: 'login-delete-old-user-sessions',
217
+ text: 'DELETE FROM "session" WHERE sess::text LIKE $1',
218
+ values: [`%"username":"${username}"%`]
204
219
  });
205
220
 
206
- await dblogin.query({ name: 'login-update-session-id', text: `UPDATE "Users" SET "SessionId" = $1 WHERE "id" = $2`, values: [
207
- sessionId,
208
- user.id,
209
- ] });
221
+ await dblogin.query({
222
+ name: 'login-update-session-id', text: `UPDATE "Users" SET "SessionId" = $1 WHERE "id" = $2`, values: [
223
+ sessionId,
224
+ user.id,
225
+ ]
226
+ });
210
227
 
211
228
  req.session.user = {
212
229
  id: user.id,
@@ -533,19 +550,15 @@ async function getLatestVersion() {
533
550
  }
534
551
  }
535
552
 
536
- router.get("/mbkauthe/bg.avif", (req, res) => {
537
- res.sendFile("bg.avif", { root: path.join(process.cwd(), "public") });
538
- });
539
-
540
553
  router.get(["/mbkauthe/info", "/mbkauthe/i"], LoginLimit, async (req, res) => {
541
554
  let latestVersion;
542
555
  const parameters = req.query;
543
556
  let authorized = false;
544
-
557
+
545
558
  if (parameters.password && mbkautheVar.Main_SECRET_TOKEN) {
546
559
  authorized = String(parameters.password) === String(mbkautheVar.Main_SECRET_TOKEN);
547
560
  }
548
-
561
+
549
562
  try {
550
563
  latestVersion = await getLatestVersion();
551
564
  //latestVersion = "Under Development"; // Placeholder for the latest version
@@ -579,69 +592,69 @@ router.get(["/mbkauthe/info", "/mbkauthe/i"], LoginLimit, async (req, res) => {
579
592
 
580
593
  // Configure GitHub Strategy for login
581
594
  passport.use('github-login', new GitHubStrategy({
582
- clientID: process.env.GITHUB_CLIENT_ID,
583
- clientSecret: process.env.GITHUB_CLIENT_SECRET,
584
- callbackURL: '/mbkauthe/api/github/login/callback',
585
- scope: ['user:email']
595
+ clientID: mbkautheVar.GITHUB_CLIENT_ID,
596
+ clientSecret: mbkautheVar.GITHUB_CLIENT_SECRET,
597
+ callbackURL: '/mbkauthe/api/github/login/callback',
598
+ scope: ['user:email']
586
599
  },
587
- async (accessToken, refreshToken, profile, done) => {
588
- try {
589
- // Check if this GitHub account is linked to any user
590
- const githubUser = await dblogin.query({
591
- name: 'github-login-get-user',
592
- 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',
593
- values: [profile.id]
594
- });
600
+ async (accessToken, refreshToken, profile, done) => {
601
+ try {
602
+ // Check if this GitHub account is linked to any user
603
+ const githubUser = await dblogin.query({
604
+ name: 'github-login-get-user',
605
+ 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',
606
+ values: [profile.id]
607
+ });
595
608
 
596
- if (githubUser.rows.length === 0) {
597
- // GitHub account is not linked to any user
598
- const error = new Error('GitHub account not linked to any user');
599
- error.code = 'GITHUB_NOT_LINKED';
600
- return done(error);
601
- }
602
-
603
- const user = githubUser.rows[0];
604
-
605
- // Check if the user account is active
606
- if (!user.Active) {
607
- const error = new Error('Account is inactive');
608
- error.code = 'ACCOUNT_INACTIVE';
609
- return done(error);
610
- }
611
-
612
- // Check if user is authorized for this app (same logic as regular login)
613
- if (user.Role !== "SuperAdmin") {
614
- const allowedApps = user.AllowedApps;
615
- if (!allowedApps || !allowedApps.some(app => app.toLowerCase() === mbkautheVar.APP_NAME.toLowerCase())) {
616
- const error = new Error(`Not authorized to use ${mbkautheVar.APP_NAME}`);
617
- error.code = 'NOT_AUTHORIZED';
618
- return done(error);
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);
609
+ if (githubUser.rows.length === 0) {
610
+ // GitHub account is not linked to any user
611
+ const error = new Error('GitHub account not linked to any user');
612
+ error.code = 'GITHUB_NOT_LINKED';
613
+ return done(error);
614
+ }
615
+
616
+ const user = githubUser.rows[0];
617
+
618
+ // Check if the user account is active
619
+ if (!user.Active) {
620
+ const error = new Error('Account is inactive');
621
+ error.code = 'ACCOUNT_INACTIVE';
622
+ return done(error);
623
+ }
624
+
625
+ // Check if user is authorized for this app (same logic as regular login)
626
+ if (user.Role !== "SuperAdmin") {
627
+ const allowedApps = user.AllowedApps;
628
+ if (!allowedApps || !allowedApps.some(app => app.toLowerCase() === mbkautheVar.APP_NAME.toLowerCase())) {
629
+ const error = new Error(`Not authorized to use ${mbkautheVar.APP_NAME}`);
630
+ error.code = 'NOT_AUTHORIZED';
631
+ return done(error);
634
632
  }
633
+ }
634
+
635
+ // Return user data for login
636
+ return done(null, {
637
+ id: user.id, // This should be the user ID from the Users table
638
+ username: user.UserName,
639
+ role: user.Role,
640
+ githubId: user.github_id,
641
+ githubUsername: user.github_username
642
+ });
643
+ } catch (err) {
644
+ console.error('[mbkauthe] GitHub login error:', err);
645
+ err.code = err.code || 'GITHUB_AUTH_ERROR';
646
+ return done(err);
635
647
  }
648
+ }
636
649
  ));
637
650
 
638
651
  // Serialize/Deserialize user for GitHub login
639
652
  passport.serializeUser((user, done) => {
640
- done(null, user);
653
+ done(null, user);
641
654
  });
642
655
 
643
656
  passport.deserializeUser((user, done) => {
644
- done(null, user);
657
+ done(null, user);
645
658
  });
646
659
 
647
660
  // Initialize passport
@@ -650,212 +663,226 @@ router.use(passport.session());
650
663
 
651
664
  // GitHub login initiation
652
665
  router.get('/mbkauthe/api/github/login', GitHubOAuthLimit, (req, res, next) => {
666
+ if (mbkautheVar.GITHUB_LOGIN_ENABLED) {
653
667
  // Store redirect parameter in session before OAuth flow (validate to prevent open redirect)
654
668
  const redirect = req.query.redirect;
655
669
  if (redirect && typeof redirect === 'string') {
656
- // Only allow relative URLs or same-origin URLs to prevent open redirect attacks
657
- if (redirect.startsWith('/') && !redirect.startsWith('//')) {
658
- req.session.oauthRedirect = redirect;
659
- } else {
660
- console.warn(`[mbkauthe] Invalid redirect parameter rejected: ${redirect}`);
661
- }
670
+ // Only allow relative URLs or same-origin URLs to prevent open redirect attacks
671
+ if (redirect.startsWith('/') && !redirect.startsWith('//')) {
672
+ req.session.oauthRedirect = redirect;
673
+ } else {
674
+ console.warn(`[mbkauthe] Invalid redirect parameter rejected: ${redirect}`);
675
+ }
662
676
  }
663
677
  passport.authenticate('github-login')(req, res, next);
678
+ }
679
+ else {
680
+ res.status(403).render('Error/dError.handlebars', {
681
+ layout: false,
682
+ code: '403',
683
+ error: 'GitHub Login Disabled',
684
+ message: 'GitHub login is currently disabled. Please use your username and password to log in.',
685
+ page: '/mbkauthe/login',
686
+ pagename: 'Login',
687
+ version: packageJson.version,
688
+ app: mbkautheVar.APP_NAME
689
+ });
690
+ }
664
691
  });
665
692
 
666
693
  // GitHub login callback
667
694
  router.get('/mbkauthe/api/github/login/callback',
668
- GitHubOAuthLimit,
669
- (req, res, next) => {
670
- passport.authenticate('github-login', {
671
- session: false // We'll handle session manually
672
- }, (err, user, info) => {
673
- // Custom error handling for passport authentication
674
- if (err) {
675
- console.error('[mbkauthe] GitHub authentication error:', err);
676
-
677
- // Map error codes to user-friendly messages
678
- switch(err.code) {
679
- case 'GITHUB_NOT_LINKED':
680
- return res.status(403).render('Error/dError.handlebars', {
681
- layout: false,
682
- code: '403',
683
- error: 'GitHub Account Not Linked',
684
- 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.',
685
- page: '/mbkauthe/login',
686
- pagename: 'Login',
687
- version: packageJson.version,
688
- app: mbkautheVar.APP_NAME
689
- });
690
-
691
- case 'ACCOUNT_INACTIVE':
692
- return res.status(403).render('Error/dError.handlebars', {
693
- layout: false,
694
- code: '403',
695
- error: 'Account Inactive',
696
- message: 'Your account has been deactivated. Please contact your administrator.',
697
- page: '/mbkauthe/login',
698
- pagename: 'Login',
699
- version: packageJson.version,
700
- app: mbkautheVar.APP_NAME
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]
695
+ GitHubOAuthLimit,
696
+ (req, res, next) => {
697
+ passport.authenticate('github-login', {
698
+ session: false // We'll handle session manually
699
+ }, (err, user, info) => {
700
+ // Custom error handling for passport authentication
701
+ if (err) {
702
+ console.error('[mbkauthe] GitHub authentication error:', err);
703
+
704
+ // Map error codes to user-friendly messages
705
+ switch (err.code) {
706
+ case 'GITHUB_NOT_LINKED':
707
+ return res.status(403).render('Error/dError.handlebars', {
708
+ layout: false,
709
+ code: '403',
710
+ error: 'GitHub Account Not Linked',
711
+ 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.',
712
+ page: '/mbkauthe/login',
713
+ pagename: 'Login',
714
+ version: packageJson.version,
715
+ app: mbkautheVar.APP_NAME
716
+ });
717
+
718
+ case 'ACCOUNT_INACTIVE':
719
+ return res.status(403).render('Error/dError.handlebars', {
720
+ layout: false,
721
+ code: '403',
722
+ error: 'Account Inactive',
723
+ message: 'Your account has been deactivated. Please contact your administrator.',
724
+ page: '/mbkauthe/login',
725
+ pagename: 'Login',
726
+ version: packageJson.version,
727
+ app: mbkautheVar.APP_NAME
759
728
  });
760
729
 
761
- if (userResult.rows.length === 0) {
762
- console.error(`[mbkauthe] GitHub login: User not found: ${githubUser.username}`);
763
- return res.status(404).render('Error/dError.handlebars', {
764
- layout: false,
765
- code: '404',
766
- error: 'User Not Found',
767
- message: 'Your GitHub account is linked, but the user account no longer exists in our system.',
768
- page: '/mbkauthe/login',
769
- pagename: 'Login',
770
- version: packageJson.version,
771
- app: mbkautheVar.APP_NAME,
772
- details: `GitHub username: ${githubUser.username}\nPlease contact your administrator.`
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);
730
+ case 'NOT_AUTHORIZED':
731
+ return res.status(403).render('Error/dError.handlebars', {
732
+ layout: false,
733
+ code: '403',
734
+ error: 'Not Authorized',
735
+ message: `You are not authorized to access ${mbkautheVar.APP_NAME}. Please contact your administrator.`,
736
+ page: '/mbkauthe/login',
737
+ pagename: 'Login',
738
+ version: packageJson.version,
739
+ app: mbkautheVar.APP_NAME
740
+ });
741
+
742
+ default:
846
743
  return res.status(500).render('Error/dError.handlebars', {
847
- layout: false,
848
- code: '500',
849
- error: 'Internal Server Error',
850
- message: 'An error occurred during GitHub authentication. Please try again.',
851
- page: '/mbkauthe/login',
852
- pagename: 'Login',
853
- version: packageJson.version,
854
- app: mbkautheVar.APP_NAME,
855
- details: process.env.NODE_ENV === 'development' ? `${err.message}\n${err.stack}` : 'Error details hidden in production'
744
+ layout: false,
745
+ code: '500',
746
+ error: 'Authentication Error',
747
+ message: 'An error occurred during GitHub authentication. Please try again.',
748
+ page: '/mbkauthe/login',
749
+ pagename: 'Login',
750
+ version: packageJson.version,
751
+ app: mbkautheVar.APP_NAME,
752
+ details: process.env.NODE_ENV === 'development' ? `${err.message}\n${err.stack}` : 'Error details hidden in production'
856
753
  });
857
754
  }
755
+ }
756
+
757
+ if (!user) {
758
+ console.error('[mbkauthe] GitHub callback: No user data received');
759
+ return res.status(401).render('Error/dError.handlebars', {
760
+ layout: false,
761
+ code: '401',
762
+ error: 'Authentication Failed',
763
+ message: 'GitHub authentication failed. Please try again.',
764
+ page: '/mbkauthe/login',
765
+ pagename: 'Login',
766
+ version: packageJson.version,
767
+ app: mbkautheVar.APP_NAME
768
+ });
769
+ }
770
+
771
+ // Authentication successful, attach user to request
772
+ req.user = user;
773
+ next();
774
+ })(req, res, next);
775
+ },
776
+ async (req, res) => {
777
+ try {
778
+ const githubUser = req.user;
779
+
780
+ // Find the actual user record with named query
781
+ const userQuery = `SELECT id, "UserName", "Active", "Role", "AllowedApps" FROM "Users" WHERE "UserName" = $1`;
782
+ const userResult = await dblogin.query({
783
+ name: 'github-callback-get-user',
784
+ text: userQuery,
785
+ values: [githubUser.username]
786
+ });
787
+
788
+ if (userResult.rows.length === 0) {
789
+ console.error(`[mbkauthe] GitHub login: User not found: ${githubUser.username}`);
790
+ return res.status(404).render('Error/dError.handlebars', {
791
+ layout: false,
792
+ code: '404',
793
+ error: 'User Not Found',
794
+ message: 'Your GitHub account is linked, but the user account no longer exists in our system.',
795
+ page: '/mbkauthe/login',
796
+ pagename: 'Login',
797
+ version: packageJson.version,
798
+ app: mbkautheVar.APP_NAME,
799
+ details: `GitHub username: ${githubUser.username}\nPlease contact your administrator.`
800
+ });
801
+ }
802
+
803
+ const user = userResult.rows[0];
804
+
805
+ // Check 2FA if enabled
806
+ if ((mbkautheVar.MBKAUTH_TWO_FA_ENABLE || "").toLowerCase() === "true") {
807
+ const twoFAQuery = `SELECT "TwoFAStatus" FROM "TwoFA" WHERE "UserName" = $1`;
808
+ const twoFAResult = await dblogin.query({
809
+ name: 'github-check-2fa-status',
810
+ text: twoFAQuery,
811
+ values: [githubUser.username]
812
+ });
813
+
814
+ if (twoFAResult.rows.length > 0 && twoFAResult.rows[0].TwoFAStatus) {
815
+ // 2FA is enabled, store pre-auth user and redirect to 2FA
816
+ req.session.preAuthUser = {
817
+ id: user.id,
818
+ username: user.UserName,
819
+ UserName: user.UserName,
820
+ role: user.Role,
821
+ Role: user.Role,
822
+ loginMethod: 'github'
823
+ };
824
+ console.log(`[mbkauthe] GitHub login: 2FA required for user: ${githubUser.username}`);
825
+ return res.redirect('/mbkauthe/2fa');
826
+ }
827
+ }
828
+
829
+ // Complete login process using the shared function
830
+ const userForSession = {
831
+ id: user.id,
832
+ username: user.UserName,
833
+ UserName: user.UserName,
834
+ role: user.Role,
835
+ Role: user.Role
836
+ };
837
+
838
+ // For OAuth redirect flow, we need to handle redirect differently
839
+ // Store the redirect URL before calling completeLoginProcess
840
+ const oauthRedirect = req.session.oauthRedirect;
841
+ delete req.session.oauthRedirect;
842
+
843
+ // Custom response handler for OAuth flow - wrap the response object
844
+ const originalJson = res.json.bind(res);
845
+ const originalStatus = res.status.bind(res);
846
+ let statusCode = 200;
847
+
848
+ res.status = function (code) {
849
+ statusCode = code;
850
+ return originalStatus(code);
851
+ };
852
+
853
+ res.json = function (data) {
854
+ if (data.success && statusCode === 200) {
855
+ // If login successful, redirect instead of sending JSON
856
+ const redirectUrl = oauthRedirect || mbkautheVar.loginRedirectURL || '/home';
857
+ console.log(`[mbkauthe] GitHub login: Redirecting to ${redirectUrl}`);
858
+ // Restore original methods before redirect
859
+ res.json = originalJson;
860
+ res.status = originalStatus;
861
+ return res.redirect(redirectUrl);
862
+ }
863
+ // Restore original methods for error responses
864
+ res.json = originalJson;
865
+ res.status = originalStatus;
866
+ return originalJson(data);
867
+ };
868
+
869
+ await completeLoginProcess(req, res, userForSession);
870
+
871
+ } catch (err) {
872
+ console.error('[mbkauthe] GitHub login callback error:', err);
873
+ return res.status(500).render('Error/dError.handlebars', {
874
+ layout: false,
875
+ code: '500',
876
+ error: 'Internal Server Error',
877
+ message: 'An error occurred during GitHub authentication. Please try again.',
878
+ page: '/mbkauthe/login',
879
+ pagename: 'Login',
880
+ version: packageJson.version,
881
+ app: mbkautheVar.APP_NAME,
882
+ details: process.env.NODE_ENV === 'development' ? `${err.message}\n${err.stack}` : 'Error details hidden in production'
883
+ });
858
884
  }
885
+ }
859
886
  );
860
887
 
861
888
  export { getLatestVersion };
package/lib/pool.js CHANGED
@@ -36,9 +36,9 @@ const poolConfig = {
36
36
  // - keep max small to avoid exhausting DB connections
37
37
  // - reduce idle time so connections are returned sooner
38
38
  // - set a short connection timeout to fail fast
39
- max: 20,
39
+ max: 10,
40
40
  idleTimeoutMillis: 10000,
41
- connectionTimeoutMillis: 5000,
41
+ connectionTimeoutMillis: 25000,
42
42
  };
43
43
 
44
44
  export const dblogin = new Pool(poolConfig);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mbkauthe",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "MBKTech's reusable authentication system for Node.js applications.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -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