mbkauthe 4.2.1 → 4.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/docs/api.md CHANGED
@@ -1315,6 +1315,39 @@ app.get('/admin', strictValidateSessionAndRole('SuperAdmin'), (req, res) => {
1315
1315
 
1316
1316
  ---
1317
1317
 
1318
+ ### Response Utilities
1319
+
1320
+ MBKAuthe exports small helpers to assist with page rendering and context:
1321
+
1322
+ - `getUserContext(req)` — returns a lightweight context object for templates: `{ userLoggedIn, isuserlogin, username, fullname, role, allowedApps }`.
1323
+ - `renderPage(req, res, fileLocation, layout = true, data = {})` — renders a template with the user/context merged into the data; returns a Promise and yields the typical Express `res.render` behavior.
1324
+ - `renderError(res, req, options)` — renders the standardized error page; note the signature is `(res, req, options)` and `options` follow the `ErrorRenderOptions` described in the types.
1325
+
1326
+ **Example:**
1327
+
1328
+ ```javascript
1329
+ import { getUserContext, renderPage, renderError } from 'mbkauthe';
1330
+
1331
+ app.get('/dashboard', (req, res) => {
1332
+ const ctx = getUserContext(req);
1333
+ return renderPage(req, res, 'info', true, { greeting: 'Hello', ...ctx });
1334
+ });
1335
+
1336
+ app.get('/err', (req, res) => {
1337
+ return renderError(res, req, {
1338
+ layout: false,
1339
+ code: 500,
1340
+ error: "Internal Server Error",
1341
+ message: "Simulated 500 Error",
1342
+ details: "This is a simulated 500 error page for testing purposes.",
1343
+ pagename: "Home",
1344
+ page: "/mbkauthe/login",
1345
+ });
1346
+ });
1347
+ ```
1348
+
1349
+ ---
1350
+
1318
1351
  ### `authenticate(token)`
1319
1352
 
1320
1353
  API authentication middleware for server-to-server communication.
@@ -0,0 +1,121 @@
1
+ %%{init: {"themeVariables": {"fontFamily": "Arial, Helvetica, sans-serif", "primaryTextColor": "#333333", "secondaryTextColor": "#333333", "background": "#ffffff"}}}%%
2
+ flowchart TD
3
+ A[Client POST /api/login - payload: username password redirect] --> B[Validate input length & format]
4
+ B -->|invalid| Aerr[400 - missing/invalid]
5
+ B --> C[Query Users and TwoFA status]
6
+ C -->|no user| U404[401 - invalid credentials]
7
+ C --> D[Compare password - EncPass or raw]
8
+ D -->|fail| P401[401 - incorrect password]
9
+ D -->|ok| E[Check account Active and app authorization]
10
+ E -->|inactive| I403[403 - account inactive]
11
+ E -->|not authorized| A403[403 - app not authorized]
12
+ E --> F[checkTrustedDevice username]
13
+ F -->|trusted and 2FA enabled| G[completeLoginProcess]
14
+ F -->|not trusted and 2FA enabled| H[Set preAuthUser and respond twoFactorRequired:true]
15
+ F -->|not trusted and 2FA disabled| G
16
+
17
+ %% Expanded completeLoginProcess (implementation detail)
18
+ G --> Gdel[Delete old session store row]
19
+ Gdel --> Gregen[Regenerate session - new session id]
20
+ Gregen --> Gprune[Enforce max sessions per user - prune oldest if limit reached]
21
+ Gprune --> Gcache[clearProfilePicCache]
22
+ Gcache --> Ginsert[Insert app Sessions row - returns sessionId]
23
+ Ginsert --> Glast[Update Users.last_login timestamp]
24
+ Glast --> Gcookies[Set cookies: encrypted sessionId, username, fullName, lastLoginMethod; upsert account list cookie]
25
+ Gcookies --> |trustDevice| Gtrust[Create TrustedDevices row; set device_token cookie]
26
+ Gcookies --> RESP[200 - success: session created; cookies set; sessionId returned]
27
+ Gtrust --> RESP
28
+
29
+ %% 2FA
30
+ A2[Client POST /api/verify-2fa - payload: token trustDevice] --> B2[Ensure req.session.preAuthUser exists]
31
+ B2 -->|missing| Err401[401 - session not found]
32
+ B2 --> C2[Validate token format - 6 digits]
33
+ C2 --> D2[Fetch TwoFA secret from DB for username]
34
+ D2 --> E2[Verify TOTP token]
35
+ E2 -->|invalid| T401[401 - invalid 2FA token]
36
+ E2 -->|valid| F2[completeLoginProcess]
37
+ F2 --> RESP2[200 - success: session created; cookies set]
38
+
39
+ %% completeLoginProcess & validateSession kept separate for clarity
40
+
41
+ %% Logout
42
+ L1[Client POST /api/logout] --> L2{req.session.user exists?}
43
+ L2 -->|no| Lerr[400 - Not logged in]
44
+ L2 -->|yes| L3[Delete app Sessions row and session store row]
45
+ L3 --> L4[removeAccountFromCookie]
46
+ L4 --> L5[Destroy session and clear session cookies]
47
+ L5 --> LResp[200 - logout successful]
48
+
49
+ %% Account-sessions
50
+ AS1[Client GET /api/account-sessions] --> AS2[readAccountListFromCookie]
51
+ AS2 -->|empty| ASresp[Return empty accounts and currentSessionId]
52
+ AS2 --> AS3[For each stored account: validate each stored session via fetchActiveSession]
53
+ AS3 -->|invalid| AS4[invalidate DB session and remove account from cookie]
54
+ AS3 -->|valid| AS5[Push validated account to list]
55
+ AS5 --> ASresp2[Return accounts and currentSessionId]
56
+
57
+ %% Switch session
58
+ SS1[Client POST /api/switch-session] --> SS2[Validate sessionId format]
59
+ SS2 -->|invalid| SSerr400[400 - Invalid session id]
60
+ SS2 --> SS3[Check sessionId in readAccountListFromCookie]
61
+ SS3 -->|missing| SSerr403[403 - Account not available on this device]
62
+ SS3 --> SS4[fetchActiveSession]
63
+ SS4 -->|not found| SSerr401[401 - Session expired]
64
+ SS4 --> SS5[Regenerate session; set req.session.user to DB row; save session]
65
+ SS5 --> SS6[Sync cookies & upsert account list cookie]
66
+ SS6 --> SSresp[200 - success with username fullName redirect]
67
+
68
+ %% Logout-all
69
+ LA1[Client POST /api/logout-all] --> LA2[readAccountListFromCookie]
70
+ LA2 --> LA3[Collect sessionIds; add currentSession if present]
71
+ LA3 --> LA4[Delete Sessions rows by ids]
72
+ LA4 --> LA5[Delete session store row for current session]
73
+ LA5 --> LA6[Clear account list cookie, clear session cookies, destroy session]
74
+ LA6 --> LAresp[200 - All accounts logged out]
75
+
76
+ %% OAuth
77
+ O1[Client GET /api/provider/login?redirect=...] --> O2[Server create CSRF token and save to session.oauthCsrfToken and oauthRedirect]
78
+ O2 --> O3[passport.authenticate -> redirect to provider auth page]
79
+ O3 --> O4[Provider redirects back to /api/provider/login/callback with state and auth code]
80
+ O4 --> O5[validateOAuthCallback: compare state with session.oauthCsrfToken]
81
+ O5 -->|invalid| Oerr[403 - CSRF mismatch]
82
+ O5 -->|valid| O6[passport.authenticate -> locate linked user record]
83
+ O6 -->|not linked| Oerr2[403 - Account not linked]
84
+ O6 --> O7[Check account active and allowedApps]
85
+ O7 -->|2FA enabled and device trusted| O8[completeLoginProcess skip 2FA]
86
+ O7 -->|2FA enabled and not trusted| O9[Set req.session.preAuthUser and redirect to /2fa]
87
+ O7 -->|2FA disabled| O8
88
+ O8 --> Oresp[200/redirect - login completed]
89
+
90
+ %% UI pages
91
+ LG1[Client GET /mbkauthe/login] --> LG2[Render login page with CSRF, provider flags and lastLogin cookie]
92
+ AC1[Client GET /mbkauthe/accounts] --> AC2[Render account switch page with CSRF and accounts list]
93
+
94
+ %% Token validation
95
+ T1[Incoming request with Authorization: Bearer <token>] --> T2[If token starts with mbk_ -> API token]
96
+ T2 --> T3[Hash token then query ApiTokens joined to Users by TokenHash]
97
+ T3 -->|not found| Terr[401 - INVALID_TOKEN]
98
+ T3 -->|found| T4[Check ExpiresAt; parse permissions.scope and allowedApps]
99
+ T4 -->|expired| Texp[401 - TOKEN_EXPIRED]
100
+ T4 --> T5[Update ApiTokens LastUsed; return tokenUser with tokenScope]
101
+ T5 --> T6[Validate tokenScope via canAccessMethod for request method]
102
+ T6 -->|insufficient| T403[403 - TOKEN_SCOPE_INSUFFICIENT]
103
+ T6 --> NEXT[Populate req.session.user and proceed]
104
+
105
+ %% reloadSessionUser
106
+ R1[Call reloadSessionUser helper] --> R2[Ensure req.session.user.id exists]
107
+ R2 --> R3[Query Sessions JOIN Users WHERE s.id = req.session.user.sessionId]
108
+ R3 -->|not found| RFALSE[Destroy session; clear cookies; return false]
109
+ R3 --> R4[Check expiry and Active and allowedApps]
110
+ R4 -->|fail| RFALSE
111
+ R4 --> R5[Update req.session.user values; fetch fullname from cookie or DB]
112
+ R5 --> R6[Save session and sync cookies]
113
+ R6 --> RTRUE[Return true]
114
+
115
+ %% Trusted device
116
+ TD1[Request has device_token cookie] --> TD2[If missing -> null]
117
+ TD2 --> TD3[Hash device token then query TrustedDevices for matching token and username and not expired]
118
+ TD3 -->|no row| TDNULL[return null]
119
+ TD3 -->|row found| TD4[If account inactive or not authorized for app -> return null]
120
+ TD4 --> TD5[Update TrustedDevices last used timestamp; return device user object]
121
+