mbkauthe 1.2.1 → 1.3.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 CHANGED
@@ -14,9 +14,7 @@
14
14
  - [Middleware Function Documentation](#middleware-function-documentation)
15
15
  - [validateSession(session)](#validatesessionsession)
16
16
  - [checkRolePermission(userRole, requiredRoles)](#checkrolepermissionuserrole-requiredroles)
17
- - [validateSessionAndRole(session, userRole, requiredRoles)](#validatesessionandrolesession-userrole-requiredroles)
18
- - [getUserData(session)](#getuserdatasession)
19
- - [authenticate(session)](#authenticatesession)
17
+ - [validateSessionAndRole(session, userRole, requiredRoles)]
20
18
  - [API Endpoints](#api-endpoints)
21
19
  - [Login](#login)
22
20
  - [Logout](#logout)
@@ -155,17 +153,6 @@ router.get(["/admin"], validateSessionAndRole("SuperAdmin"), (req, res) => {
155
153
  ```
156
154
  ---
157
155
 
158
- ### `getUserData(session)`
159
- Retrieves user data based on the session.
160
-
161
- - **Parameters:**
162
- - `session` (Object): The session object containing user information.
163
-
164
- - **Returns:**
165
- - `Object|null`: Returns the user data object if found, otherwise `null`.
166
-
167
- ---
168
-
169
156
  ### `authenticate(session)`
170
157
  Authenticates the user by validating the session and retrieving user data.
171
158
 
package/docs/db.md CHANGED
@@ -1,3 +1,127 @@
1
+ # GitHub Login Setup Guide
2
+
3
+ ## Overview
4
+ This GitHub login feature allows users to authenticate using their GitHub account if it's already linked to their account in the system. Users must first connect their GitHub account through the regular account linking process, then they can use GitHub to log in directly.
5
+
6
+ ## Setup Instructions
7
+
8
+ ### 1. Environment Variables
9
+ Add these to your `.env` file:
10
+
11
+ ```env
12
+ # GitHub OAuth App Configuration
13
+ GITHUB_CLIENT_ID=your_github_client_id
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
+ ```
18
+
19
+ ### 2. GitHub OAuth App Setup
20
+ 1. Go to GitHub Settings > Developer settings > OAuth Apps
21
+ 2. Create a new OAuth App
22
+ 3. Set the Authorization callback URL to: `https://yourdomain.com/mbkauthe/api/github/login/callback`
23
+ 4. Copy the Client ID and Client Secret to your `.env` file
24
+
25
+ ### 3. Database Schema
26
+ Ensure your `user_github` table exists with these columns:
27
+
28
+ ```sql
29
+ CREATE TABLE user_github (
30
+ id SERIAL PRIMARY KEY,
31
+ user_name VARCHAR(255) REFERENCES "Users"("UserName"),
32
+ github_id VARCHAR(255) UNIQUE NOT NULL,
33
+ github_username VARCHAR(255),
34
+ access_token TEXT,
35
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
36
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
37
+ );
38
+ ```
39
+
40
+ ## How It Works
41
+
42
+ ### Login Flow
43
+ 1. User clicks "Login with GitHub" on the login page
44
+ 2. User is redirected to GitHub for authentication
45
+ 3. GitHub redirects back to `/mbkauthe/api/github/login/callback`
46
+ 4. System checks if the GitHub ID exists in `user_github` table
47
+ 5. If found and user is active/authorized:
48
+ - If 2FA is enabled, redirect to 2FA page
49
+ - If no 2FA, complete login and redirect to home
50
+ 6. If not found, redirect to login page with error
51
+
52
+ ### Account Linking
53
+ Users must first link their GitHub account through your existing GitHub connection system (likely in user settings) before they can use GitHub login.
54
+
55
+ ## API Routes Added
56
+
57
+ ### `/mbkauthe/api/github/login`
58
+ - **Method**: GET
59
+ - **Description**: Initiates GitHub OAuth flow
60
+ - **Redirects to**: GitHub authorization page
61
+
62
+ ### `/mbkauthe/api/github/login/callback`
63
+ - **Method**: GET
64
+ - **Description**: Handles GitHub OAuth callback
65
+ - **Parameters**: `code` (from GitHub), `state` (optional)
66
+ - **Success**: Redirects to home page or configured redirect URL
67
+ - **Error**: Redirects to login page with error parameter
68
+
69
+ ## Error Handling
70
+
71
+ The system handles various error cases:
72
+ - `github_auth_failed`: GitHub OAuth failed
73
+ - `user_not_found`: GitHub account not linked to any user
74
+ - `session_error`: Session save failed
75
+ - `internal_error`: General server error
76
+
77
+ ## Testing
78
+
79
+ 1. Create a test user in your `Users` table
80
+ 2. Link a GitHub account to that user using your existing connection system
81
+ 3. Try logging in with GitHub using the new login button
82
+ 4. Check console logs for debugging information
83
+
84
+ ## Login Page Updates
85
+
86
+ The login page now includes:
87
+ - A "Continue with GitHub" button
88
+ - A divider ("or") between regular and GitHub login
89
+ - Proper styling that matches your existing design
90
+
91
+ ## Security Notes
92
+
93
+ - Only users with active accounts can log in
94
+ - App authorization is checked (same as regular login)
95
+ - 2FA is respected if enabled
96
+ - Session management is handled the same way as regular login
97
+ - GitHub access tokens are stored securely
98
+
99
+ ## Troubleshooting
100
+
101
+ 1. **GitHub OAuth errors**: Check your GitHub OAuth app configuration
102
+ 2. **Database errors**: Ensure `user_github` table exists and has proper relationships
103
+ 3. **Session errors**: Check your session configuration
104
+ 4. **2FA issues**: Verify 2FA table structure and configuration
105
+
106
+ ## Environment Variables Summary
107
+
108
+ ```env
109
+ # Required for GitHub Login
110
+ GITHUB_CLIENT_ID=your_github_client_id
111
+ 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
+ ```
117
+
118
+ The GitHub login feature is now fully integrated into your mbkauthe system and ready to use!
119
+
120
+
121
+
122
+
123
+
124
+
1
125
  ## Database structure
2
126
 
3
127
  [<- Back](README.md)
package/index.js CHANGED
@@ -44,8 +44,8 @@ if (process.env.test === "true") {
44
44
  }
45
45
 
46
46
  app.set("views", [
47
- path.join(__dirname, "views"),
48
- path.join(__dirname, "node_modules/mbkauthe/views")
47
+ path.join(__dirname, "views"),
48
+ path.join(__dirname, "node_modules/mbkauthe/views")
49
49
  ]);
50
50
 
51
51
  app.engine("handlebars", engine({
@@ -55,6 +55,28 @@ app.engine("handlebars", engine({
55
55
  path.join(__dirname, "node_modules/mbkauthe/views"),
56
56
  path.join(__dirname, "node_modules/mbkauthe/views/Error"),
57
57
  ],
58
+ helpers: {
59
+ eq: function (a, b) {
60
+ return a === b;
61
+ },
62
+ encodeURIComponent: function (str) {
63
+ return encodeURIComponent(str);
64
+ },
65
+ formatTimestamp: function (timestamp) {
66
+ return new Date(timestamp).toLocaleString();
67
+ },
68
+ jsonStringify: function (context) {
69
+ return JSON.stringify(context);
70
+ },
71
+ json: (obj) => JSON.stringify(obj, null, 2),
72
+ objectEntries: function (obj) {
73
+ if (!obj || typeof obj !== 'object') {
74
+ return []; // Return an empty array if obj is undefined, null, or not an object
75
+ }
76
+ return Object.entries(obj).map(([key, value]) => ({ key, value }));
77
+ }
78
+ }
79
+
58
80
  }));
59
81
 
60
82
  app.set("view engine", "handlebars");
@@ -63,10 +85,10 @@ import { createRequire } from "module";
63
85
  const require = createRequire(import.meta.url);
64
86
  const packageJson = require("./package.json");
65
87
  const latestVersion = await getLatestVersion();
66
- if(latestVersion !== packageJson.version) {
88
+ if (latestVersion !== packageJson.version) {
67
89
  console.warn(`[mbkauthe] Warning: The current version (${packageJson.version}) is not the latest version (${latestVersion}). Please update mbkauthe.`);
68
90
  }
69
91
 
70
- export { validateSession, checkRolePermission, validateSessionAndRole, getUserData, authenticate, authapi } from "./lib/validateSessionAndRole.js";
92
+ export { validateSession, checkRolePermission, validateSessionAndRole, authenticate, authapi } from "./lib/validateSessionAndRole.js";
71
93
  export { dblogin } from "./lib/pool.js";
72
94
  export default router;
package/lib/main.js CHANGED
@@ -11,6 +11,8 @@ import cookieParser from "cookie-parser";
11
11
  import bcrypt from 'bcrypt';
12
12
  import rateLimit from 'express-rate-limit';
13
13
  import speakeasy from "speakeasy";
14
+ //import passport from 'passport';
15
+ //import GitHubStrategy from 'passport-github2';
14
16
 
15
17
  import { createRequire } from "module";
16
18
  import fs from "fs";
@@ -383,6 +385,7 @@ router.post("/mbkauthe/api/logout", async (req, res) => {
383
385
  router.get("/mbkauthe/login", LoginLimit, csrfProtection, (req, res) => {
384
386
  return res.render("loginmbkauthe.handlebars", {
385
387
  layout: false,
388
+ githubLoginEnabled: mbkautheVar.GITHUB_LOGIN_ENABLED,
386
389
  customURL: mbkautheVar.loginRedirectURL || '/home',
387
390
  userLoggedIn: !!req.session?.user,
388
391
  username: req.session?.user?.username || '',
@@ -396,7 +399,8 @@ async function getLatestVersion() {
396
399
  try {
397
400
  const response = await fetch('https://raw.githubusercontent.com/MIbnEKhalid/mbkauthe/main/package.json');
398
401
  if (!response.ok) {
399
- throw new Error(`GitHub API responded with status ${response.status}`);
402
+ console.Error(`GitHub API responded with status ${response.status}`);
403
+ return "0.0.0";
400
404
  }
401
405
  const latestPackageJson = await response.json();
402
406
  return latestPackageJson.version;
@@ -479,382 +483,16 @@ router.get(["/mbkauthe/info", "/mbkauthe/i"], LoginLimit, async (_, res) => {
479
483
  console.error("[mbkauthe] Error fetching package-lock.json:", err);
480
484
  pkgl = { error: "Failed to fetch package-lock.json" };
481
485
  }
482
-
486
+ console.log(pkgl);
483
487
  try {
484
- res.status(200).send(`
485
- <html>
486
- <head>
487
- <title>Version and Configuration Information</title>
488
- <link rel="icon" type="image/x-icon" href="https://mbktechstudio.com/Assets/Images/Icon/dgicon.svg">
489
-
490
- <style>
491
- :root {
492
- --bg-color: #121212;
493
- --card-bg: #1e1e1e;
494
- --text-color: #e0e0e0;
495
- --text-secondary: #a0a0a0;
496
- --primary: #bb86fc;
497
- --primary-dark: #3700b3;
498
- --secondary: #03dac6;
499
- --border-color: #333;
500
- --success: #4caf50;
501
- --warning: #ff9800;
502
- --error: #f44336;
503
- --key-color: #bb86fc;
504
- --string-color: #03dac6;
505
- --number-color: #ff7043;
506
- --boolean-color: #7986cb;
507
- }
508
-
509
- body {
510
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
511
- margin: 0;
512
- padding: 20px;
513
- background-color: var(--bg-color);
514
- color: var(--text-color);
515
- }
516
-
517
- .container {
518
- max-width: 1000px;
519
- margin: 0 auto;
520
- padding: 20px;
521
- background: var(--card-bg);
522
- border-radius: 8px;
523
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
524
- }
525
-
526
- h1 {
527
- color: var(--primary);
528
- text-align: center;
529
- margin-bottom: 30px;
530
- padding-bottom: 10px;
531
- border-bottom: 1px solid var(--border-color);
532
- font-weight: bold;
533
- letter-spacing: 1px;
534
- }
535
-
536
- .info-section {
537
- margin-bottom: 25px;
538
- padding: 20px;
539
- border: 1px solid var(--border-color);
540
- border-radius: 8px;
541
- background-color: rgba(30, 30, 30, 0.7);
542
- transition: all 0.3s ease;
543
- }
544
-
545
- .info-section:hover {
546
- border-color: var(--primary);
547
- box-shadow: 0 0 0 1px var(--primary);
548
- }
549
-
550
- .info-section h2 {
551
- color: var(--primary);
552
- border-bottom: 2px solid var(--primary-dark);
553
- padding-bottom: 8px;
554
- margin-top: 0;
555
- margin-bottom: 15px;
556
- font-size: 1.2em;
557
- display: flex;
558
- justify-content: space-between;
559
- align-items: center;
560
- }
561
-
562
- .info-row {
563
- display: flex;
564
- margin-bottom: 10px;
565
- padding-bottom: 10px;
566
- border-bottom: 1px solid var(--border-color);
567
- }
568
-
569
- .info-label {
570
- font-weight: 600;
571
- color: var(--text-secondary);
572
- min-width: 220px;
573
- font-size: 0.95em;
574
- }
575
-
576
- .info-value {
577
- flex: 1;
578
- word-break: break-word;
579
- color: var(--text-color);
580
- }
581
-
582
- .json-container {
583
- background: #252525;
584
- border: 1px solid var(--border-color);
585
- border-radius: 6px;
586
- padding: 12px;
587
- margin-top: 10px;
588
- max-height: 400px;
589
- overflow: auto;
590
- font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;
591
- font-size: 0.85em;
592
- white-space: pre-wrap;
593
- position: relative;
594
- }
595
-
596
- .json-container pre {
597
- margin: 0;
598
- font-family: inherit;
599
- }
600
-
601
- .json-container .key {
602
- color: var(--key-color);
603
- }
604
-
605
- .json-container .string {
606
- color: var(--string-color);
607
- }
608
-
609
- .json-container .number {
610
- color: var(--number-color);
611
- }
612
-
613
- .json-container .boolean {
614
- color: var(--boolean-color);
615
- }
616
-
617
- .json-container .null {
618
- color: var(--boolean-color);
619
- opacity: 0.7;
620
- }
621
-
622
- .version-status {
623
- display: inline-block;
624
- padding: 3px 10px;
625
- border-radius: 12px;
626
- font-size: 0.8em;
627
- font-weight: 600;
628
- margin-left: 10px;
629
- }
630
-
631
- .version-up-to-date {
632
- background: rgba(76, 175, 80, 0.2);
633
- color: var(--success);
634
- border: 1px solid var(--success);
635
- }
636
-
637
- .version-outdated {
638
- background: rgba(244, 67, 54, 0.2);
639
- color: var(--error);
640
- border: 1px solid var(--error);
641
- }
642
-
643
- .version-fetch-error {
644
- background: rgba(255, 152, 0, 0.2);
645
- color: var(--warning);
646
- border: 1px solid var(--warning);
647
- }
648
-
649
- .copy-btn {
650
- background: var(--primary-dark);
651
- color: white;
652
- border: none;
653
- padding: 5px 12px;
654
- border-radius: 4px;
655
- cursor: pointer;
656
- font-size: 0.8em;
657
- transition: all 0.2s ease;
658
- display: flex;
659
- align-items: center;
660
- gap: 5px;
661
- }
662
-
663
- .copy-btn:hover {
664
- background: var(--primary);
665
- transform: translateY(-1px);
666
- }
667
-
668
- .copy-btn:active {
669
- transform: translateY(0);
670
- }
671
-
672
- /* Scrollbar styling */
673
- ::-webkit-scrollbar {
674
- width: 8px;
675
- height: 8px;
676
- }
677
-
678
- ::-webkit-scrollbar-track {
679
- background: #2d2d2d;
680
- border-radius: 4px;
681
- }
682
-
683
- ::-webkit-scrollbar-thumb {
684
- background: #555;
685
- border-radius: 4px;
686
- }
687
-
688
- ::-webkit-scrollbar-thumb:hover {
689
- background: var(--primary);
690
- }
691
-
692
- /* Tooltip for copy button */
693
- .tooltip {
694
- position: relative;
695
- display: inline-block;
696
- }
697
-
698
- .tooltip .tooltiptext {
699
- visibility: hidden;
700
- width: 120px;
701
- background-color: #333;
702
- color: #fff;
703
- text-align: center;
704
- border-radius: 6px;
705
- padding: 5px;
706
- position: absolute;
707
- z-index: 1;
708
- bottom: 125%;
709
- left: 50%;
710
- margin-left: -60px;
711
- opacity: 0;
712
- transition: opacity 0.3s;
713
- font-size: 0.8em;
714
- }
715
-
716
- .tooltip:hover .tooltiptext {
717
- visibility: visible;
718
- opacity: 1;
719
- }
720
- </style>
721
- <link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
722
- </head>
723
-
724
- <body>
725
- <div class="container">
726
- <h1>Version and Configuration Dashboard</h1>
727
-
728
- <div class="info-section">
729
- <h2>Version Information</h2>
730
- <div class="info-row">
731
- <div class="info-label">Current Version:</div>
732
- <div class="info-value" id="CurrentVersion">${packageJson.version}</div>
733
- </div>
734
- <div class="info-row">
735
- <div class="info-label">Latest Version:</div>
736
- <div class="info-value">
737
- ${latestVersion || 'Could not fetch latest version'}
738
- ${latestVersion ? `
739
- <span class="version-status ${packageJson.version === latestVersion ? 'version-up-to-date' : 'version-outdated'}">
740
- ${packageJson.version === latestVersion ? 'Up to date' : 'Update available'}
741
- </span>
742
- ` : `
743
- <span class="version-status version-fetch-error">
744
- Fetch error
745
- </span>
746
- `}
747
- </div>
748
- </div>
749
- </div>
750
-
751
- <div class="info-section">
752
- <h2>Configuration Information</h2>
753
- <div class="info-row">
754
- <div class="info-label">APP_NAME:</div>
755
- <div class="info-value">${mbkautheVar.APP_NAME}</div>
756
- </div>
757
- <div class="info-row">
758
- <div class="info-label">MBKAUTH_TWO_FA_ENABLE:</div>
759
- <div class="info-value">${mbkautheVar.MBKAUTH_TWO_FA_ENABLE}</div>
760
- </div>
761
- <div class="info-row">
762
- <div class="info-label">COOKIE_EXPIRE_TIME:</div>
763
- <div class="info-value">${mbkautheVar.COOKIE_EXPIRE_TIME} Days</div>
764
- </div>
765
- <div class="info-row">
766
- <div class="info-label">IS_DEPLOYED:</div>
767
- <div class="info-value">${mbkautheVar.IS_DEPLOYED}</div>
768
- </div>
769
- <div class="info-row">
770
- <div class="info-label">DOMAIN:</div>
771
- <div class="info-value">${mbkautheVar.DOMAIN}</div>
772
- </div>
773
- <div class="info-row">
774
- <div class="info-label">Login Redirect URL:</div>
775
- <div class="info-value">${mbkautheVar.loginRedirectURL}</div>
776
- </div>
777
- </div>
778
-
779
- <div class="info-section">
780
- <h2>
781
- Package Information
782
- <button class="copy-btn tooltip" onclick="copyToClipboard('package-json')">
783
- <span class="tooltiptext">Copy to clipboard</span>
784
- Copy JSON
785
- </button>
786
- </h2>
787
- <div id="package-json" class="json-container"><pre>${JSON.stringify(packageJson, null, 2)}</pre></div>
788
- </div>
789
-
790
- <div class="info-section">
791
- <h2>
792
- Package Lock
793
- <button class="copy-btn tooltip" onclick="copyToClipboard('package-lock')">
794
- <span class="tooltiptext">Copy to clipboard</span>
795
- Copy JSON
796
- </button>
797
- </h2>
798
- <div id="package-lock" class="json-container"><pre>${JSON.stringify(pkgl, null, 2)}</pre></div>
799
- </div>
800
- </div>
801
-
802
- <script>
803
- document.addEventListener('DOMContentLoaded', function() {
804
- // Apply syntax highlighting to all JSON containers
805
- const jsonContainers = document.querySelectorAll('.json-container pre');
806
- jsonContainers.forEach(container => {
807
- container.innerHTML = syntaxHighlight(container.textContent);
808
- });
809
- });
810
-
811
- function syntaxHighlight(json) {
812
- if (typeof json !== 'string') {
813
- json = JSON.stringify(json, null, 2);
814
- }
815
-
816
- // Escape HTML
817
- json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
818
-
819
- // Apply syntax highlighting
820
- return json.replace(
821
- /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,
822
- function(match) {
823
- let cls = 'number';
824
- if (/^"/.test(match)) {
825
- if (/:$/.test(match)) {
826
- cls = 'key';
827
- } else {
828
- cls = 'string';
829
- }
830
- } else if (/true|false/.test(match)) {
831
- cls = 'boolean';
832
- } else if (/null/.test(match)) {
833
- cls = 'null';
834
- }
835
- return '<span class="' + cls + '">' + match + '</span>';
836
- }
837
- );
838
- }
839
-
840
- function copyToClipboard(elementId) {
841
- const element = document.getElementById(elementId);
842
- const text = element.textContent;
843
- navigator.clipboard.writeText(text).then(() => {
844
- const btn = element.parentElement.querySelector('.copy-btn');
845
- const originalText = btn.innerHTML;
846
- btn.innerHTML = '<span class="tooltiptext">Copied!</span>✓ Copied';
847
- setTimeout(() => {
848
- btn.innerHTML = '<span class="tooltiptext">Copy to clipboard</span>' + originalText.replace('✓ Copied', 'Copy JSON');
849
- }, 2000);
850
- }).catch(err => {
851
- console.error('[mbkauthe] Failed to copy text: ', err);
852
- });
853
- }
854
- </script>
855
- </body>
856
- </html>
857
- `);
488
+ res.render("info.handlebars", {
489
+ layout: false,
490
+ mbkautheVar: mbkautheVar,
491
+ version: packageJson.version,
492
+ latestVersion,
493
+ packageJson: packageJson.stringify,
494
+ packageLock: formatJson(pkgl),
495
+ });
858
496
  } catch (err) {
859
497
  console.error("[mbkauthe] Error fetching version information:", err);
860
498
  res.status(500).send(`
@@ -870,6 +508,168 @@ router.get(["/mbkauthe/info", "/mbkauthe/i"], LoginLimit, async (_, res) => {
870
508
  `);
871
509
  }
872
510
  });
511
+ /*
512
+ // Configure GitHub Strategy for login
513
+ passport.use('github-login', new GitHubStrategy({
514
+ clientID: process.env.GITHUB_CLIENT_ID,
515
+ clientSecret: process.env.GITHUB_CLIENT_SECRET,
516
+ callbackURL: '/mbkauthe/api/github/login/callback',
517
+ scope: ['user:email']
518
+ },
519
+ async (accessToken, refreshToken, profile, done) => {
520
+ try {
521
+ // Check if this GitHub account is linked to any user
522
+ const githubUser = await dblogin.query(
523
+ 'SELECT ug.*, u."UserName", u."Role", u."Active", u."AllowedApps" FROM user_github ug JOIN "Users" u ON ug.user_name = u."UserName" WHERE ug.github_id = $1',
524
+ [profile.id]
525
+ );
526
+
527
+ if (githubUser.rows.length === 0) {
528
+ // GitHub account is not linked to any user
529
+ return done(new Error('GitHub account not linked to any user'));
530
+ }
873
531
 
532
+ const user = githubUser.rows[0];
533
+
534
+ // Check if the user account is active
535
+ if (!user.Active) {
536
+ return done(new Error('Account is inactive'));
537
+ }
538
+
539
+ // Check if user is authorized for this app (same logic as regular login)
540
+ if (user.Role !== "SuperAdmin") {
541
+ const allowedApps = user.AllowedApps;
542
+ if (!allowedApps || !allowedApps.some(app => app.toLowerCase() === mbkautheVar.APP_NAME.toLowerCase())) {
543
+ return done(new Error(`Not authorized to use ${mbkautheVar.APP_NAME}`));
544
+ }
545
+ }
546
+
547
+ // Return user data for login
548
+ return done(null, {
549
+ id: user.id, // This should be the user ID from the Users table
550
+ username: user.UserName,
551
+ role: user.Role,
552
+ githubId: user.github_id,
553
+ githubUsername: user.github_username
554
+ });
555
+ } catch (err) {
556
+ console.error('[mbkauthe] GitHub login error:', err);
557
+ return done(err);
558
+ }
559
+ }
560
+ ));
561
+
562
+ // Serialize/Deserialize user for GitHub login
563
+ passport.serializeUser((user, done) => {
564
+ done(null, user);
565
+ });
566
+
567
+ passport.deserializeUser((user, done) => {
568
+ done(null, user);
569
+ });
570
+
571
+ // Initialize passport
572
+ router.use(passport.initialize());
573
+ router.use(passport.session());
574
+
575
+ // GitHub login initiation
576
+ router.get('/mbkauthe/api/github/login', passport.authenticate('github-login'));
577
+
578
+ // GitHub login callback
579
+ router.get('/mbkauthe/api/github/login/callback',
580
+ passport.authenticate('github-login', {
581
+ failureRedirect: '/mbkauthe/login?error=github_auth_failed',
582
+ session: false // We'll handle session manually
583
+ }),
584
+ async (req, res) => {
585
+ try {
586
+ const githubUser = req.user;
587
+
588
+ // Find the actual user record
589
+ const userQuery = `SELECT * FROM "Users" WHERE "UserName" = $1`;
590
+ const userResult = await dblogin.query(userQuery, [githubUser.username]);
591
+
592
+ if (userResult.rows.length === 0) {
593
+ console.log(`[mbkauthe] GitHub login: User not found: ${githubUser.username}`);
594
+ return res.redirect('/mbkauthe/login?error=user_not_found');
595
+ }
596
+
597
+ const user = userResult.rows[0];
598
+
599
+ // Check 2FA if enabled
600
+ if ((mbkautheVar.MBKAUTH_TWO_FA_ENABLE || "").toLowerCase() === "true") {
601
+ const twoFAQuery = `SELECT "TwoFAStatus" FROM "TwoFA" WHERE "UserName" = $1`;
602
+ const twoFAResult = await dblogin.query(twoFAQuery, [githubUser.username]);
603
+
604
+ if (twoFAResult.rows.length > 0 && twoFAResult.rows[0].TwoFAStatus) {
605
+ // 2FA is enabled, store pre-auth user and redirect to 2FA
606
+ req.session.preAuthUser = {
607
+ id: user.id,
608
+ username: user.UserName,
609
+ role: user.Role,
610
+ loginMethod: 'github'
611
+ };
612
+ console.log(`[mbkauthe] GitHub login: 2FA required for user: ${githubUser.username}`);
613
+ return res.redirect('/mbkauthe/2fa');
614
+ }
615
+ }
616
+
617
+ // Complete login process
618
+ const userForSession = {
619
+ id: user.id,
620
+ username: user.UserName,
621
+ role: user.Role,
622
+ };
623
+
624
+ // Generate session and complete login
625
+ const sessionId = crypto.randomBytes(256).toString("hex");
626
+ console.log(`[mbkauthe] GitHub login: Generated session ID for username: ${user.UserName}`);
627
+
628
+ // Delete old session record for this user
629
+ await dblogin.query('DELETE FROM "session" WHERE username = $1', [user.UserName]);
630
+
631
+ await dblogin.query(`UPDATE "Users" SET "SessionId" = $1 WHERE "id" = $2`, [
632
+ sessionId,
633
+ user.id,
634
+ ]);
635
+
636
+ req.session.user = {
637
+ id: user.id,
638
+ username: user.UserName,
639
+ role: user.Role,
640
+ sessionId,
641
+ };
642
+
643
+ req.session.save(async (err) => {
644
+ if (err) {
645
+ console.log("[mbkauthe] GitHub login session save error:", err);
646
+ return res.redirect('/mbkauthe/login?error=session_error');
647
+ }
648
+
649
+ try {
650
+ await dblogin.query(
651
+ 'UPDATE "session" SET username = $1 WHERE sid = $2',
652
+ [user.UserName, req.sessionID]
653
+ );
654
+ } catch (e) {
655
+ console.log("[mbkauthe] GitHub login: Failed to update username in session table:", e);
656
+ }
657
+
658
+ const cookieOptions = getCookieOptions();
659
+ res.cookie("sessionId", sessionId, cookieOptions);
660
+ console.log(`[mbkauthe] GitHub login: User "${user.UserName}" logged in successfully`);
661
+
662
+ // Redirect to the configured URL or home
663
+ const redirectUrl = mbkautheVar.loginRedirectURL || '/home';
664
+ res.redirect(redirectUrl);
665
+ });
666
+
667
+ } catch (err) {
668
+ console.error('[mbkauthe] GitHub login callback error:', err);
669
+ res.redirect('/mbkauthe/login?error=internal_error');
670
+ }
671
+ }
672
+ );
673
+ */
874
674
  export { getLatestVersion };
875
675
  export default router;
@@ -170,56 +170,6 @@ const validateSessionAndRole = (requiredRole) => {
170
170
  };
171
171
  };
172
172
 
173
- async function getUserData(UserName, parameters) {
174
- try {
175
- if (!parameters || parameters.length === 0) {
176
- throw new Error("Parameters are required to fetch user data");
177
- }
178
-
179
- const userFields = [
180
- "Password", "UserName", "Role", "Active", "GuestRole", "HaveMailAccount", "AllowedApps",
181
- ];
182
- const profileFields = [
183
- "FullName", "email", "Image", "ProjectLinks", "SocialAccounts", "Bio", "Positions",
184
- ];
185
-
186
- let userParameters = [];
187
- let profileParameters = [];
188
-
189
- if (parameters === "profiledata") {
190
- userParameters = userFields.filter(field => field !== "Password");
191
- profileParameters = profileFields;
192
- } else {
193
- userParameters = userFields.filter(field => parameters.includes(field));
194
- profileParameters = profileFields.filter(field => parameters.includes(field));
195
- }
196
-
197
- let userResult = {};
198
- if (userParameters.length > 0) {
199
- const userQuery = `SELECT ${userParameters.map(field => `"${field}"`).join(", ")}
200
- FROM "Users" WHERE "UserName" = $1`;
201
- const userQueryResult = await dblogin.query(userQuery, [UserName]);
202
- if (userQueryResult.rows.length === 0) return { error: "User not found" };
203
- userResult = userQueryResult.rows[0];
204
- }
205
-
206
- let profileResult = {};
207
- if (profileParameters.length > 0) {
208
- const profileQuery = `SELECT ${profileParameters.map(field => `"${field}"`).join(", ")}
209
- FROM profiledata WHERE "UserName" = $1`;
210
- const profileQueryResult = await dblogin.query(profileQuery, [UserName]);
211
- if (profileQueryResult.rows.length === 0) return { error: "Profile data not found" };
212
- profileResult = profileQueryResult.rows[0];
213
- }
214
-
215
- const combinedResult = { ...userResult, ...profileResult };
216
- return combinedResult;
217
- } catch (err) {
218
- console.error("[mbkauthe] Error fetching user data:", err.message);
219
- throw err;
220
- }
221
- }
222
-
223
173
  const authenticate = (authentication) => {
224
174
  return (req, res, next) => {
225
175
  const token = req.headers["authorization"];
@@ -331,4 +281,4 @@ const authapi = (requiredRole = []) => {
331
281
  };
332
282
  };
333
283
 
334
- export { validateSession, checkRolePermission, validateSessionAndRole, getUserData, authenticate, authapi };
284
+ export { validateSession, checkRolePermission, validateSessionAndRole, authenticate, authapi };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mbkauthe",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "description": "MBKTechStudio's reusable authentication system for Node.js applications.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -0,0 +1,378 @@
1
+ <html>
2
+
3
+ <head>
4
+ <title>Version and Configuration Information</title>
5
+ <link rel="icon" type="image/x-icon" href="https://mbktechstudio.com/Assets/Images/Icon/dgicon.svg">
6
+
7
+ <style>
8
+ :root {
9
+ --bg-color: #121212;
10
+ --card-bg: #1e1e1e;
11
+ --text-color: #e0e0e0;
12
+ --text-secondary: #a0a0a0;
13
+ --primary: #bb86fc;
14
+ --primary-dark: #3700b3;
15
+ --secondary: #03dac6;
16
+ --border-color: #333;
17
+ --success: #4caf50;
18
+ --warning: #ff9800;
19
+ --error: #f44336;
20
+ --key-color: #bb86fc;
21
+ --string-color: #03dac6;
22
+ --number-color: #ff7043;
23
+ --boolean-color: #7986cb;
24
+ }
25
+
26
+ body {
27
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
28
+ margin: 0;
29
+ padding: 20px;
30
+ background-color: var(--bg-color);
31
+ color: var(--text-color);
32
+ }
33
+
34
+ .container {
35
+ max-width: 1000px;
36
+ margin: 0 auto;
37
+ padding: 20px;
38
+ background: var(--card-bg);
39
+ border-radius: 8px;
40
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
41
+ }
42
+
43
+ h1 {
44
+ color: var(--primary);
45
+ text-align: center;
46
+ margin-bottom: 30px;
47
+ padding-bottom: 10px;
48
+ border-bottom: 1px solid var(--border-color);
49
+ font-weight: bold;
50
+ letter-spacing: 1px;
51
+ }
52
+
53
+ .info-section {
54
+ margin-bottom: 25px;
55
+ padding: 20px;
56
+ border: 1px solid var(--border-color);
57
+ border-radius: 8px;
58
+ background-color: rgba(30, 30, 30, 0.7);
59
+ transition: all 0.3s ease;
60
+ }
61
+
62
+ .info-section:hover {
63
+ border-color: var(--primary);
64
+ box-shadow: 0 0 0 1px var(--primary);
65
+ }
66
+
67
+ .info-section h2 {
68
+ color: var(--primary);
69
+ border-bottom: 2px solid var(--primary-dark);
70
+ padding-bottom: 8px;
71
+ margin-top: 0;
72
+ margin-bottom: 15px;
73
+ font-size: 1.2em;
74
+ display: flex;
75
+ justify-content: space-between;
76
+ align-items: center;
77
+ }
78
+
79
+ .info-row {
80
+ display: flex;
81
+ margin-bottom: 10px;
82
+ padding-bottom: 10px;
83
+ border-bottom: 1px solid var(--border-color);
84
+ }
85
+
86
+ .info-label {
87
+ font-weight: 600;
88
+ color: var(--text-secondary);
89
+ min-width: 220px;
90
+ font-size: 0.95em;
91
+ }
92
+
93
+ .info-value {
94
+ flex: 1;
95
+ word-break: break-word;
96
+ color: var(--text-color);
97
+ }
98
+
99
+ .json-container {
100
+ background: #252525;
101
+ border: 1px solid var(--border-color);
102
+ border-radius: 6px;
103
+ padding: 12px;
104
+ margin-top: 10px;
105
+ max-height: 400px;
106
+ overflow: auto;
107
+ font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;
108
+ font-size: 0.85em;
109
+ white-space: pre-wrap;
110
+ position: relative;
111
+ }
112
+
113
+ .json-container pre {
114
+ margin: 0;
115
+ font-family: inherit;
116
+ }
117
+
118
+ .json-container .key {
119
+ color: var(--key-color);
120
+ }
121
+
122
+ .json-container .string {
123
+ color: var(--string-color);
124
+ }
125
+
126
+ .json-container .number {
127
+ color: var(--number-color);
128
+ }
129
+
130
+ .json-container .boolean {
131
+ color: var(--boolean-color);
132
+ }
133
+
134
+ .json-container .null {
135
+ color: var(--boolean-color);
136
+ opacity: 0.7;
137
+ }
138
+
139
+ .version-status {
140
+ display: inline-block;
141
+ padding: 3px 10px;
142
+ border-radius: 12px;
143
+ font-size: 0.8em;
144
+ font-weight: 600;
145
+ margin-left: 10px;
146
+ }
147
+
148
+ .version-up-to-date {
149
+ background: rgba(76, 175, 80, 0.2);
150
+ color: var(--success);
151
+ border: 1px solid var(--success);
152
+ }
153
+
154
+ .version-outdated {
155
+ background: rgba(244, 67, 54, 0.2);
156
+ color: var(--error);
157
+ border: 1px solid var(--error);
158
+ }
159
+
160
+ .version-fetch-error {
161
+ background: rgba(255, 152, 0, 0.2);
162
+ color: var(--warning);
163
+ border: 1px solid var(--warning);
164
+ }
165
+
166
+ .copy-btn {
167
+ background: var(--primary-dark);
168
+ color: white;
169
+ border: none;
170
+ padding: 5px 12px;
171
+ border-radius: 4px;
172
+ cursor: pointer;
173
+ font-size: 0.8em;
174
+ transition: all 0.2s ease;
175
+ display: flex;
176
+ align-items: center;
177
+ gap: 5px;
178
+ }
179
+
180
+ .copy-btn:hover {
181
+ background: var(--primary);
182
+ transform: translateY(-1px);
183
+ }
184
+
185
+ .copy-btn:active {
186
+ transform: translateY(0);
187
+ }
188
+
189
+ /* Scrollbar styling */
190
+ ::-webkit-scrollbar {
191
+ width: 8px;
192
+ height: 8px;
193
+ }
194
+
195
+ ::-webkit-scrollbar-track {
196
+ background: #2d2d2d;
197
+ border-radius: 4px;
198
+ }
199
+
200
+ ::-webkit-scrollbar-thumb {
201
+ background: #555;
202
+ border-radius: 4px;
203
+ }
204
+
205
+ ::-webkit-scrollbar-thumb:hover {
206
+ background: var(--primary);
207
+ }
208
+
209
+ /* Tooltip for copy button */
210
+ .tooltip {
211
+ position: relative;
212
+ display: inline-block;
213
+ }
214
+
215
+ .tooltip .tooltiptext {
216
+ visibility: hidden;
217
+ width: 120px;
218
+ background-color: #333;
219
+ color: #fff;
220
+ text-align: center;
221
+ border-radius: 6px;
222
+ padding: 5px;
223
+ position: absolute;
224
+ z-index: 1;
225
+ bottom: 125%;
226
+ left: 50%;
227
+ margin-left: -60px;
228
+ opacity: 0;
229
+ transition: opacity 0.3s;
230
+ font-size: 0.8em;
231
+ }
232
+
233
+ .tooltip:hover .tooltiptext {
234
+ visibility: visible;
235
+ opacity: 1;
236
+ }
237
+ </style>
238
+ <link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
239
+ </head>
240
+
241
+ <body>
242
+ <div class="container">
243
+ <h1>Version and Configuration Dashboard</h1>
244
+
245
+ <div class="info-section">
246
+ <h2>Version Information</h2>
247
+ <div class="info-row">
248
+ <div class="info-label">Current Version:</div>
249
+ <div class="info-value" id="CurrentVersion">{{version}}</div>
250
+ </div>
251
+ <div class="info-row">
252
+ <div class="info-label">Latest Version:</div>
253
+ <div class="info-value">
254
+ {{latestVersion}}
255
+
256
+ {{#if (eq latestVersion version)}}
257
+ <span class="version-status version-up-to-date">Up to date</span>
258
+ {{/if}}
259
+
260
+ </div>
261
+ </div>
262
+ </div>
263
+
264
+ <div class="info-section">
265
+ <h2>Configuration Information</h2>
266
+ <div class="info-row">
267
+ <div class="info-label">APP_NAME:</div>
268
+ <div class="info-value">{{mbkautheVar.APP_NAME}}</div>
269
+ </div>
270
+ <div class="info-row">
271
+ <div class="info-label">MBKAUTH_TWO_FA_ENABLE:</div>
272
+ <div class="info-value">{{mbkautheVar.MBKAUTH_TWO_FA_ENABLE}}</div>
273
+ </div>
274
+ <div class="info-row">
275
+ <div class="info-label">COOKIE_EXPIRE_TIME:</div>
276
+ <div class="info-value">{{mbkautheVar.COOKIE_EXPIRE_TIME}} Days</div>
277
+ </div>
278
+ <div class="info-row">
279
+ <div class="info-label">IS_DEPLOYED:</div>
280
+ <div class="info-value">{{mbkautheVar.IS_DEPLOYED}}</div>
281
+ </div>
282
+ <div class="info-row">
283
+ <div class="info-label">DOMAIN:</div>
284
+ <div class="info-value">{{mbkautheVar.DOMAIN}}</div>
285
+ </div>
286
+ <div class="info-row">
287
+ <div class="info-label">Login Redirect URL:</div>
288
+ <div class="info-value">{{mbkautheVar.loginRedirectURL}}</div>
289
+ </div>
290
+ <div class="info-row">
291
+ <div class="info-label">GitHub Login Enabled:</div>
292
+ <div class="info-value">{{mbkautheVar.GITHUB_LOGIN_ENABLED}}</div>
293
+ </div>
294
+ </div>
295
+
296
+ <div class="info-section">
297
+ <h2>
298
+ Package Information
299
+ <button class="copy-btn tooltip" onclick="copyToClipboard('package-json')">
300
+ <span class="tooltiptext">Copy to clipboard</span>
301
+ Copy JSON
302
+ </button>
303
+ </h2>
304
+ <div id="package-json" class="json-container">
305
+ <pre>{{packageJson}}</pre>
306
+ </div>
307
+ </div>
308
+
309
+ <div class="info-section">
310
+ <h2>
311
+ Package Lock
312
+ <button class="copy-btn tooltip" onclick="copyToClipboard('package-lock')">
313
+ <span class="tooltiptext">Copy to clipboard</span>
314
+ Copy JSON
315
+ </button>
316
+ </h2>
317
+ <div id="package-lock" class="json-container">
318
+ <pre>{{packageLockJson}}</pre>
319
+ </div>
320
+ </div>
321
+ </div>
322
+
323
+ <script>
324
+ document.addEventListener('DOMContentLoaded', function () {
325
+ // Apply syntax highlighting to all JSON containers
326
+ const jsonContainers = document.querySelectorAll('.json-container pre');
327
+ jsonContainers.forEach(container => {
328
+ container.innerHTML = syntaxHighlight(container.textContent);
329
+ });
330
+ });
331
+
332
+ function syntaxHighlight(json) {
333
+ if (typeof json !== 'string') {
334
+ json = JSON.stringify(json, null, 2);
335
+ }
336
+
337
+ // Escape HTML
338
+ json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
339
+
340
+ // Apply syntax highlighting
341
+ return json.replace(
342
+ /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,
343
+ function (match) {
344
+ let cls = 'number';
345
+ if (/^"/.test(match)) {
346
+ if (/:$/.test(match)) {
347
+ cls = 'key';
348
+ } else {
349
+ cls = 'string';
350
+ }
351
+ } else if (/true|false/.test(match)) {
352
+ cls = 'boolean';
353
+ } else if (/null/.test(match)) {
354
+ cls = 'null';
355
+ }
356
+ return '<span class="' + cls + '">' + match + '</span>';
357
+ }
358
+ );
359
+ }
360
+
361
+ function copyToClipboard(elementId) {
362
+ const element = document.getElementById(elementId);
363
+ const text = element.textContent;
364
+ navigator.clipboard.writeText(text).then(() => {
365
+ const btn = element.parentElement.querySelector('.copy-btn');
366
+ const originalText = btn.innerHTML;
367
+ btn.innerHTML = '<span class="tooltiptext">Copied!</span>✓ Copied';
368
+ setTimeout(() => {
369
+ btn.innerHTML = '<span class="tooltiptext">Copy to clipboard</span>' + originalText.replace('✓ Copied', 'Copy JSON');
370
+ }, 2000);
371
+ }).catch(err => {
372
+ console.error('[mbkauthe] Failed to copy text: ', err);
373
+ });
374
+ }
375
+ </script>
376
+ </body>
377
+
378
+ </html>
@@ -242,6 +242,60 @@
242
242
  box-shadow: none;
243
243
  }
244
244
 
245
+ .social-login {
246
+ margin-top: 1.5rem;
247
+ text-align: center;
248
+ }
249
+
250
+ .divider {
251
+ display: flex;
252
+ align-items: center;
253
+ justify-content: center;
254
+ margin: 1rem 0;
255
+ }
256
+
257
+ .divider::before,
258
+ .divider::after {
259
+ content: '';
260
+ flex: 1;
261
+ height: 1px;
262
+ background: var(--gray-dark);
263
+ }
264
+
265
+ .divider span {
266
+ background: var(--dark);
267
+ padding: 0 15px;
268
+ color: var(--gray);
269
+ font-size: 0.9rem;
270
+ }
271
+
272
+ .btn-github {
273
+ display: inline-flex;
274
+ align-items: center;
275
+ justify-content: center;
276
+ gap: 8px;
277
+ width: 100%;
278
+ padding: 12px 20px;
279
+ border-radius: var(--radius-sm);
280
+ background: #24292e;
281
+ color: white;
282
+ font-weight: 500;
283
+ font-size: 0.95rem;
284
+ text-decoration: none;
285
+ transition: var(--transition);
286
+ box-shadow: var(--shadow-sm);
287
+ }
288
+
289
+ .btn-github:hover {
290
+ background: #3b4045;
291
+ box-shadow: var(--shadow-md);
292
+ transform: translateY(-2px);
293
+ }
294
+
295
+ .btn-github i {
296
+ font-size: 1.1rem;
297
+ }
298
+
245
299
  .login-links {
246
300
  display: flex;
247
301
  justify-content: space-between;
@@ -511,6 +565,17 @@
511
565
  <button type="submit" class="btn-login" id="loginButton">
512
566
  <span id="loginButtonText">Login</span>
513
567
  </button>
568
+ {{#if githubLoginEnabled }}
569
+ <div class="social-login">
570
+ <div class="divider">
571
+ <span>or</span>
572
+ </div>
573
+ <a href="/mbkauthe/api/github/login" class="btn-github">
574
+ <i class="fab fa-github"></i>
575
+ <span>Continue with GitHub</span>
576
+ </a>
577
+ </div>
578
+ {{/if }}
514
579
 
515
580
  {{#if userLoggedIn }}
516
581
  <div class="WarningboxInfo">
@@ -598,7 +663,7 @@
598
663
  })
599
664
  })
600
665
  .then(response => response.json())
601
- .then(data => {
666
+ .then data => {
602
667
  if (data.success) {
603
668
  if (data.twoFactorRequired) {
604
669
  // Redirect to 2FA page