mbkauthe 4.4.0 → 4.6.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 +3 -1
- package/docs/db.md +2 -2
- package/index.js +1 -1
- package/lib/routes/misc.js +44 -70
- package/package.json +1 -1
- package/views/{info.handlebars → info_mbkauthe.handlebars} +3 -3
- package/views/loginmbkauthe.handlebars +4 -4
- package/views/sharedStyles.handlebars +22 -3
- package/views/test.handlebars +260 -0
- package/views/versionInfo.handlebars +2 -2
package/README.md
CHANGED
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
[](LICENSE)
|
|
5
5
|
[](https://nodejs.org/)
|
|
6
6
|
[](https://github.com/MIbnEKhalid/mbkauthe/actions/workflows/publish.yml)
|
|
7
|
+
[](https://www.npmjs.com/package/mbkauthe)
|
|
8
|
+
|
|
7
9
|
|
|
8
10
|
<p align="center">
|
|
9
|
-
<img height="64px" src="./public/
|
|
11
|
+
<img height="64px" src="./public/logo.png" alt="MBKAuthe" />
|
|
10
12
|
</p>
|
|
11
13
|
|
|
12
14
|
**MBKAuthe** is a production-ready authentication system for Node.js with Express and PostgreSQL. Features include secure login, 2FA, role-based access, OAuth (GitHub & Google), multi-session support, and multi-app user management.
|
package/docs/db.md
CHANGED
|
@@ -193,8 +193,8 @@ The system handles various error cases:
|
|
|
193
193
|
## Login Page Updates
|
|
194
194
|
|
|
195
195
|
The login page now includes:
|
|
196
|
-
- A "
|
|
197
|
-
- A "
|
|
196
|
+
- A "Login with GitHub" button
|
|
197
|
+
- A "Login with Google" button
|
|
198
198
|
- A divider ("or") between regular and OAuth login
|
|
199
199
|
- Proper styling that matches your existing design
|
|
200
200
|
|
package/index.js
CHANGED
|
@@ -70,7 +70,7 @@ if (process.env.test === "dev") {
|
|
|
70
70
|
const port = 5555;
|
|
71
71
|
app.use(router);
|
|
72
72
|
app.get(["/dashboard", "/home", "/"], (req, res) => {
|
|
73
|
-
|
|
73
|
+
return res.redirect("/mbkauthe/");
|
|
74
74
|
});
|
|
75
75
|
app.get("/showmessage", (req, res) => {
|
|
76
76
|
//uncomment line 26 on showmessage.handlebars for testing, after testing comment it back
|
package/lib/routes/misc.js
CHANGED
|
@@ -150,65 +150,26 @@ router.get('/user/profilepic', async (req, res) => {
|
|
|
150
150
|
});
|
|
151
151
|
|
|
152
152
|
// Test route
|
|
153
|
-
router.get('/test', validateSession, LoginLimit, async (req, res) => {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
.actions{margin-top:14px;display:flex;flex-wrap:wrap;gap:8px}
|
|
174
|
-
.btn{display:inline-flex;align-items:center;gap:8px;padding:9px 14px;border-radius:10px;border:1px solid transparent;cursor:pointer;text-decoration:none}
|
|
175
|
-
.btn-primary{background:var(--accent);color:white}
|
|
176
|
-
.btn-outline{background:transparent;border-color:rgba(37,99,235,0.12);color:var(--accent)}
|
|
177
|
-
.btn-danger{background:var(--danger);color:#fff}
|
|
178
|
-
a{color:inherit}
|
|
179
|
-
@media (max-width:640px){.card{grid-template-columns:1fr;align-items:stretch}.avatar{width:80px;height:80px}}
|
|
180
|
-
</style>
|
|
181
|
-
</head>
|
|
182
|
-
<div class="container">
|
|
183
|
-
<div class="card" role="region" aria-label="User Session">
|
|
184
|
-
<div class="avatar" aria-hidden="true">
|
|
185
|
-
<img src="/mbkauthe/user/profilepic?u=${encodeURIComponent(req.session.user.username)}" alt="Avatar for ${req.session.user.username}" title="${req.session.user.fullname || req.session.user.username}" loading="lazy" decoding="async" width="96" height="96" onerror="this.style.display='none';var s=this.nextElementSibling; if(s) s.style.display='flex';" />
|
|
186
|
-
<div class="initials" aria-hidden="true" style="display:none">${(req.session.user.fullname && req.session.user.fullname[0]) || req.session.user.username[0]}</div>
|
|
187
|
-
</div>
|
|
188
|
-
<div class="meta">
|
|
189
|
-
<div>
|
|
190
|
-
<div class="status">✅ Authentication successful</div>
|
|
191
|
-
<h3 class="user-title">${req.session.user.username} <small style="color:var(--muted);font-weight:600">· ${req.session.user.role}</small></h3>
|
|
192
|
-
<p class="user-sub">ID: ${req.session.user.id} · Session: ${req.session.user.sessionId.slice(0,8)}…</p>
|
|
193
|
-
</div>
|
|
194
|
-
|
|
195
|
-
<div class="details" aria-live="polite">
|
|
196
|
-
<div>Full Name: ${req.session.user.fullname || 'N/A'}</div>
|
|
197
|
-
<div>Allowed Apps: ${Array.isArray(req.session.user.allowedApps) ? req.session.user.allowedApps.join(', ') : 'N/A'}</div>
|
|
198
|
-
</div>
|
|
199
|
-
|
|
200
|
-
<div class="actions">
|
|
201
|
-
<button class="btn btn-primary" onclick="logout()" aria-label="Log out">Logout</button>
|
|
202
|
-
<a class="btn btn-outline" href="https://portal.mbktech.org/">Web Portal</a>
|
|
203
|
-
<a class="btn btn-outline" href="https://portal.mbktech.org/user/settings">User Settings</a>
|
|
204
|
-
<a class="btn btn-outline" href="/mbkauthe/info">Info Page</a>
|
|
205
|
-
<a class="btn btn-outline" href="/mbkauthe/login">Login Page</a>
|
|
206
|
-
</div>
|
|
207
|
-
</div>
|
|
208
|
-
</div>
|
|
209
|
-
</div>
|
|
210
|
-
`);
|
|
211
|
-
}
|
|
153
|
+
router.get(['/test', '/'], validateSession, LoginLimit, async (req, res) => {
|
|
154
|
+
const { username, fullname, role, id, sessionId, allowedApps } = req.session.user;
|
|
155
|
+
|
|
156
|
+
const sessionExpiry = req.session.cookie?.expires
|
|
157
|
+
? new Date(req.session.cookie.expires).toISOString()
|
|
158
|
+
: null;
|
|
159
|
+
|
|
160
|
+
return res.render('test.handlebars', {
|
|
161
|
+
layout: false,
|
|
162
|
+
username,
|
|
163
|
+
fullname: fullname || 'N/A',
|
|
164
|
+
role,
|
|
165
|
+
id,
|
|
166
|
+
sessionIdShort: sessionId.slice(0, 8),
|
|
167
|
+
profilePicUrl: encodeURIComponent(username),
|
|
168
|
+
displayName: fullname || username,
|
|
169
|
+
initial: (fullname && fullname[0]) || username[0],
|
|
170
|
+
allowedApps: Array.isArray(allowedApps) ? allowedApps.join(', ') : 'N/A',
|
|
171
|
+
sessionExpiry
|
|
172
|
+
});
|
|
212
173
|
});
|
|
213
174
|
|
|
214
175
|
router.post('/test', validateSession, LoginLimit, async (req, res) => {
|
|
@@ -476,15 +437,13 @@ export async function getLatestVersion() {
|
|
|
476
437
|
}
|
|
477
438
|
}
|
|
478
439
|
|
|
440
|
+
|
|
441
|
+
const { APP_NAME, DOMAIN, IS_DEPLOYED, loginRedirectURL } = mbkautheVar;
|
|
442
|
+
const safe_mbkautheVar = { APP_NAME, DOMAIN, IS_DEPLOYED, loginRedirectURL };
|
|
443
|
+
|
|
479
444
|
// Info page
|
|
480
445
|
router.get(["/info", "/i"], LoginLimit, async (req, res) => {
|
|
481
446
|
let latestVersion;
|
|
482
|
-
const parameters = req.query;
|
|
483
|
-
let authorized = false;
|
|
484
|
-
|
|
485
|
-
if (parameters.password && mbkautheVar.Main_SECRET_TOKEN) {
|
|
486
|
-
authorized = String(parameters.password) === String(mbkautheVar.Main_SECRET_TOKEN);
|
|
487
|
-
}
|
|
488
447
|
|
|
489
448
|
try {
|
|
490
449
|
latestVersion = await getLatestVersion();
|
|
@@ -493,13 +452,12 @@ router.get(["/info", "/i"], LoginLimit, async (req, res) => {
|
|
|
493
452
|
}
|
|
494
453
|
|
|
495
454
|
try {
|
|
496
|
-
res.render("
|
|
455
|
+
res.render("info_mbkauthe.handlebars", {
|
|
497
456
|
layout: false,
|
|
498
|
-
mbkautheVar:
|
|
499
|
-
|
|
457
|
+
mbkautheVar: safe_mbkautheVar,
|
|
458
|
+
CurrentVersion: packageJson.version,
|
|
500
459
|
APP_VERSION: appVersion,
|
|
501
|
-
latestVersion
|
|
502
|
-
authorized: authorized,
|
|
460
|
+
latestVersion
|
|
503
461
|
});
|
|
504
462
|
} catch (err) {
|
|
505
463
|
console.error("[mbkauthe] Error fetching version information:", err);
|
|
@@ -517,6 +475,22 @@ router.get(["/info", "/i"], LoginLimit, async (req, res) => {
|
|
|
517
475
|
}
|
|
518
476
|
});
|
|
519
477
|
|
|
478
|
+
router.get(["/info.json", "/i.json"], LoginLimit, async (req, res) => {
|
|
479
|
+
let latestVersion;
|
|
480
|
+
try {
|
|
481
|
+
latestVersion = await getLatestVersion();
|
|
482
|
+
} catch (err) {
|
|
483
|
+
console.error("[mbkauthe] Error fetching package-lock.json:", err);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
try {
|
|
487
|
+
res.json({ mbkautheVar: safe_mbkautheVar, CurrentVersion: packageJson.version, APP_VERSION: appVersion, latestVersion });
|
|
488
|
+
} catch (err) {
|
|
489
|
+
console.error("[mbkauthe] Error fetching version information:", err);
|
|
490
|
+
res.status(500).json({ success: false, message: "Failed to fetch version information" });
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
|
|
520
494
|
// Terminate all sessions (admin endpoint)
|
|
521
495
|
router.post("/api/terminateAllSessions", AdminOperationLimit, authenticate(mbkautheVar.Main_SECRET_TOKEN), async (req, res) => {
|
|
522
496
|
try {
|
package/package.json
CHANGED
|
@@ -180,13 +180,13 @@
|
|
|
180
180
|
<h2><i class="fas fa-code-branch"></i> Version Information</h2>
|
|
181
181
|
<div class="info-row">
|
|
182
182
|
<div class="info-label">Current Version:</div>
|
|
183
|
-
<div class="info-value" id="CurrentVersion">{{
|
|
183
|
+
<div class="info-value" id="CurrentVersion">{{CurrentVersion}}</div>
|
|
184
184
|
</div>
|
|
185
185
|
<div class="info-row">
|
|
186
186
|
<div class="info-label">Latest Version:</div>
|
|
187
187
|
<div class="info-value">
|
|
188
188
|
{{latestVersion}}
|
|
189
|
-
{{#if (eq latestVersion
|
|
189
|
+
{{#if (eq latestVersion CurrentVersion)}}
|
|
190
190
|
<span class="version-status version-up-to-date">
|
|
191
191
|
<i class="fas fa-check-circle"></i> Up to date
|
|
192
192
|
</span>
|
|
@@ -206,7 +206,7 @@
|
|
|
206
206
|
<div class="info-value">{{APP_VERSION}}</div>
|
|
207
207
|
</div>
|
|
208
208
|
<div class="info-row">
|
|
209
|
-
<div class="info-label">
|
|
209
|
+
<div class="info-label">DOMAIN:</div>
|
|
210
210
|
<div class="info-value">{{mbkautheVar.DOMAIN}}</div>
|
|
211
211
|
</div>
|
|
212
212
|
<div class="info-row">
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
{{#if githubLoginEnabled }}
|
|
94
94
|
<a type="button" id="githubLoginBtn" class="btn-social btn-switch-side last-used-parent">
|
|
95
95
|
<i class="fab fa-github"></i>
|
|
96
|
-
<span>
|
|
96
|
+
<span>Login with GitHub</span>
|
|
97
97
|
{{#if lastLoginGithub}}<span class="last-used-badge" aria-hidden="true"
|
|
98
98
|
title="Last used">Last</span>{{/if}}
|
|
99
99
|
</a>
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
{{#if googleLoginEnabled }}
|
|
102
102
|
<a type="button" id="googleLoginBtn" class="btn-social btn-google-side last-used-parent">
|
|
103
103
|
<i class="fab fa-google"></i>
|
|
104
|
-
<span>
|
|
104
|
+
<span>Login with Google</span>
|
|
105
105
|
{{#if lastLoginGoogle}}<span class="last-used-badge" aria-hidden="true"
|
|
106
106
|
title="Last used">Last</span>{{/if}}
|
|
107
107
|
</a>
|
|
@@ -182,13 +182,13 @@
|
|
|
182
182
|
</div>
|
|
183
183
|
<div class="social-icons-row">
|
|
184
184
|
<a type="button" class="swi s mobile-github-btn last-used-parent"
|
|
185
|
-
title="
|
|
185
|
+
title="Login with GitHub">
|
|
186
186
|
<i class="fab fa-github"></i>
|
|
187
187
|
{{#if lastLoginGithub}}<span class="last-used-badge" aria-hidden="true"
|
|
188
188
|
title="Last used">Last</span>{{/if}}
|
|
189
189
|
</a>
|
|
190
190
|
<a type="button" class="swi s mobile-google-btn last-used-parent"
|
|
191
|
-
title="
|
|
191
|
+
title="Login with Google">
|
|
192
192
|
<i class="fab fa-google"></i>
|
|
193
193
|
{{#if lastLoginGoogle}}<span class="last-used-badge" aria-hidden="true"
|
|
194
194
|
title="Last used">Last</span>{{/if}}
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
display: flex;
|
|
49
49
|
justify-content: space-between;
|
|
50
50
|
align-items: center;
|
|
51
|
-
padding:
|
|
51
|
+
padding: 10px 1.5rem;
|
|
52
52
|
max-width: 1400px;
|
|
53
53
|
margin: 0 auto;
|
|
54
54
|
}
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
.logo-text {
|
|
70
70
|
font-size: 2rem;
|
|
71
71
|
font-weight: 700;
|
|
72
|
+
padding-top: 10px;
|
|
72
73
|
color: var(--light);
|
|
73
74
|
}
|
|
74
75
|
|
|
@@ -78,7 +79,7 @@
|
|
|
78
79
|
|
|
79
80
|
.logo-comp {
|
|
80
81
|
margin-top: 20px;
|
|
81
|
-
font-size:
|
|
82
|
+
font-size: 1rem;
|
|
82
83
|
font-weight: bold;
|
|
83
84
|
color: var(--text-light);
|
|
84
85
|
}
|
|
@@ -608,7 +609,7 @@
|
|
|
608
609
|
flex-direction: column;
|
|
609
610
|
width: 40%;
|
|
610
611
|
background: linear-gradient(135deg, rgba(33, 150, 243, 0.08), rgba(0, 184, 148, 0.08));
|
|
611
|
-
padding:
|
|
612
|
+
padding: 2.5rem 2rem;
|
|
612
613
|
border-right: 1px solid rgba(0, 184, 148, 0.3);
|
|
613
614
|
position: relative;
|
|
614
615
|
overflow: hidden;
|
|
@@ -803,6 +804,24 @@
|
|
|
803
804
|
}
|
|
804
805
|
}
|
|
805
806
|
|
|
807
|
+
@media (max-width: 400px) {
|
|
808
|
+
|
|
809
|
+
.logo svg,
|
|
810
|
+
.logo-image {
|
|
811
|
+
height: 30px;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
.logo-text {
|
|
815
|
+
font-size: 1.6rem;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
.mbkauthe-btn-login {
|
|
819
|
+
font-size: 1rem !important;
|
|
820
|
+
padding: 6px !important;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
}
|
|
824
|
+
|
|
806
825
|
@media (max-width: 768px) {
|
|
807
826
|
.login-box {
|
|
808
827
|
padding: 2rem;
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
{{> head pageTitle="Session Test" ogUrl="/mbkauthe/test"}}
|
|
5
|
+
|
|
6
|
+
<body>
|
|
7
|
+
{{> header}}
|
|
8
|
+
|
|
9
|
+
<style>
|
|
10
|
+
.session-card {
|
|
11
|
+
background: rgba(10, 20, 20, 0.95);
|
|
12
|
+
backdrop-filter: blur(10px);
|
|
13
|
+
border-radius: var(--border-radius);
|
|
14
|
+
padding: 2.5rem;
|
|
15
|
+
width: 100%;
|
|
16
|
+
max-width: 760px;
|
|
17
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
|
|
18
|
+
border: 1px solid rgba(0, 184, 148, 0.2);
|
|
19
|
+
position: relative;
|
|
20
|
+
z-index: 2;
|
|
21
|
+
transition: var(--transition);
|
|
22
|
+
display: grid;
|
|
23
|
+
grid-template-columns: 110px 1fr;
|
|
24
|
+
gap: 2rem;
|
|
25
|
+
align-items: start;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.session-card:hover {
|
|
29
|
+
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
|
|
30
|
+
border-color: rgba(0, 184, 148, 0.3);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.avatar {
|
|
34
|
+
width: 96px;
|
|
35
|
+
height: 96px;
|
|
36
|
+
border-radius: 50%;
|
|
37
|
+
background: linear-gradient(135deg, rgba(0, 184, 148, 0.2), rgba(33, 150, 243, 0.15));
|
|
38
|
+
border: 2px solid rgba(0, 184, 148, 0.3);
|
|
39
|
+
position: relative;
|
|
40
|
+
overflow: hidden;
|
|
41
|
+
display: flex;
|
|
42
|
+
align-items: center;
|
|
43
|
+
justify-content: center;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.avatar img {
|
|
47
|
+
width: 100%;
|
|
48
|
+
height: 100%;
|
|
49
|
+
object-fit: cover;
|
|
50
|
+
display: block;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.avatar .initials {
|
|
54
|
+
position: absolute;
|
|
55
|
+
inset: 0;
|
|
56
|
+
display: none;
|
|
57
|
+
align-items: center;
|
|
58
|
+
justify-content: center;
|
|
59
|
+
font-weight: 700;
|
|
60
|
+
color: var(--accent);
|
|
61
|
+
font-size: 2rem;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.session-meta {
|
|
65
|
+
display: flex;
|
|
66
|
+
flex-direction: column;
|
|
67
|
+
gap: 1rem;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.session-status {
|
|
71
|
+
color: var(--success);
|
|
72
|
+
font-weight: 600;
|
|
73
|
+
display: flex;
|
|
74
|
+
align-items: center;
|
|
75
|
+
gap: 8px;
|
|
76
|
+
font-size: 0.95rem;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.session-title {
|
|
80
|
+
font-size: 1.3rem;
|
|
81
|
+
font-weight: 700;
|
|
82
|
+
color: var(--light);
|
|
83
|
+
margin: 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.session-sub {
|
|
87
|
+
color: var(--text-light);
|
|
88
|
+
font-size: 0.85rem;
|
|
89
|
+
margin: 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.session-details {
|
|
93
|
+
background: rgba(0, 0, 0, 0.3);
|
|
94
|
+
border: 1px solid rgba(0, 184, 148, 0.15);
|
|
95
|
+
border-radius: var(--border-radius);
|
|
96
|
+
padding: 1rem 1.2rem;
|
|
97
|
+
font-family: 'Courier New', monospace;
|
|
98
|
+
font-size: 0.85rem;
|
|
99
|
+
color: var(--text-light);
|
|
100
|
+
display: flex;
|
|
101
|
+
flex-direction: column;
|
|
102
|
+
gap: 6px;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.session-details div span {
|
|
106
|
+
color: var(--accent);
|
|
107
|
+
font-weight: 600;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.session-actions {
|
|
111
|
+
display: flex;
|
|
112
|
+
flex-wrap: wrap;
|
|
113
|
+
gap: 10px;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.btn {
|
|
117
|
+
display: inline-flex;
|
|
118
|
+
align-items: center;
|
|
119
|
+
gap: 8px;
|
|
120
|
+
padding: 10px 18px;
|
|
121
|
+
border-radius: var(--border-radius);
|
|
122
|
+
border: none;
|
|
123
|
+
cursor: pointer;
|
|
124
|
+
font-size: 0.9rem;
|
|
125
|
+
font-weight: 600;
|
|
126
|
+
text-decoration: none;
|
|
127
|
+
transition: var(--transition);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.btn-primary {
|
|
131
|
+
background: var(--accent);
|
|
132
|
+
color: var(--dark);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.btn-primary:hover {
|
|
136
|
+
background: #00a07f;
|
|
137
|
+
box-shadow: 0 4px 12px rgba(0, 184, 148, 0.4);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.btn-outline {
|
|
141
|
+
background: transparent;
|
|
142
|
+
border: 1px solid rgba(0, 184, 148, 0.35);
|
|
143
|
+
color: var(--accent);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.btn-outline:hover {
|
|
147
|
+
background: rgba(0, 184, 148, 0.1);
|
|
148
|
+
border-color: var(--accent);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.btn-danger {
|
|
152
|
+
background: var(--danger);
|
|
153
|
+
color: #fff;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.btn-danger:hover {
|
|
157
|
+
background: #e55f5f;
|
|
158
|
+
box-shadow: 0 4px 12px rgba(255, 118, 117, 0.4);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
@media (max-width: 600px) {
|
|
162
|
+
.session-card {
|
|
163
|
+
grid-template-columns: 1fr;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.avatar {
|
|
167
|
+
width: 80px;
|
|
168
|
+
height: 80px;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
</style>
|
|
172
|
+
|
|
173
|
+
<section class="login-container">
|
|
174
|
+
{{> backgroundElements}}
|
|
175
|
+
|
|
176
|
+
<div class="session-card" role="region" aria-label="User Session">
|
|
177
|
+
<div class="avatar" aria-hidden="true">
|
|
178
|
+
<img src="/mbkauthe/user/profilepic?u={{profilePicUrl}}"
|
|
179
|
+
alt="Avatar for {{username}}"
|
|
180
|
+
title="{{displayName}}"
|
|
181
|
+
loading="lazy" decoding="async" width="96" height="96"
|
|
182
|
+
onerror="this.style.display='none';var s=this.nextElementSibling; if(s) s.style.display='flex';" />
|
|
183
|
+
<div class="initials" aria-hidden="true" style="display:none">{{initial}}</div>
|
|
184
|
+
</div>
|
|
185
|
+
|
|
186
|
+
<div class="session-meta">
|
|
187
|
+
<div>
|
|
188
|
+
<div class="session-status">✅ Authentication successful</div>
|
|
189
|
+
<h3 class="session-title">{{username}} <small style="color:var(--text-light);font-weight:500">· {{role}}</small></h3>
|
|
190
|
+
<p class="session-sub">ID: {{id}} · Session: {{sessionIdShort}}…</p>
|
|
191
|
+
</div>
|
|
192
|
+
|
|
193
|
+
<div class="session-details" aria-live="polite">
|
|
194
|
+
<div><span>Full Name:</span> {{fullname}}</div>
|
|
195
|
+
<div><span>Allowed Apps:</span> {{allowedApps}}</div>
|
|
196
|
+
<div><span>Session Expires:</span> <span id="session-expiry-display">{{#if sessionExpiry}}{{sessionExpiry}}{{else}}N/A{{/if}}</span></div>
|
|
197
|
+
{{#if sessionExpiry}}<div id="session-countdown"><span>Time Remaining:</span> <span id="countdown-value">calculating…</span></div>{{/if}}
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
<div class="session-actions">
|
|
201
|
+
<button class="btn btn-danger" onclick="logout()" aria-label="Log out">
|
|
202
|
+
<i class="fas fa-sign-out-alt"></i> Logout
|
|
203
|
+
</button>
|
|
204
|
+
<a class="btn btn-outline" href="https://portal.mbktech.org/">
|
|
205
|
+
<i class="fas fa-globe"></i> Web Portal
|
|
206
|
+
</a>
|
|
207
|
+
<a class="btn btn-outline" href="https://portal.mbktech.org/user/settings">
|
|
208
|
+
<i class="fas fa-user-cog"></i> User Settings
|
|
209
|
+
</a>
|
|
210
|
+
<a class="btn btn-outline" href="/mbkauthe/info">
|
|
211
|
+
<i class="fas fa-info-circle"></i> Info Page
|
|
212
|
+
</a>
|
|
213
|
+
<a class="btn btn-outline" href="/mbkauthe/login">
|
|
214
|
+
<i class="fas fa-sign-in-alt"></i> Login Page
|
|
215
|
+
</a>
|
|
216
|
+
</div>
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
</section>
|
|
220
|
+
|
|
221
|
+
<script src="/mbkauthe/main.js"></script>
|
|
222
|
+
{{#if sessionExpiry}}
|
|
223
|
+
<script>
|
|
224
|
+
(function () {
|
|
225
|
+
var expiry = new Date('{{sessionExpiry}}');
|
|
226
|
+
var display = document.getElementById('session-expiry-display');
|
|
227
|
+
var countdown = document.getElementById('countdown-value');
|
|
228
|
+
|
|
229
|
+
// Format the expiry date in local time
|
|
230
|
+
if (display) {
|
|
231
|
+
display.textContent = expiry.toLocaleString();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function updateCountdown() {
|
|
235
|
+
var now = new Date();
|
|
236
|
+
var diff = expiry - now;
|
|
237
|
+
if (diff <= 0) {
|
|
238
|
+
countdown.textContent = 'Expired';
|
|
239
|
+
countdown.style.color = 'var(--danger)';
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
var h = Math.floor(diff / 3600000);
|
|
243
|
+
var m = Math.floor((diff % 3600000) / 60000);
|
|
244
|
+
var s = Math.floor((diff % 60000) / 1000);
|
|
245
|
+
countdown.textContent =
|
|
246
|
+
(h ? h + 'h ' : '') +
|
|
247
|
+
(h || m ? m + 'm ' : '') +
|
|
248
|
+
s + 's';
|
|
249
|
+
|
|
250
|
+
// warn when under 5 minutes
|
|
251
|
+
countdown.style.color = diff < 300000 ? 'var(--warning)' : 'var(--success)';
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
updateCountdown();
|
|
255
|
+
setInterval(updateCountdown, 1000);
|
|
256
|
+
})();
|
|
257
|
+
</script>
|
|
258
|
+
{{/if}}
|
|
259
|
+
</body>
|
|
260
|
+
</html>
|