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 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
- (originUrl.hostname.endsWith(allowedDomain) && originUrl.hostname.charAt(originUrl.hostname.length - allowedDomain.length - 1) !== '.')) {
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({ name: 'login-update-session-id', text: `UPDATE "Users" SET "SessionId" = $1 WHERE "id" = $2`, values: [
207
- sessionId,
208
- user.id,
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
- clientID: process.env.GITHUB_CLIENT_ID,
583
- clientSecret: process.env.GITHUB_CLIENT_SECRET,
584
- callbackURL: '/mbkauthe/api/github/login/callback',
585
- scope: ['user:email']
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
- 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
- });
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
- 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);
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
- done(null, user);
642
+ done(null, user);
641
643
  });
642
644
 
643
645
  passport.deserializeUser((user, done) => {
644
- done(null, user);
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
- // 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
- }
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
- 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]
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
- 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);
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
- 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'
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mbkauthe",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
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