@payez/next-mvp 3.0.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.
Files changed (654) hide show
  1. package/README.md +782 -0
  2. package/dist/api/auth-handler.d.ts +67 -0
  3. package/dist/api/auth-handler.js +397 -0
  4. package/dist/api/index.d.ts +10 -0
  5. package/dist/api/index.js +19 -0
  6. package/dist/api-handlers/account/change-password.d.ts +9 -0
  7. package/dist/api-handlers/account/change-password.js +112 -0
  8. package/dist/api-handlers/account/masked-info.d.ts +2 -0
  9. package/dist/api-handlers/account/masked-info.js +41 -0
  10. package/dist/api-handlers/account/profile.d.ts +3 -0
  11. package/dist/api-handlers/account/profile.js +63 -0
  12. package/dist/api-handlers/account/recovery/initiate.d.ts +2 -0
  13. package/dist/api-handlers/account/recovery/initiate.js +26 -0
  14. package/dist/api-handlers/account/recovery/send-code.d.ts +2 -0
  15. package/dist/api-handlers/account/recovery/send-code.js +28 -0
  16. package/dist/api-handlers/account/recovery/verify-code.d.ts +2 -0
  17. package/dist/api-handlers/account/recovery/verify-code.js +28 -0
  18. package/dist/api-handlers/account/reset-password.d.ts +2 -0
  19. package/dist/api-handlers/account/reset-password.js +26 -0
  20. package/dist/api-handlers/account/send-code.d.ts +24 -0
  21. package/dist/api-handlers/account/send-code.js +60 -0
  22. package/dist/api-handlers/account/update-phone.d.ts +27 -0
  23. package/dist/api-handlers/account/update-phone.js +64 -0
  24. package/dist/api-handlers/account/validate-password.d.ts +17 -0
  25. package/dist/api-handlers/account/validate-password.js +81 -0
  26. package/dist/api-handlers/account/verify-email.d.ts +26 -0
  27. package/dist/api-handlers/account/verify-email.js +106 -0
  28. package/dist/api-handlers/account/verify-sms.d.ts +26 -0
  29. package/dist/api-handlers/account/verify-sms.js +106 -0
  30. package/dist/api-handlers/admin/analytics.d.ts +20 -0
  31. package/dist/api-handlers/admin/analytics.js +379 -0
  32. package/dist/api-handlers/admin/audit.d.ts +20 -0
  33. package/dist/api-handlers/admin/audit.js +214 -0
  34. package/dist/api-handlers/admin/index.d.ts +21 -0
  35. package/dist/api-handlers/admin/index.js +41 -0
  36. package/dist/api-handlers/admin/redis-sessions.d.ts +36 -0
  37. package/dist/api-handlers/admin/redis-sessions.js +204 -0
  38. package/dist/api-handlers/admin/sessions.d.ts +21 -0
  39. package/dist/api-handlers/admin/sessions.js +284 -0
  40. package/dist/api-handlers/admin/site-logs.d.ts +46 -0
  41. package/dist/api-handlers/admin/site-logs.js +318 -0
  42. package/dist/api-handlers/admin/users.d.ts +20 -0
  43. package/dist/api-handlers/admin/users.js +222 -0
  44. package/dist/api-handlers/admin/vibe-data.d.ts +80 -0
  45. package/dist/api-handlers/admin/vibe-data.js +268 -0
  46. package/dist/api-handlers/anon/preferences.d.ts +37 -0
  47. package/dist/api-handlers/anon/preferences.js +96 -0
  48. package/dist/api-handlers/auth/jwks.d.ts +2 -0
  49. package/dist/api-handlers/auth/jwks.js +24 -0
  50. package/dist/api-handlers/auth/login.d.ts +42 -0
  51. package/dist/api-handlers/auth/login.js +178 -0
  52. package/dist/api-handlers/auth/refresh.d.ts +74 -0
  53. package/dist/api-handlers/auth/refresh.js +635 -0
  54. package/dist/api-handlers/auth/signout.d.ts +37 -0
  55. package/dist/api-handlers/auth/signout.js +187 -0
  56. package/dist/api-handlers/auth/status.d.ts +8 -0
  57. package/dist/api-handlers/auth/status.js +26 -0
  58. package/dist/api-handlers/auth/update-session.d.ts +37 -0
  59. package/dist/api-handlers/auth/update-session.js +95 -0
  60. package/dist/api-handlers/auth/validate.d.ts +6 -0
  61. package/dist/api-handlers/auth/validate.js +43 -0
  62. package/dist/api-handlers/auth/verify-code.d.ts +43 -0
  63. package/dist/api-handlers/auth/verify-code.js +94 -0
  64. package/dist/api-handlers/session/refresh-viability.d.ts +14 -0
  65. package/dist/api-handlers/session/refresh-viability.js +39 -0
  66. package/dist/api-handlers/session/viability.d.ts +13 -0
  67. package/dist/api-handlers/session/viability.js +146 -0
  68. package/dist/api-handlers/test/force-expire.d.ts +23 -0
  69. package/dist/api-handlers/test/force-expire.js +65 -0
  70. package/dist/auth/auth-decision.d.ts +39 -0
  71. package/dist/auth/auth-decision.js +182 -0
  72. package/dist/auth/auth-options.d.ts +57 -0
  73. package/dist/auth/auth-options.js +213 -0
  74. package/dist/auth/callbacks/index.d.ts +6 -0
  75. package/dist/auth/callbacks/index.js +12 -0
  76. package/dist/auth/callbacks/jwt.d.ts +45 -0
  77. package/dist/auth/callbacks/jwt.js +305 -0
  78. package/dist/auth/callbacks/session.d.ts +60 -0
  79. package/dist/auth/callbacks/session.js +170 -0
  80. package/dist/auth/callbacks/signin.d.ts +23 -0
  81. package/dist/auth/callbacks/signin.js +44 -0
  82. package/dist/auth/events/index.d.ts +4 -0
  83. package/dist/auth/events/index.js +8 -0
  84. package/dist/auth/events/signout.d.ts +17 -0
  85. package/dist/auth/events/signout.js +32 -0
  86. package/dist/auth/providers/credentials.d.ts +32 -0
  87. package/dist/auth/providers/credentials.js +223 -0
  88. package/dist/auth/providers/index.d.ts +5 -0
  89. package/dist/auth/providers/index.js +21 -0
  90. package/dist/auth/providers/oauth.d.ts +26 -0
  91. package/dist/auth/providers/oauth.js +105 -0
  92. package/dist/auth/route-config.d.ts +66 -0
  93. package/dist/auth/route-config.js +190 -0
  94. package/dist/auth/types/auth-types.d.ts +417 -0
  95. package/dist/auth/types/auth-types.js +53 -0
  96. package/dist/auth/types/index.d.ts +6 -0
  97. package/dist/auth/types/index.js +22 -0
  98. package/dist/auth/unauthenticated-routes.d.ts +1 -0
  99. package/dist/auth/unauthenticated-routes.js +19 -0
  100. package/dist/auth/utils/idp-client.d.ts +94 -0
  101. package/dist/auth/utils/idp-client.js +383 -0
  102. package/dist/auth/utils/index.d.ts +5 -0
  103. package/dist/auth/utils/index.js +21 -0
  104. package/dist/auth/utils/token-utils.d.ts +84 -0
  105. package/dist/auth/utils/token-utils.js +219 -0
  106. package/dist/client/AuthContext.d.ts +19 -0
  107. package/dist/client/AuthContext.js +112 -0
  108. package/dist/client/fetch-with-auth.d.ts +11 -0
  109. package/dist/client/fetch-with-auth.js +44 -0
  110. package/dist/client/fetchWithSession.d.ts +3 -0
  111. package/dist/client/fetchWithSession.js +24 -0
  112. package/dist/client/index.d.ts +9 -0
  113. package/dist/client/index.js +20 -0
  114. package/dist/client/useAnonSession.d.ts +36 -0
  115. package/dist/client/useAnonSession.js +99 -0
  116. package/dist/components/SessionSync.d.ts +13 -0
  117. package/dist/components/SessionSync.js +119 -0
  118. package/dist/components/SignalRHealthCheck.d.ts +10 -0
  119. package/dist/components/SignalRHealthCheck.js +97 -0
  120. package/dist/components/account/UserAvatarMenu.d.ts +20 -0
  121. package/dist/components/account/UserAvatarMenu.js +80 -0
  122. package/dist/components/account/index.d.ts +7 -0
  123. package/dist/components/account/index.js +10 -0
  124. package/dist/components/admin/AlertSettingsTab.d.ts +48 -0
  125. package/dist/components/admin/AlertSettingsTab.js +351 -0
  126. package/dist/components/admin/AnalyticsTab.d.ts +22 -0
  127. package/dist/components/admin/AnalyticsTab.js +167 -0
  128. package/dist/components/admin/DataBrowserTab.d.ts +19 -0
  129. package/dist/components/admin/DataBrowserTab.js +252 -0
  130. package/dist/components/admin/LoggingSettingsTab.d.ts +73 -0
  131. package/dist/components/admin/LoggingSettingsTab.js +339 -0
  132. package/dist/components/admin/SessionsTab.d.ts +37 -0
  133. package/dist/components/admin/SessionsTab.js +165 -0
  134. package/dist/components/admin/StatsTab.d.ts +53 -0
  135. package/dist/components/admin/StatsTab.js +161 -0
  136. package/dist/components/admin/VibeAdminContext.d.ts +32 -0
  137. package/dist/components/admin/VibeAdminContext.js +38 -0
  138. package/dist/components/admin/VibeAdminLayout.d.ts +11 -0
  139. package/dist/components/admin/VibeAdminLayout.js +69 -0
  140. package/dist/components/admin/index.d.ts +29 -0
  141. package/dist/components/admin/index.js +44 -0
  142. package/dist/components/auth/FederatedAuthSection.d.ts +8 -0
  143. package/dist/components/auth/FederatedAuthSection.js +45 -0
  144. package/dist/components/auth/ModeAwareLoginPage.d.ts +10 -0
  145. package/dist/components/auth/ModeAwareLoginPage.js +42 -0
  146. package/dist/components/auth/ModeAwareSignupPage.d.ts +9 -0
  147. package/dist/components/auth/ModeAwareSignupPage.js +78 -0
  148. package/dist/components/auth/TraditionalAuthSection.d.ts +14 -0
  149. package/dist/components/auth/TraditionalAuthSection.js +20 -0
  150. package/dist/components/recovery/CompleteStep.d.ts +5 -0
  151. package/dist/components/recovery/CompleteStep.js +8 -0
  152. package/dist/components/recovery/InitiateRecoveryStep.d.ts +8 -0
  153. package/dist/components/recovery/InitiateRecoveryStep.js +20 -0
  154. package/dist/components/recovery/SelectMethodStep.d.ts +8 -0
  155. package/dist/components/recovery/SelectMethodStep.js +8 -0
  156. package/dist/components/recovery/SetPasswordStep.d.ts +6 -0
  157. package/dist/components/recovery/SetPasswordStep.js +20 -0
  158. package/dist/components/recovery/VerifyCodeStep.d.ts +10 -0
  159. package/dist/components/recovery/VerifyCodeStep.js +24 -0
  160. package/dist/components/reserved/ReservedRecoveryWarning.d.ts +38 -0
  161. package/dist/components/reserved/ReservedRecoveryWarning.js +92 -0
  162. package/dist/components/reserved/ReservedStatusBox.d.ts +30 -0
  163. package/dist/components/reserved/ReservedStatusBox.js +71 -0
  164. package/dist/components/ui/BetaBadge.d.ts +29 -0
  165. package/dist/components/ui/BetaBadge.js +38 -0
  166. package/dist/components/ui/Footer.d.ts +37 -0
  167. package/dist/components/ui/Footer.js +41 -0
  168. package/dist/config/env.d.ts +66 -0
  169. package/dist/config/env.js +57 -0
  170. package/dist/config/logger.d.ts +57 -0
  171. package/dist/config/logger.js +73 -0
  172. package/dist/config/logging-config.d.ts +30 -0
  173. package/dist/config/logging-config.js +122 -0
  174. package/dist/config/unauthenticated-routes.d.ts +17 -0
  175. package/dist/config/unauthenticated-routes.js +24 -0
  176. package/dist/config/vibe-log-transport.d.ts +79 -0
  177. package/dist/config/vibe-log-transport.js +203 -0
  178. package/dist/edge/internal-api-url.d.ts +53 -0
  179. package/dist/edge/internal-api-url.js +63 -0
  180. package/dist/edge/middleware.d.ts +14 -0
  181. package/dist/edge/middleware.js +32 -0
  182. package/dist/hooks/useAuth.d.ts +23 -0
  183. package/dist/hooks/useAuth.js +81 -0
  184. package/dist/hooks/useAuthSettings.d.ts +59 -0
  185. package/dist/hooks/useAuthSettings.js +93 -0
  186. package/dist/hooks/useAvailableProviders.d.ts +45 -0
  187. package/dist/hooks/useAvailableProviders.js +108 -0
  188. package/dist/hooks/usePasswordValidation.d.ts +27 -0
  189. package/dist/hooks/usePasswordValidation.js +102 -0
  190. package/dist/hooks/useProfile.d.ts +15 -0
  191. package/dist/hooks/useProfile.js +59 -0
  192. package/dist/hooks/usePublicAuthSettings.d.ts +56 -0
  193. package/dist/hooks/usePublicAuthSettings.js +131 -0
  194. package/dist/hooks/useSessionExpiration.d.ts +57 -0
  195. package/dist/hooks/useSessionExpiration.js +72 -0
  196. package/dist/hooks/useViabilitySession.d.ts +75 -0
  197. package/dist/hooks/useViabilitySession.js +268 -0
  198. package/dist/index.d.ts +12 -0
  199. package/dist/index.js +54 -0
  200. package/dist/lib/anon-session.d.ts +74 -0
  201. package/dist/lib/anon-session.js +169 -0
  202. package/dist/lib/api-handler.d.ts +123 -0
  203. package/dist/lib/api-handler.js +478 -0
  204. package/dist/lib/app-slug.d.ts +95 -0
  205. package/dist/lib/app-slug.js +172 -0
  206. package/dist/lib/demo-mode.d.ts +6 -0
  207. package/dist/lib/demo-mode.js +16 -0
  208. package/dist/lib/geolocation.d.ts +64 -0
  209. package/dist/lib/geolocation.js +235 -0
  210. package/dist/lib/idp-client-config.d.ts +75 -0
  211. package/dist/lib/idp-client-config.js +351 -0
  212. package/dist/lib/idp-fetch.d.ts +14 -0
  213. package/dist/lib/idp-fetch.js +91 -0
  214. package/dist/lib/internal-api.d.ts +87 -0
  215. package/dist/lib/internal-api.js +122 -0
  216. package/dist/lib/jwt-decode-client.d.ts +10 -0
  217. package/dist/lib/jwt-decode-client.js +46 -0
  218. package/dist/lib/jwt-decode.d.ts +48 -0
  219. package/dist/lib/jwt-decode.js +57 -0
  220. package/dist/lib/nextauth-secret.d.ts +10 -0
  221. package/dist/lib/nextauth-secret.js +104 -0
  222. package/dist/lib/rate-limit-service.d.ts +23 -0
  223. package/dist/lib/rate-limit-service.js +6 -0
  224. package/dist/lib/redis.d.ts +5 -0
  225. package/dist/lib/redis.js +28 -0
  226. package/dist/lib/refresh-token-validator.d.ts +13 -0
  227. package/dist/lib/refresh-token-validator.js +117 -0
  228. package/dist/lib/roles.d.ts +145 -0
  229. package/dist/lib/roles.js +168 -0
  230. package/dist/lib/secret-validation.d.ts +4 -0
  231. package/dist/lib/secret-validation.js +14 -0
  232. package/dist/lib/session-store.d.ts +166 -0
  233. package/dist/lib/session-store.js +537 -0
  234. package/dist/lib/session.d.ts +21 -0
  235. package/dist/lib/session.js +26 -0
  236. package/dist/lib/site-logger.d.ts +214 -0
  237. package/dist/lib/site-logger.js +210 -0
  238. package/dist/lib/standardized-client-api.d.ts +161 -0
  239. package/dist/lib/standardized-client-api.js +786 -0
  240. package/dist/lib/startup-init.d.ts +40 -0
  241. package/dist/lib/startup-init.js +261 -0
  242. package/dist/lib/test-aware-get-token.d.ts +2 -0
  243. package/dist/lib/test-aware-get-token.js +81 -0
  244. package/dist/lib/token-expiry.d.ts +14 -0
  245. package/dist/lib/token-expiry.js +39 -0
  246. package/dist/lib/token-lifecycle.d.ts +52 -0
  247. package/dist/lib/token-lifecycle.js +398 -0
  248. package/dist/lib/types/api-responses.d.ts +128 -0
  249. package/dist/lib/types/api-responses.js +171 -0
  250. package/dist/lib/user-agent-parser.d.ts +50 -0
  251. package/dist/lib/user-agent-parser.js +220 -0
  252. package/dist/logging/api/admin-analytics.d.ts +3 -0
  253. package/dist/logging/api/admin-analytics.js +45 -0
  254. package/dist/logging/api/audit-log.d.ts +3 -0
  255. package/dist/logging/api/audit-log.js +52 -0
  256. package/dist/logging/components/AdminAnalyticsLayout.d.ts +10 -0
  257. package/dist/logging/components/AdminAnalyticsLayout.js +11 -0
  258. package/dist/logging/components/AuditLogViewer.d.ts +7 -0
  259. package/dist/logging/components/AuditLogViewer.js +51 -0
  260. package/dist/logging/components/ErrorMetricsCard.d.ts +7 -0
  261. package/dist/logging/components/ErrorMetricsCard.js +16 -0
  262. package/dist/logging/components/HealthMetricsCard.d.ts +7 -0
  263. package/dist/logging/components/HealthMetricsCard.js +19 -0
  264. package/dist/logging/hooks/useAdminAnalytics.d.ts +24 -0
  265. package/dist/logging/hooks/useAdminAnalytics.js +22 -0
  266. package/dist/logging/hooks/useAuditLog.d.ts +6 -0
  267. package/dist/logging/hooks/useAuditLog.js +25 -0
  268. package/dist/logging/hooks/useErrorMetrics.d.ts +6 -0
  269. package/dist/logging/hooks/useErrorMetrics.js +38 -0
  270. package/dist/logging/hooks/useHealthMetrics.d.ts +6 -0
  271. package/dist/logging/hooks/useHealthMetrics.js +41 -0
  272. package/dist/logging/index.d.ts +11 -0
  273. package/dist/logging/index.js +40 -0
  274. package/dist/logging/types/analytics.d.ts +68 -0
  275. package/dist/logging/types/analytics.js +3 -0
  276. package/dist/logging/types/audit.d.ts +29 -0
  277. package/dist/logging/types/audit.js +2 -0
  278. package/dist/logging/types/index.d.ts +2 -0
  279. package/dist/logging/types/index.js +19 -0
  280. package/dist/middleware/auth-decision.d.ts +33 -0
  281. package/dist/middleware/auth-decision.js +65 -0
  282. package/dist/middleware/create-middleware.d.ts +100 -0
  283. package/dist/middleware/create-middleware.js +445 -0
  284. package/dist/middleware/rbac-check.d.ts +44 -0
  285. package/dist/middleware/rbac-check.js +191 -0
  286. package/dist/middleware/twofa-presets.d.ts +134 -0
  287. package/dist/middleware/twofa-presets.js +175 -0
  288. package/dist/models/DecodedAccessToken.d.ts +17 -0
  289. package/dist/models/DecodedAccessToken.js +2 -0
  290. package/dist/models/SessionModel.d.ts +122 -0
  291. package/dist/models/SessionModel.js +136 -0
  292. package/dist/pages/admin-login/page.d.ts +31 -0
  293. package/dist/pages/admin-login/page.js +83 -0
  294. package/dist/pages/admin-roles/RolesAdminPage.d.ts +15 -0
  295. package/dist/pages/admin-roles/RolesAdminPage.js +78 -0
  296. package/dist/pages/admin-roles/index.d.ts +8 -0
  297. package/dist/pages/admin-roles/index.js +15 -0
  298. package/dist/pages/admin-roles/modals.d.ts +72 -0
  299. package/dist/pages/admin-roles/modals.js +154 -0
  300. package/dist/pages/client-admin/ClientSiteAdminPage.d.ts +79 -0
  301. package/dist/pages/client-admin/ClientSiteAdminPage.js +177 -0
  302. package/dist/pages/client-admin/index.d.ts +32 -0
  303. package/dist/pages/client-admin/index.js +37 -0
  304. package/dist/pages/login/page.d.ts +22 -0
  305. package/dist/pages/login/page.js +239 -0
  306. package/dist/pages/profile/EnhancedProfilePage.d.ts +13 -0
  307. package/dist/pages/profile/EnhancedProfilePage.js +150 -0
  308. package/dist/pages/profile/index.d.ts +8 -0
  309. package/dist/pages/profile/index.js +16 -0
  310. package/dist/pages/profile/page.d.ts +19 -0
  311. package/dist/pages/profile/page.js +47 -0
  312. package/dist/pages/profile/profile-patch.d.ts +1 -0
  313. package/dist/pages/profile/profile-patch.js +281 -0
  314. package/dist/pages/recovery/page.d.ts +1 -0
  315. package/dist/pages/recovery/page.js +142 -0
  316. package/dist/pages/roles/MyRolesPage.d.ts +24 -0
  317. package/dist/pages/roles/MyRolesPage.js +71 -0
  318. package/dist/pages/roles/components.d.ts +63 -0
  319. package/dist/pages/roles/components.js +108 -0
  320. package/dist/pages/roles/index.d.ts +8 -0
  321. package/dist/pages/roles/index.js +19 -0
  322. package/dist/pages/security/EnhancedSecurityPage.d.ts +14 -0
  323. package/dist/pages/security/EnhancedSecurityPage.js +248 -0
  324. package/dist/pages/security/index.d.ts +8 -0
  325. package/dist/pages/security/index.js +16 -0
  326. package/dist/pages/security/page.d.ts +21 -0
  327. package/dist/pages/security/page.js +212 -0
  328. package/dist/pages/security/security-patch.d.ts +1 -0
  329. package/dist/pages/security/security-patch.js +302 -0
  330. package/dist/pages/settings/EnhancedSettingsPage.d.ts +46 -0
  331. package/dist/pages/settings/EnhancedSettingsPage.js +231 -0
  332. package/dist/pages/settings/index.d.ts +8 -0
  333. package/dist/pages/settings/index.js +16 -0
  334. package/dist/pages/settings/page.d.ts +7 -0
  335. package/dist/pages/settings/page.js +26 -0
  336. package/dist/pages/showcase/ShowcasePage.d.ts +13 -0
  337. package/dist/pages/showcase/ShowcasePage.js +140 -0
  338. package/dist/pages/showcase/index.d.ts +12 -0
  339. package/dist/pages/showcase/index.js +17 -0
  340. package/dist/pages/test-env/EmergencyLogoutPage.d.ts +14 -0
  341. package/dist/pages/test-env/EmergencyLogoutPage.js +98 -0
  342. package/dist/pages/test-env/JwtInspectPage.d.ts +14 -0
  343. package/dist/pages/test-env/JwtInspectPage.js +114 -0
  344. package/dist/pages/test-env/RefreshTokenPage.d.ts +15 -0
  345. package/dist/pages/test-env/RefreshTokenPage.js +91 -0
  346. package/dist/pages/test-env/TestEnvPage.d.ts +13 -0
  347. package/dist/pages/test-env/TestEnvPage.js +49 -0
  348. package/dist/pages/test-env/index.d.ts +24 -0
  349. package/dist/pages/test-env/index.js +32 -0
  350. package/dist/pages/verify-code/page.d.ts +30 -0
  351. package/dist/pages/verify-code/page.js +408 -0
  352. package/dist/routes/account/index.d.ts +28 -0
  353. package/dist/routes/account/index.js +71 -0
  354. package/dist/routes/account/masked-info.d.ts +33 -0
  355. package/dist/routes/account/masked-info.js +39 -0
  356. package/dist/routes/account/send-code.d.ts +37 -0
  357. package/dist/routes/account/send-code.js +42 -0
  358. package/dist/routes/account/update-phone.d.ts +13 -0
  359. package/dist/routes/account/update-phone.js +17 -0
  360. package/dist/routes/account/verify-email.d.ts +38 -0
  361. package/dist/routes/account/verify-email.js +43 -0
  362. package/dist/routes/account/verify-sms.d.ts +38 -0
  363. package/dist/routes/account/verify-sms.js +43 -0
  364. package/dist/routes/auth/index.d.ts +19 -0
  365. package/dist/routes/auth/index.js +64 -0
  366. package/dist/routes/auth/logout.d.ts +31 -0
  367. package/dist/routes/auth/logout.js +113 -0
  368. package/dist/routes/auth/nextauth.d.ts +19 -0
  369. package/dist/routes/auth/nextauth.js +72 -0
  370. package/dist/routes/auth/refresh.d.ts +30 -0
  371. package/dist/routes/auth/refresh.js +51 -0
  372. package/dist/routes/auth/session.d.ts +72 -0
  373. package/dist/routes/auth/session.js +180 -0
  374. package/dist/routes/auth/settings.d.ts +25 -0
  375. package/dist/routes/auth/settings.js +55 -0
  376. package/dist/routes/auth/viability.d.ts +52 -0
  377. package/dist/routes/auth/viability.js +201 -0
  378. package/dist/routes/index.d.ts +12 -0
  379. package/dist/routes/index.js +54 -0
  380. package/dist/routes/session/index.d.ts +6 -0
  381. package/dist/routes/session/index.js +10 -0
  382. package/dist/routes/session/refresh-viability.d.ts +16 -0
  383. package/dist/routes/session/refresh-viability.js +20 -0
  384. package/dist/services/signalrActivityService.d.ts +44 -0
  385. package/dist/services/signalrActivityService.js +257 -0
  386. package/dist/stores/authStore.d.ts +154 -0
  387. package/dist/stores/authStore.js +1531 -0
  388. package/dist/theme/ThemeProvider.d.ts +14 -0
  389. package/dist/theme/ThemeProvider.js +28 -0
  390. package/dist/theme/default.d.ts +8 -0
  391. package/dist/theme/default.js +33 -0
  392. package/dist/theme/index.d.ts +15 -0
  393. package/dist/theme/index.js +25 -0
  394. package/dist/theme/types.d.ts +56 -0
  395. package/dist/theme/types.js +8 -0
  396. package/dist/theme/useTheme.d.ts +60 -0
  397. package/dist/theme/useTheme.js +63 -0
  398. package/dist/theme/utils.d.ts +13 -0
  399. package/dist/theme/utils.js +39 -0
  400. package/dist/types/api.d.ts +134 -0
  401. package/dist/types/api.js +44 -0
  402. package/dist/types/auth.d.ts +19 -0
  403. package/dist/types/auth.js +2 -0
  404. package/dist/types/logging.d.ts +42 -0
  405. package/dist/types/logging.js +2 -0
  406. package/dist/types/recovery.d.ts +48 -0
  407. package/dist/types/recovery.js +2 -0
  408. package/dist/types/security.d.ts +1 -0
  409. package/dist/types/security.js +2 -0
  410. package/dist/utils/api.d.ts +85 -0
  411. package/dist/utils/api.js +287 -0
  412. package/dist/utils/circuitBreaker.d.ts +43 -0
  413. package/dist/utils/circuitBreaker.js +91 -0
  414. package/dist/utils/error-message.d.ts +1 -0
  415. package/dist/utils/error-message.js +103 -0
  416. package/dist/utils/layout/reservedSpace.d.ts +59 -0
  417. package/dist/utils/layout/reservedSpace.js +102 -0
  418. package/dist/utils/logout.d.ts +14 -0
  419. package/dist/utils/logout.js +32 -0
  420. package/dist/vibe/client.d.ts +261 -0
  421. package/dist/vibe/client.js +445 -0
  422. package/dist/vibe/errors.d.ts +83 -0
  423. package/dist/vibe/errors.js +146 -0
  424. package/dist/vibe/generic.d.ts +234 -0
  425. package/dist/vibe/generic.js +369 -0
  426. package/dist/vibe/hooks/index.d.ts +169 -0
  427. package/dist/vibe/hooks/index.js +252 -0
  428. package/dist/vibe/index.d.ts +23 -0
  429. package/dist/vibe/index.js +67 -0
  430. package/dist/vibe/sessions.d.ts +161 -0
  431. package/dist/vibe/sessions.js +391 -0
  432. package/dist/vibe/types.d.ts +353 -0
  433. package/dist/vibe/types.js +315 -0
  434. package/package.json +855 -0
  435. package/scripts/check-internal-url-usage.sh +73 -0
  436. package/scripts/dev-broker.ps1 +35 -0
  437. package/scripts/dev-local.ps1 +45 -0
  438. package/src/api/auth-handler.ts +550 -0
  439. package/src/api/index.ts +18 -0
  440. package/src/api-handlers/account/change-password.ts +145 -0
  441. package/src/api-handlers/account/masked-info.ts +45 -0
  442. package/src/api-handlers/account/profile.ts +80 -0
  443. package/src/api-handlers/account/recovery/initiate.ts +23 -0
  444. package/src/api-handlers/account/recovery/send-code.ts +25 -0
  445. package/src/api-handlers/account/recovery/verify-code.ts +25 -0
  446. package/src/api-handlers/account/reset-password.ts +23 -0
  447. package/src/api-handlers/account/send-code.ts +76 -0
  448. package/src/api-handlers/account/update-phone.ts +79 -0
  449. package/src/api-handlers/account/validate-password.ts +118 -0
  450. package/src/api-handlers/account/verify-email.ts +125 -0
  451. package/src/api-handlers/account/verify-sms.ts +125 -0
  452. package/src/api-handlers/admin/analytics.ts +445 -0
  453. package/src/api-handlers/admin/audit.ts +225 -0
  454. package/src/api-handlers/admin/index.ts +59 -0
  455. package/src/api-handlers/admin/redis-sessions.ts +253 -0
  456. package/src/api-handlers/admin/sessions.ts +320 -0
  457. package/src/api-handlers/admin/site-logs.ts +367 -0
  458. package/src/api-handlers/admin/users.ts +244 -0
  459. package/src/api-handlers/admin/vibe-data.ts +326 -0
  460. package/src/api-handlers/anon/preferences.ts +123 -0
  461. package/src/api-handlers/auth/jwks.ts +20 -0
  462. package/src/api-handlers/auth/login.ts +240 -0
  463. package/src/api-handlers/auth/refresh.ts +687 -0
  464. package/src/api-handlers/auth/signout.ts +212 -0
  465. package/src/api-handlers/auth/status.ts +23 -0
  466. package/src/api-handlers/auth/update-session.ts +125 -0
  467. package/src/api-handlers/auth/validate.ts +44 -0
  468. package/src/api-handlers/auth/verify-code.ts +129 -0
  469. package/src/api-handlers/session/refresh-viability.ts +36 -0
  470. package/src/api-handlers/session/viability.ts +166 -0
  471. package/src/api-handlers/test/force-expire.ts +67 -0
  472. package/src/auth/auth-decision.ts +230 -0
  473. package/src/auth/auth-options.ts +237 -0
  474. package/src/auth/callbacks/index.ts +7 -0
  475. package/src/auth/callbacks/jwt.ts +382 -0
  476. package/src/auth/callbacks/session.ts +243 -0
  477. package/src/auth/callbacks/signin.ts +56 -0
  478. package/src/auth/events/index.ts +5 -0
  479. package/src/auth/events/signout.ts +33 -0
  480. package/src/auth/providers/credentials.ts +256 -0
  481. package/src/auth/providers/index.ts +6 -0
  482. package/src/auth/providers/oauth.ts +114 -0
  483. package/src/auth/route-config.ts +220 -0
  484. package/src/auth/types/auth-types.ts +555 -0
  485. package/src/auth/types/index.ts +7 -0
  486. package/src/auth/unauthenticated-routes.ts +3 -0
  487. package/src/auth/utils/idp-client.ts +444 -0
  488. package/src/auth/utils/index.ts +6 -0
  489. package/src/auth/utils/token-utils.ts +244 -0
  490. package/src/client/AuthContext.tsx +140 -0
  491. package/src/client/fetch-with-auth.ts +48 -0
  492. package/src/client/fetchWithSession.ts +21 -0
  493. package/src/client/index.ts +13 -0
  494. package/src/client/useAnonSession.ts +131 -0
  495. package/src/components/SessionSync.tsx +137 -0
  496. package/src/components/SignalRHealthCheck.tsx +131 -0
  497. package/src/components/account/UserAvatarMenu.tsx +217 -0
  498. package/src/components/account/index.ts +8 -0
  499. package/src/components/admin/AlertSettingsTab.tsx +728 -0
  500. package/src/components/admin/AnalyticsTab.tsx +703 -0
  501. package/src/components/admin/DataBrowserTab.tsx +505 -0
  502. package/src/components/admin/LoggingSettingsTab.tsx +665 -0
  503. package/src/components/admin/SessionsTab.tsx +414 -0
  504. package/src/components/admin/StatsTab.tsx +379 -0
  505. package/src/components/admin/VibeAdminContext.tsx +87 -0
  506. package/src/components/admin/VibeAdminLayout.tsx +185 -0
  507. package/src/components/admin/index.ts +59 -0
  508. package/src/components/auth/FederatedAuthSection.tsx +95 -0
  509. package/src/components/auth/ModeAwareLoginPage.tsx +135 -0
  510. package/src/components/auth/ModeAwareSignupPage.tsx +267 -0
  511. package/src/components/auth/TraditionalAuthSection.tsx +99 -0
  512. package/src/components/recovery/CompleteStep.tsx +36 -0
  513. package/src/components/recovery/InitiateRecoveryStep.tsx +68 -0
  514. package/src/components/recovery/SelectMethodStep.tsx +73 -0
  515. package/src/components/recovery/SetPasswordStep.tsx +97 -0
  516. package/src/components/recovery/VerifyCodeStep.tsx +90 -0
  517. package/src/components/reserved/ReservedRecoveryWarning.tsx +160 -0
  518. package/src/components/reserved/ReservedStatusBox.tsx +118 -0
  519. package/src/components/ui/BetaBadge.tsx +58 -0
  520. package/src/components/ui/Footer.tsx +93 -0
  521. package/src/config/env.ts +57 -0
  522. package/src/config/logger.ts +62 -0
  523. package/src/config/logging-config.ts +82 -0
  524. package/src/config/unauthenticated-routes.ts +19 -0
  525. package/src/config/vibe-log-transport.ts +250 -0
  526. package/src/edge/internal-api-url.ts +65 -0
  527. package/src/edge/middleware.ts +42 -0
  528. package/src/hooks/useAuth.ts +115 -0
  529. package/src/hooks/useAuthSettings.ts +97 -0
  530. package/src/hooks/useAvailableProviders.ts +118 -0
  531. package/src/hooks/usePasswordValidation.ts +127 -0
  532. package/src/hooks/useProfile.ts +75 -0
  533. package/src/hooks/usePublicAuthSettings.ts +149 -0
  534. package/src/hooks/useSessionExpiration.ts +102 -0
  535. package/src/hooks/useViabilitySession.ts +335 -0
  536. package/src/index.ts +63 -0
  537. package/src/lib/anon-session.ts +213 -0
  538. package/src/lib/api-handler.ts +625 -0
  539. package/src/lib/app-slug.ts +178 -0
  540. package/src/lib/demo-mode.ts +13 -0
  541. package/src/lib/geolocation.ts +265 -0
  542. package/src/lib/idp-client-config.ts +442 -0
  543. package/src/lib/idp-fetch.ts +101 -0
  544. package/src/lib/internal-api.ts +171 -0
  545. package/src/lib/jwt-decode-client.ts +45 -0
  546. package/src/lib/jwt-decode.ts +83 -0
  547. package/src/lib/nextauth-secret.ts +126 -0
  548. package/src/lib/rate-limit-service.ts +9 -0
  549. package/src/lib/redis.ts +27 -0
  550. package/src/lib/refresh-token-validator.ts +64 -0
  551. package/src/lib/roles.ts +177 -0
  552. package/src/lib/secret-validation.ts +8 -0
  553. package/src/lib/session-store.ts +637 -0
  554. package/src/lib/session.ts +34 -0
  555. package/src/lib/site-logger.ts +245 -0
  556. package/src/lib/standardized-client-api.ts +896 -0
  557. package/src/lib/startup-init.ts +247 -0
  558. package/src/lib/test-aware-get-token.ts +30 -0
  559. package/src/lib/token-expiry.ts +40 -0
  560. package/src/lib/token-lifecycle.ts +477 -0
  561. package/src/lib/types/api-responses.ts +336 -0
  562. package/src/lib/user-agent-parser.ts +252 -0
  563. package/src/logging/api/admin-analytics.ts +51 -0
  564. package/src/logging/api/audit-log.ts +53 -0
  565. package/src/logging/components/AdminAnalyticsLayout.tsx +49 -0
  566. package/src/logging/components/AuditLogViewer.tsx +125 -0
  567. package/src/logging/components/ErrorMetricsCard.tsx +98 -0
  568. package/src/logging/components/HealthMetricsCard.tsx +70 -0
  569. package/src/logging/hooks/useAdminAnalytics.ts +22 -0
  570. package/src/logging/hooks/useAuditLog.ts +24 -0
  571. package/src/logging/hooks/useErrorMetrics.ts +40 -0
  572. package/src/logging/hooks/useHealthMetrics.ts +44 -0
  573. package/src/logging/index.ts +18 -0
  574. package/src/logging/types/analytics.ts +81 -0
  575. package/src/logging/types/audit.ts +31 -0
  576. package/src/logging/types/index.ts +3 -0
  577. package/src/middleware/auth-decision.ts +43 -0
  578. package/src/middleware/create-middleware.ts +626 -0
  579. package/src/middleware/rbac-check.ts +244 -0
  580. package/src/middleware/twofa-presets.ts +224 -0
  581. package/src/models/DecodedAccessToken.ts +17 -0
  582. package/src/models/SessionModel.ts +258 -0
  583. package/src/pages/admin-login/page.tsx +229 -0
  584. package/src/pages/admin-roles/RolesAdminPage.tsx +357 -0
  585. package/src/pages/admin-roles/index.ts +9 -0
  586. package/src/pages/admin-roles/modals.tsx +469 -0
  587. package/src/pages/client-admin/ClientSiteAdminPage.tsx +380 -0
  588. package/src/pages/client-admin/index.ts +33 -0
  589. package/src/pages/login/page.tsx +463 -0
  590. package/src/pages/profile/EnhancedProfilePage.tsx +479 -0
  591. package/src/pages/profile/index.ts +9 -0
  592. package/src/pages/profile/page.tsx +166 -0
  593. package/src/pages/recovery/page.tsx +234 -0
  594. package/src/pages/roles/MyRolesPage.tsx +211 -0
  595. package/src/pages/roles/components.tsx +294 -0
  596. package/src/pages/roles/index.ts +17 -0
  597. package/src/pages/security/EnhancedSecurityPage.tsx +574 -0
  598. package/src/pages/security/index.ts +9 -0
  599. package/src/pages/security/page.tsx +507 -0
  600. package/src/pages/settings/EnhancedSettingsPage.tsx +642 -0
  601. package/src/pages/settings/index.ts +9 -0
  602. package/src/pages/settings/page.tsx +47 -0
  603. package/src/pages/showcase/ShowcasePage.tsx +530 -0
  604. package/src/pages/showcase/index.ts +13 -0
  605. package/src/pages/test-env/EmergencyLogoutPage.tsx +179 -0
  606. package/src/pages/test-env/JwtInspectPage.tsx +418 -0
  607. package/src/pages/test-env/RefreshTokenPage.tsx +155 -0
  608. package/src/pages/test-env/TestEnvPage.tsx +116 -0
  609. package/src/pages/test-env/index.ts +25 -0
  610. package/src/pages/verify-code/page.tsx +648 -0
  611. package/src/routes/account/index.ts +32 -0
  612. package/src/routes/account/masked-info.ts +37 -0
  613. package/src/routes/account/send-code.ts +40 -0
  614. package/src/routes/account/update-phone.ts +13 -0
  615. package/src/routes/account/verify-email.ts +41 -0
  616. package/src/routes/account/verify-sms.ts +41 -0
  617. package/src/routes/auth/index.ts +23 -0
  618. package/src/routes/auth/logout.ts +127 -0
  619. package/src/routes/auth/nextauth.ts +71 -0
  620. package/src/routes/auth/refresh.ts +54 -0
  621. package/src/routes/auth/session.ts +193 -0
  622. package/src/routes/auth/settings.ts +75 -0
  623. package/src/routes/auth/viability.ts +220 -0
  624. package/src/routes/index.ts +18 -0
  625. package/src/routes/session/index.ts +7 -0
  626. package/src/routes/session/refresh-viability.ts +17 -0
  627. package/src/services/signalrActivityService.ts +258 -0
  628. package/src/stores/authStore.ts +1904 -0
  629. package/src/templates/instrumentation.ts +41 -0
  630. package/src/theme/ThemeProvider.tsx +39 -0
  631. package/src/theme/default.ts +33 -0
  632. package/src/theme/index.ts +31 -0
  633. package/src/theme/types.ts +69 -0
  634. package/src/theme/useTheme.ts +57 -0
  635. package/src/theme/utils.ts +40 -0
  636. package/src/types/api.ts +13 -0
  637. package/src/types/auth.d.ts +15 -0
  638. package/src/types/auth.ts +22 -0
  639. package/src/types/logging.ts +11 -0
  640. package/src/types/next-auth.d.ts +15 -0
  641. package/src/types/recovery.ts +54 -0
  642. package/src/types/security.ts +1 -0
  643. package/src/utils/api.ts +353 -0
  644. package/src/utils/circuitBreaker.ts +40 -0
  645. package/src/utils/error-message.ts +108 -0
  646. package/src/utils/layout/reservedSpace.ts +124 -0
  647. package/src/utils/logout.ts +30 -0
  648. package/src/vibe/client.ts +590 -0
  649. package/src/vibe/errors.ts +185 -0
  650. package/src/vibe/generic.ts +429 -0
  651. package/src/vibe/hooks/index.ts +367 -0
  652. package/src/vibe/index.ts +121 -0
  653. package/src/vibe/sessions.ts +551 -0
  654. package/src/vibe/types.ts +577 -0
@@ -0,0 +1,626 @@
1
+ /**
2
+ * MVP Middleware - Authentication & Route Protection
3
+ *
4
+ * Creates a Next.js middleware handler that protects routes based on:
5
+ * - Authentication status (session cookie → Redis viability check)
6
+ * - 2FA completion status
7
+ * - RBAC permissions (if enabled)
8
+ *
9
+ * Usage: Export the result from your app's middleware.ts:
10
+ * export default createMvpMiddleware();
11
+ *
12
+ * With custom options:
13
+ * export default createMvpMiddleware({
14
+ * circuitBreaker: myCircuitBreaker,
15
+ * logger: myLogger,
16
+ * viabilityEndpoint: '/api/session/refresh-viability',
17
+ * });
18
+ */
19
+
20
+ import { NextRequest, NextResponse } from 'next/server';
21
+ import { isUnauthenticatedRoute, should2FABypass } from '../auth/route-config';
22
+ import { getInternalApiUrl } from '../edge/internal-api-url';
23
+ import { getSessionCookieName, getSecureSessionCookieName } from '../lib/app-slug';
24
+ import { checkPagePermission, isRBACEnabled } from './rbac-check';
25
+
26
+ // =============================================================================
27
+ // TYPES
28
+ // =============================================================================
29
+
30
+ export interface AuthContext {
31
+ pathname: string;
32
+ isPublicRoute: boolean;
33
+ isLoginPage: boolean;
34
+ searchParams: URLSearchParams;
35
+ sessionPointer: SessionPointer;
36
+ sessionStatus: SessionStatus;
37
+ circuitBreakerOpen: boolean;
38
+ }
39
+
40
+ export interface SessionPointer {
41
+ exists: boolean;
42
+ sessionToken?: string;
43
+ expired?: boolean;
44
+ hasRefreshToken?: boolean;
45
+ roles: string[];
46
+ clientId: string;
47
+ }
48
+
49
+ export interface SessionStatus {
50
+ exists: boolean | null; // null = check failed/timeout
51
+ forceInvalid: boolean | null; // null = check failed/timeout
52
+ requires2FA: boolean;
53
+ twoFactorComplete: boolean;
54
+ }
55
+
56
+ export type AuthAction =
57
+ | { type: 'allow' }
58
+ | { type: 'redirect'; location: string; reason: string; clearCookies?: boolean }
59
+ | { type: 'service_error'; reason: string }
60
+ | { type: 'refresh_needed' };
61
+
62
+ // =============================================================================
63
+ // CIRCUIT BREAKER INTERFACE
64
+ // =============================================================================
65
+
66
+ /**
67
+ * Circuit breaker interface for middleware integration.
68
+ * Implement this to provide custom circuit breaker behavior.
69
+ */
70
+ export interface CircuitBreakerProvider {
71
+ /** Check if circuit breaker is currently open */
72
+ isOpen(): boolean;
73
+ /** Check if a refresh attempt is allowed (for HALF_OPEN state) */
74
+ canAttemptRefresh(): boolean;
75
+ /** Record a successful operation (closes the breaker) */
76
+ recordSuccess(): void;
77
+ /** Record a failed operation (may open the breaker) */
78
+ recordFailure(error?: Error): void;
79
+ /** Check if an error is a network error (vs HTTP error) */
80
+ isNetworkError(error: any): boolean;
81
+ }
82
+
83
+ /**
84
+ * Logger interface for middleware.
85
+ * Implement this to provide custom logging.
86
+ */
87
+ export interface MiddlewareLogger {
88
+ info(message: string, data?: Record<string, any>): void;
89
+ warn(message: string, data?: Record<string, any>): void;
90
+ error(message: string, data?: Record<string, any>): void;
91
+ }
92
+
93
+ /**
94
+ * Configuration options for createMvpMiddleware
95
+ */
96
+ export interface MvpMiddlewareOptions {
97
+ /** Custom circuit breaker implementation */
98
+ circuitBreaker?: CircuitBreakerProvider;
99
+ /** Custom logger implementation */
100
+ logger?: MiddlewareLogger;
101
+ /** Custom viability check endpoint (default: /api/session/viability) */
102
+ viabilityEndpoint?: string;
103
+ /** Custom refresh endpoint (default: /api/auth/refresh) */
104
+ refreshEndpoint?: string;
105
+ /** Hook called on successful refresh */
106
+ onRefreshSuccess?: () => void;
107
+ /** Hook called on refresh failure */
108
+ onRefreshFailure?: (status: number, isNetworkError: boolean) => void;
109
+ /** Additional paths to bypass middleware (beyond /api/auth/ and /api/session/) */
110
+ bypassPaths?: string[];
111
+ }
112
+
113
+ // =============================================================================
114
+ // DEFAULT IMPLEMENTATIONS
115
+ // =============================================================================
116
+
117
+ /** Default no-op circuit breaker (always closed) */
118
+ const defaultCircuitBreaker: CircuitBreakerProvider = {
119
+ isOpen: () => false,
120
+ canAttemptRefresh: () => true,
121
+ recordSuccess: () => {},
122
+ recordFailure: () => {},
123
+ isNetworkError: (error: any) => {
124
+ return error?.cause?.code === 'ECONNREFUSED' ||
125
+ error?.code === 'ECONNREFUSED' ||
126
+ error?.message?.includes('ECONNREFUSED') ||
127
+ error?.message?.includes('fetch failed') ||
128
+ error?.message?.includes('ENOTFOUND') ||
129
+ error?.message?.includes('ETIMEDOUT') ||
130
+ error?.message?.includes('ECONNRESET');
131
+ }
132
+ };
133
+
134
+ /** Default console logger */
135
+ const defaultLogger: MiddlewareLogger = {
136
+ info: (msg, data) => console.log(`[MIDDLEWARE] ${msg}`, data || ''),
137
+ warn: (msg, data) => console.warn(`[MIDDLEWARE] ${msg}`, data || ''),
138
+ error: (msg, data) => console.error(`[MIDDLEWARE] ${msg}`, data || ''),
139
+ };
140
+
141
+ // =============================================================================
142
+ // HELPERS
143
+ // =============================================================================
144
+
145
+ const LOGIN_PAGE = '/account-auth/login';
146
+ const VERIFY_CODE_PAGE = '/account-auth/verify-code';
147
+
148
+ function isLoginPage(pathname: string): boolean {
149
+ return pathname === LOGIN_PAGE;
150
+ }
151
+
152
+ /** Ensures callback URLs never point to auth pages (prevents redirect loops) */
153
+ function getSafeCallbackUrl(pathname: string, searchParams?: URLSearchParams): string {
154
+ if (pathname.startsWith('/account-auth/')) {
155
+ return '/';
156
+ }
157
+ const existing = searchParams?.get('callbackUrl');
158
+ if (existing && !existing.startsWith('/account-auth/')) {
159
+ return existing;
160
+ }
161
+ return pathname;
162
+ }
163
+
164
+ /** Clear all session cookies on a response */
165
+ function clearSessionCookies(response: NextResponse, request: NextRequest): void {
166
+ const sessionCookie = getSessionCookieName();
167
+ const secureCookie = getSecureSessionCookieName();
168
+
169
+ // Clear main cookies
170
+ response.cookies.set(sessionCookie, '', { path: '/', expires: new Date(0), httpOnly: true, sameSite: 'lax' });
171
+ response.cookies.set(secureCookie, '', { path: '/', expires: new Date(0), httpOnly: true, sameSite: 'lax', secure: true });
172
+
173
+ // Clear any chunked cookies (NextAuth chunks large cookies)
174
+ for (const [name] of request.cookies) {
175
+ if (name.startsWith(`${sessionCookie}.`) || name.startsWith(`${secureCookie}.`)) {
176
+ const isSecure = name.startsWith('__Secure-');
177
+ response.cookies.set(name, '', {
178
+ path: '/',
179
+ expires: new Date(0),
180
+ httpOnly: true,
181
+ sameSite: 'lax',
182
+ ...(isSecure && { secure: true })
183
+ });
184
+ }
185
+ }
186
+ }
187
+
188
+ /** Create redirect response, optionally clearing cookies */
189
+ function redirectTo(
190
+ request: NextRequest,
191
+ location: string,
192
+ clearCookies = false
193
+ ): NextResponse {
194
+ const response = NextResponse.redirect(new URL(location, request.url));
195
+ if (clearCookies) {
196
+ clearSessionCookies(response, request);
197
+ }
198
+ return response;
199
+ }
200
+
201
+ // =============================================================================
202
+ // AUTH DECISION ENGINE
203
+ // =============================================================================
204
+
205
+ /**
206
+ * Pure function that determines what action to take based on auth state.
207
+ * No side effects - just examines context and returns a decision.
208
+ */
209
+ function makeAuthDecision(ctx: AuthContext): AuthAction {
210
+ const { pathname, isPublicRoute, isLoginPage: onLoginPage, searchParams, sessionPointer, sessionStatus, circuitBreakerOpen } = ctx;
211
+ const safeCallback = getSafeCallbackUrl(pathname);
212
+
213
+ // --- Public routes: Allow, but enforce 2FA for authenticated users ---
214
+ if (isPublicRoute && !onLoginPage) {
215
+ if (needsTwoFactorRedirect(pathname, sessionPointer, sessionStatus)) {
216
+ return redirect2FA(safeCallback);
217
+ }
218
+ return { type: 'allow' };
219
+ }
220
+
221
+ // --- Circuit breaker open: Service degraded ---
222
+ if (circuitBreakerOpen) {
223
+ if (onLoginPage) return { type: 'allow' };
224
+ return {
225
+ type: 'redirect',
226
+ location: `${LOGIN_PAGE}?error=ServiceUnavailable`,
227
+ reason: 'circuit_breaker_open',
228
+ clearCookies: true
229
+ };
230
+ }
231
+
232
+ // --- No session cookie: Need to login ---
233
+ if (!sessionPointer.exists) {
234
+ if (onLoginPage) return { type: 'allow' };
235
+ if (pathname === '/') {
236
+ const unauthUrl = process.env.UNAUTHENTICATED_REDIRECT_URL || '/account-auth/login';
237
+ return { type: 'redirect', location: unauthUrl, reason: 'unauthenticated_root' };
238
+ }
239
+ if (pathname.startsWith('/api/')) {
240
+ return { type: 'allow' }; // Let API return proper 401 JSON
241
+ }
242
+ return {
243
+ type: 'redirect',
244
+ location: `${LOGIN_PAGE}?callbackUrl=${encodeURIComponent(safeCallback)}`,
245
+ reason: 'no_session'
246
+ };
247
+ }
248
+
249
+ // --- Service error: Can't verify session ---
250
+ if (sessionStatus.exists === null || sessionStatus.forceInvalid === null) {
251
+ return { type: 'service_error', reason: 'viability_check_failed' };
252
+ }
253
+
254
+ // --- Session force-invalidated (admin action, password change, etc.) ---
255
+ if (sessionStatus.forceInvalid) {
256
+ if (onLoginPage) return { type: 'allow' };
257
+ if (pathname.startsWith('/api/')) return { type: 'allow' };
258
+ return {
259
+ type: 'redirect',
260
+ location: `${LOGIN_PAGE}?callbackUrl=${encodeURIComponent(safeCallback)}&reason=invalidated`,
261
+ reason: 'force_invalidated',
262
+ clearCookies: true
263
+ };
264
+ }
265
+
266
+ // --- Stale session (cookie exists but Redis entry gone) ---
267
+ if (!sessionStatus.exists) {
268
+ if (onLoginPage) return { type: 'allow' };
269
+ if (pathname.startsWith('/api/')) return { type: 'allow' };
270
+ return {
271
+ type: 'redirect',
272
+ location: `${LOGIN_PAGE}?callbackUrl=${encodeURIComponent(safeCallback)}&reason=stale`,
273
+ reason: 'stale_session',
274
+ clearCookies: true
275
+ };
276
+ }
277
+
278
+ // --- 2FA required but not completed ---
279
+ if (needsTwoFactorRedirect(pathname, sessionPointer, sessionStatus)) {
280
+ if (onLoginPage) {
281
+ const cb = getSafeCallbackUrl(pathname, searchParams);
282
+ return { type: 'redirect', location: `${VERIFY_CODE_PAGE}?callbackUrl=${encodeURIComponent(cb)}`, reason: '2fa_from_login' };
283
+ }
284
+ return redirect2FA(safeCallback);
285
+ }
286
+
287
+ // --- Token expired: Try refresh ---
288
+ if (sessionPointer.expired) {
289
+ if (onLoginPage) return { type: 'allow' };
290
+ if (sessionPointer.hasRefreshToken) {
291
+ return { type: 'refresh_needed' };
292
+ }
293
+ return {
294
+ type: 'redirect',
295
+ location: `${LOGIN_PAGE}?callbackUrl=${encodeURIComponent(safeCallback)}&error=SessionExpired`,
296
+ reason: 'token_expired_no_refresh'
297
+ };
298
+ }
299
+
300
+ // --- Authenticated user on login page: Send to their destination ---
301
+ if (onLoginPage) {
302
+ const dest = getSafeCallbackUrl(pathname, searchParams);
303
+ return { type: 'redirect', location: dest, reason: 'already_authenticated' };
304
+ }
305
+
306
+ // --- All checks passed ---
307
+ return { type: 'allow' };
308
+ }
309
+
310
+ /** Check if user needs 2FA redirect */
311
+ function needsTwoFactorRedirect(pathname: string, session: SessionPointer, status: SessionStatus): boolean {
312
+ if (!session.exists || !status.exists) return false;
313
+ if (!status.requires2FA || status.twoFactorComplete) return false;
314
+ if (pathname === VERIFY_CODE_PAGE) return false;
315
+ if (should2FABypass(pathname)) return false;
316
+ if (pathname.startsWith('/api/')) return false; // APIs return JSON errors
317
+ return true;
318
+ }
319
+
320
+ function redirect2FA(callbackUrl: string): AuthAction {
321
+ return {
322
+ type: 'redirect',
323
+ location: `${VERIFY_CODE_PAGE}?callbackUrl=${encodeURIComponent(callbackUrl)}`,
324
+ reason: '2fa_required'
325
+ };
326
+ }
327
+
328
+ // =============================================================================
329
+ // MIDDLEWARE FACTORY
330
+ // =============================================================================
331
+
332
+ export function createMvpMiddleware(options: MvpMiddlewareOptions = {}) {
333
+ // Resolve options with defaults
334
+ const cb = options.circuitBreaker || defaultCircuitBreaker;
335
+ const log = options.logger || defaultLogger;
336
+ const viabilityEndpoint = options.viabilityEndpoint || '/api/session/viability';
337
+ const refreshEndpoint = options.refreshEndpoint || '/api/auth/refresh';
338
+ const bypassPaths = options.bypassPaths || [];
339
+
340
+ return async function middleware(request: NextRequest): Promise<NextResponse> {
341
+ const { pathname, searchParams } = request.nextUrl;
342
+
343
+ // =========================================================================
344
+ // CRITICAL: Auth routes MUST bypass middleware
345
+ // =========================================================================
346
+ // These routes are called internally by middleware itself (viability check,
347
+ // token refresh). Without this bypass, we get infinite loops or 403s.
348
+ // This has caused production outages. DO NOT REMOVE.
349
+ // =========================================================================
350
+ if (pathname.startsWith('/api/auth/') || pathname.startsWith('/api/session/')) {
351
+ return NextResponse.next();
352
+ }
353
+
354
+ // Check custom bypass paths
355
+ for (const bypassPath of bypassPaths) {
356
+ if (pathname.startsWith(bypassPath)) {
357
+ return NextResponse.next();
358
+ }
359
+ }
360
+
361
+ const isPublic = isUnauthenticatedRoute(pathname);
362
+ const isLogin = isLoginPage(pathname);
363
+
364
+ // --- Optimization: Skip viability check for unauthenticated public browsing ---
365
+ if (isPublic && !isLogin) {
366
+ const hasCookie = request.cookies.get(getSessionCookieName())?.value ||
367
+ request.cookies.get(getSecureSessionCookieName())?.value;
368
+ if (!hasCookie) {
369
+ return NextResponse.next();
370
+ }
371
+ // Has cookie - continue to viability check for 2FA enforcement
372
+ }
373
+
374
+ // --- Fetch session viability from internal API ---
375
+ const { sessionStatus, sessionPointer } = await checkViability(request, viabilityEndpoint, log);
376
+
377
+ // --- Handle stale session early (cookie exists but session doesn't) ---
378
+ if (!isPublic && sessionStatus.exists === false && sessionPointer.sessionToken) {
379
+ const safeCallback = getSafeCallbackUrl(pathname);
380
+ log.warn('Stale session detected', {
381
+ sessionToken: sessionPointer.sessionToken?.substring(0, 8) + '...',
382
+ pathname
383
+ });
384
+ return redirectTo(request, `${LOGIN_PAGE}?callbackUrl=${encodeURIComponent(safeCallback)}`, true);
385
+ }
386
+
387
+ // --- Run decision engine ---
388
+ const decision = makeAuthDecision({
389
+ pathname,
390
+ isPublicRoute: isPublic,
391
+ isLoginPage: isLogin,
392
+ searchParams,
393
+ sessionPointer,
394
+ sessionStatus,
395
+ circuitBreakerOpen: cb.isOpen(),
396
+ });
397
+
398
+ // --- Execute decision ---
399
+ return executeDecision(request, decision, pathname, sessionPointer, sessionStatus, {
400
+ circuitBreaker: cb,
401
+ logger: log,
402
+ refreshEndpoint,
403
+ onRefreshSuccess: options.onRefreshSuccess,
404
+ onRefreshFailure: options.onRefreshFailure,
405
+ });
406
+ };
407
+ }
408
+
409
+ /** Check session viability via internal API */
410
+ async function checkViability(
411
+ request: NextRequest,
412
+ endpoint: string,
413
+ log: MiddlewareLogger
414
+ ): Promise<{
415
+ sessionStatus: SessionStatus;
416
+ sessionPointer: SessionPointer;
417
+ }> {
418
+ const sessionStatus: SessionStatus = {
419
+ exists: false,
420
+ forceInvalid: false,
421
+ requires2FA: false,
422
+ twoFactorComplete: false,
423
+ };
424
+ const sessionPointer: SessionPointer = {
425
+ exists: false,
426
+ sessionToken: undefined,
427
+ expired: false,
428
+ hasRefreshToken: false,
429
+ roles: [],
430
+ clientId: '',
431
+ };
432
+
433
+ try {
434
+ const baseUrl = getInternalApiUrl(request);
435
+ const response = await fetch(new URL(endpoint, baseUrl), {
436
+ method: 'GET',
437
+ headers: {
438
+ 'Accept': 'application/json',
439
+ 'Cache-Control': 'no-store',
440
+ 'Cookie': request.headers.get('cookie') || ''
441
+ },
442
+ credentials: 'include'
443
+ });
444
+
445
+ if (response.ok) {
446
+ const data = await response.json();
447
+ // Support both response formats (viability and refresh-viability)
448
+ sessionStatus.exists = data.authenticated ?? (data.sessionToken && data.reason !== 'session_not_found');
449
+ sessionStatus.requires2FA = data.requires2FA || false;
450
+ sessionStatus.twoFactorComplete = data.twoFactorComplete || false;
451
+ sessionPointer.exists = sessionStatus.exists ?? false;
452
+ sessionPointer.sessionToken = data.sessionToken;
453
+ sessionPointer.expired = data.accessTokenExpired || false;
454
+ sessionPointer.hasRefreshToken = data.hasRefreshToken ?? data.canRefresh ?? false;
455
+ sessionPointer.roles = data.roles || [];
456
+ sessionPointer.clientId = data.clientId || '';
457
+ } else {
458
+ log.error('Viability check failed', { status: response.status });
459
+ sessionStatus.exists = null;
460
+ sessionStatus.forceInvalid = null;
461
+ }
462
+ } catch (error) {
463
+ log.error('Viability check error', { error: error instanceof Error ? error.message : String(error) });
464
+ sessionStatus.exists = null;
465
+ sessionStatus.forceInvalid = null;
466
+ }
467
+
468
+ return { sessionStatus, sessionPointer };
469
+ }
470
+
471
+ /** Options passed to executeDecision */
472
+ interface ExecuteOptions {
473
+ circuitBreaker: CircuitBreakerProvider;
474
+ logger: MiddlewareLogger;
475
+ refreshEndpoint: string;
476
+ onRefreshSuccess?: () => void;
477
+ onRefreshFailure?: (status: number, isNetworkError: boolean) => void;
478
+ }
479
+
480
+ /** Execute the auth decision */
481
+ async function executeDecision(
482
+ request: NextRequest,
483
+ decision: AuthAction,
484
+ pathname: string,
485
+ sessionPointer: SessionPointer,
486
+ sessionStatus: SessionStatus,
487
+ opts: ExecuteOptions
488
+ ): Promise<NextResponse> {
489
+ const safeCallback = getSafeCallbackUrl(pathname);
490
+
491
+ switch (decision.type) {
492
+ case 'allow':
493
+ return handleAllow(request, pathname, sessionPointer, sessionStatus);
494
+
495
+ case 'redirect':
496
+ return redirectTo(request, decision.location, decision.clearCookies);
497
+
498
+ case 'service_error':
499
+ return NextResponse.redirect(new URL('/service-unavailable', request.url));
500
+
501
+ case 'refresh_needed':
502
+ return handleRefresh(request, safeCallback, opts);
503
+ }
504
+ }
505
+
506
+ /** Handle 'allow' decision - run RBAC if enabled */
507
+ async function handleAllow(
508
+ request: NextRequest,
509
+ pathname: string,
510
+ sessionPointer: SessionPointer,
511
+ sessionStatus: SessionStatus
512
+ ): Promise<NextResponse> {
513
+ const isPublic = isUnauthenticatedRoute(pathname);
514
+
515
+ if (isRBACEnabled() && !isPublic) {
516
+ if (!sessionPointer.clientId) {
517
+ console.error('[MIDDLEWARE] RBAC: No clientId');
518
+ return NextResponse.redirect(new URL('/error?code=no_client_id', request.url));
519
+ }
520
+
521
+ try {
522
+ const result = await checkPagePermission(pathname, sessionPointer.roles, sessionPointer.clientId);
523
+
524
+ if (!result.allowed) {
525
+ console.log('[MIDDLEWARE] RBAC denied:', { pathname, reason: result.reason });
526
+ return NextResponse.redirect(new URL(result.redirect || '/unauthorized', request.url));
527
+ }
528
+
529
+ if (result.requires_2fa && !sessionStatus.twoFactorComplete) {
530
+ return NextResponse.redirect(
531
+ new URL(`${VERIFY_CODE_PAGE}?callbackUrl=${encodeURIComponent(pathname)}`, request.url)
532
+ );
533
+ }
534
+ } catch (error) {
535
+ console.error('[MIDDLEWARE] RBAC error:', error);
536
+ return NextResponse.redirect(new URL('/error?code=rbac_error', request.url));
537
+ }
538
+ }
539
+
540
+ return NextResponse.next();
541
+ }
542
+
543
+ /** Handle token refresh */
544
+ async function handleRefresh(
545
+ request: NextRequest,
546
+ safeCallback: string,
547
+ opts: ExecuteOptions
548
+ ): Promise<NextResponse> {
549
+ const { circuitBreaker: cb, logger: log, refreshEndpoint, onRefreshSuccess, onRefreshFailure } = opts;
550
+
551
+ // Check if circuit breaker allows refresh attempt
552
+ if (!cb.canAttemptRefresh()) {
553
+ log.warn('Circuit breaker preventing refresh attempt');
554
+ return redirectTo(
555
+ request,
556
+ `${LOGIN_PAGE}?callbackUrl=${encodeURIComponent(safeCallback)}&error=ServiceUnavailable`,
557
+ true
558
+ );
559
+ }
560
+
561
+ try {
562
+ const baseUrl = getInternalApiUrl(request);
563
+ const response = await fetch(new URL(refreshEndpoint, baseUrl), {
564
+ method: 'POST',
565
+ headers: {
566
+ 'Accept': 'application/json',
567
+ 'Content-Type': 'application/json',
568
+ 'Cookie': request.headers.get('cookie') || '',
569
+ 'x-session-token': request.cookies.get(getSessionCookieName())?.value ||
570
+ request.cookies.get(getSecureSessionCookieName())?.value || ''
571
+ },
572
+ credentials: 'include'
573
+ });
574
+
575
+ if (response.ok) {
576
+ const data = await response.json();
577
+ if (data.refreshed || data.reason === 'already_fresh') {
578
+ cb.recordSuccess();
579
+ onRefreshSuccess?.();
580
+ log.info('Token refresh successful');
581
+ return NextResponse.next();
582
+ }
583
+ }
584
+
585
+ // Refresh failed - check for permanent failures (401/403) vs transient (5xx)
586
+ // CRITICAL: Always clear cookies on 401/403 to prevent redirect loops
587
+ // CRITICAL: Only record circuit breaker failures for network errors, not HTTP errors
588
+ // HTTP 500 means the server responded - it's reachable. Only network failures should trip the breaker.
589
+ const isPermanentFailure = response.status === 401 || response.status === 403;
590
+ const errorParam = isPermanentFailure ? 'SessionExpired' : 'RefreshError';
591
+
592
+ log.warn('Refresh failed', {
593
+ status: response.status,
594
+ isPermanentFailure,
595
+ clearingCookies: isPermanentFailure
596
+ });
597
+
598
+ onRefreshFailure?.(response.status, false);
599
+
600
+ return redirectTo(
601
+ request,
602
+ `${LOGIN_PAGE}?callbackUrl=${encodeURIComponent(safeCallback)}&error=${errorParam}`,
603
+ isPermanentFailure
604
+ );
605
+ } catch (error: any) {
606
+ const isNetwork = cb.isNetworkError(error);
607
+ log.error('Refresh error', {
608
+ error: error instanceof Error ? error.message : String(error),
609
+ isNetworkError: isNetwork
610
+ });
611
+
612
+ // Only record circuit breaker failure for actual network errors
613
+ if (isNetwork) {
614
+ cb.recordFailure(error);
615
+ }
616
+
617
+ onRefreshFailure?.(0, isNetwork);
618
+
619
+ // Network error - don't clear cookies, might be transient
620
+ return redirectTo(
621
+ request,
622
+ `${LOGIN_PAGE}?callbackUrl=${encodeURIComponent(safeCallback)}&error=RefreshError`,
623
+ false
624
+ );
625
+ }
626
+ }