@adonis-agora/authkit-server 0.34.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 (389) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +158 -0
  3. package/build/assets/grafana/authkit-dashboard.json +118 -0
  4. package/build/commands/clients_create.d.ts +27 -0
  5. package/build/commands/clients_create.js +121 -0
  6. package/build/commands/commands.json +292 -0
  7. package/build/commands/configure.d.ts +2 -0
  8. package/build/commands/configure.js +45 -0
  9. package/build/commands/doctor.d.ts +10 -0
  10. package/build/commands/doctor.js +113 -0
  11. package/build/commands/eject.d.ts +11 -0
  12. package/build/commands/eject.js +96 -0
  13. package/build/commands/expire_scan.d.ts +28 -0
  14. package/build/commands/expire_scan.js +92 -0
  15. package/build/commands/import_users.d.ts +17 -0
  16. package/build/commands/import_users.js +80 -0
  17. package/build/commands/keys_rotate.d.ts +21 -0
  18. package/build/commands/keys_rotate.js +126 -0
  19. package/build/commands/main.d.ts +12 -0
  20. package/build/commands/main.js +38 -0
  21. package/build/commands/settings_get.d.ts +18 -0
  22. package/build/commands/settings_get.js +43 -0
  23. package/build/commands/settings_list.d.ts +17 -0
  24. package/build/commands/settings_list.js +39 -0
  25. package/build/commands/settings_set.d.ts +20 -0
  26. package/build/commands/settings_set.js +55 -0
  27. package/build/commands/settings_unset.d.ts +18 -0
  28. package/build/commands/settings_unset.js +46 -0
  29. package/build/commands/ui_preset.d.ts +4 -0
  30. package/build/commands/ui_preset.js +32 -0
  31. package/build/database/migrations/make_authkit_oidc_table.d.ts +6 -0
  32. package/build/database/migrations/make_authkit_oidc_table.js +19 -0
  33. package/build/host/ui/admin.html +29 -0
  34. package/build/host/views/account/apps.edge +59 -0
  35. package/build/host/views/account/confirm.edge +70 -0
  36. package/build/host/views/account/email-confirmed.edge +16 -0
  37. package/build/host/views/account/login.edge +33 -0
  38. package/build/host/views/account/mfa.edge +152 -0
  39. package/build/host/views/account/orgs.edge +58 -0
  40. package/build/host/views/account/security.edge +215 -0
  41. package/build/host/views/account/tokens.edge +75 -0
  42. package/build/host/views/consent.edge +20 -0
  43. package/build/host/views/forgot.edge +43 -0
  44. package/build/host/views/login.edge +251 -0
  45. package/build/host/views/maintenance.edge +32 -0
  46. package/build/host/views/mfa-challenge.edge +107 -0
  47. package/build/host/views/otp-unlock.edge +40 -0
  48. package/build/host/views/partials/styles.edge +2 -0
  49. package/build/host/views/reset.edge +30 -0
  50. package/build/host/views/signup.edge +62 -0
  51. package/build/host/views/verify-email.edge +17 -0
  52. package/build/index.d.ts +115 -0
  53. package/build/index.js +76 -0
  54. package/build/password/common_passwords.txt +10000 -0
  55. package/build/providers/authkit_server_provider.d.ts +27 -0
  56. package/build/providers/authkit_server_provider.js +242 -0
  57. package/build/src/accounts/account_store.d.ts +578 -0
  58. package/build/src/accounts/account_store.js +60 -0
  59. package/build/src/accounts/lucid_account_store.d.ts +156 -0
  60. package/build/src/accounts/lucid_account_store.js +294 -0
  61. package/build/src/accounts/lucid_store/core.d.ts +9 -0
  62. package/build/src/accounts/lucid_store/core.js +306 -0
  63. package/build/src/accounts/lucid_store/mfa.d.ts +15 -0
  64. package/build/src/accounts/lucid_store/mfa.js +108 -0
  65. package/build/src/accounts/lucid_store/organizations.d.ts +13 -0
  66. package/build/src/accounts/lucid_store/organizations.js +294 -0
  67. package/build/src/accounts/lucid_store/password_hygiene.d.ts +20 -0
  68. package/build/src/accounts/lucid_store/password_hygiene.js +130 -0
  69. package/build/src/accounts/lucid_store/provider_identity.d.ts +8 -0
  70. package/build/src/accounts/lucid_store/provider_identity.js +59 -0
  71. package/build/src/accounts/lucid_store/shared.d.ts +113 -0
  72. package/build/src/accounts/lucid_store/shared.js +130 -0
  73. package/build/src/accounts/lucid_store/status_profile.d.ts +34 -0
  74. package/build/src/accounts/lucid_store/status_profile.js +100 -0
  75. package/build/src/accounts/lucid_store/webauthn.d.ts +8 -0
  76. package/build/src/accounts/lucid_store/webauthn.js +140 -0
  77. package/build/src/accounts/lucid_stores.d.ts +45 -0
  78. package/build/src/accounts/lucid_stores.js +30 -0
  79. package/build/src/adapters/adapter_contract.d.ts +37 -0
  80. package/build/src/adapters/adapter_contract.js +1 -0
  81. package/build/src/adapters/database_adapter.d.ts +21 -0
  82. package/build/src/adapters/database_adapter.js +79 -0
  83. package/build/src/adapters/factory.d.ts +30 -0
  84. package/build/src/adapters/factory.js +43 -0
  85. package/build/src/adapters/redis_adapter.d.ts +24 -0
  86. package/build/src/adapters/redis_adapter.js +122 -0
  87. package/build/src/audit/audit_sink.d.ts +63 -0
  88. package/build/src/audit/audit_sink.js +1 -0
  89. package/build/src/audit/lucid_audit_sink.d.ts +10 -0
  90. package/build/src/audit/lucid_audit_sink.js +99 -0
  91. package/build/src/commands/expire_scan_command.d.ts +60 -0
  92. package/build/src/commands/expire_scan_command.js +229 -0
  93. package/build/src/commands/import_clients.d.ts +40 -0
  94. package/build/src/commands/import_clients.js +79 -0
  95. package/build/src/commands/import_users.d.ts +51 -0
  96. package/build/src/commands/import_users.js +94 -0
  97. package/build/src/commands/resolve_config.d.ts +13 -0
  98. package/build/src/commands/resolve_config.js +18 -0
  99. package/build/src/commands/settings_commands.d.ts +47 -0
  100. package/build/src/commands/settings_commands.js +270 -0
  101. package/build/src/controllers/oidc_callback_controller.d.ts +22 -0
  102. package/build/src/controllers/oidc_callback_controller.js +33 -0
  103. package/build/src/define_config.d.ts +772 -0
  104. package/build/src/define_config.js +351 -0
  105. package/build/src/doctor/checks.d.ts +197 -0
  106. package/build/src/doctor/checks.js +858 -0
  107. package/build/src/events/dispatcher.d.ts +45 -0
  108. package/build/src/events/dispatcher.js +117 -0
  109. package/build/src/host/account_api/account_api_controller.d.ts +245 -0
  110. package/build/src/host/account_api/account_api_controller.js +827 -0
  111. package/build/src/host/account_deletion_ops.d.ts +80 -0
  112. package/build/src/host/account_deletion_ops.js +118 -0
  113. package/build/src/host/account_deletion_service.d.ts +63 -0
  114. package/build/src/host/account_deletion_service.js +133 -0
  115. package/build/src/host/account_export_service.d.ts +76 -0
  116. package/build/src/host/account_export_service.js +140 -0
  117. package/build/src/host/account_home.d.ts +11 -0
  118. package/build/src/host/account_home.js +11 -0
  119. package/build/src/host/account_lockout.d.ts +86 -0
  120. package/build/src/host/account_lockout.js +185 -0
  121. package/build/src/host/active_org_cookie.d.ts +28 -0
  122. package/build/src/host/active_org_cookie.js +47 -0
  123. package/build/src/host/admin_api/admin_api_guard.d.ts +16 -0
  124. package/build/src/host/admin_api/admin_api_guard.js +59 -0
  125. package/build/src/host/admin_api/admin_orgs_service.d.ts +125 -0
  126. package/build/src/host/admin_api/admin_orgs_service.js +324 -0
  127. package/build/src/host/admin_api/admin_users_service.d.ts +163 -0
  128. package/build/src/host/admin_api/admin_users_service.js +308 -0
  129. package/build/src/host/admin_api/api_clients_controller.d.ts +54 -0
  130. package/build/src/host/admin_api/api_clients_controller.js +89 -0
  131. package/build/src/host/admin_api/api_keys_controller.d.ts +19 -0
  132. package/build/src/host/admin_api/api_keys_controller.js +36 -0
  133. package/build/src/host/admin_api/api_misc_controller.d.ts +19 -0
  134. package/build/src/host/admin_api/api_misc_controller.js +44 -0
  135. package/build/src/host/admin_api/api_orgs_controller.d.ts +108 -0
  136. package/build/src/host/admin_api/api_orgs_controller.js +189 -0
  137. package/build/src/host/admin_api/api_settings_controller.d.ts +55 -0
  138. package/build/src/host/admin_api/api_settings_controller.js +134 -0
  139. package/build/src/host/admin_api/api_users_controller.d.ts +108 -0
  140. package/build/src/host/admin_api/api_users_controller.js +187 -0
  141. package/build/src/host/admin_api/dto.d.ts +131 -0
  142. package/build/src/host/admin_api/dto.js +122 -0
  143. package/build/src/host/admin_api/token_verify_service.d.ts +34 -0
  144. package/build/src/host/admin_api/token_verify_service.js +74 -0
  145. package/build/src/host/admin_clients_service.d.ts +88 -0
  146. package/build/src/host/admin_clients_service.js +181 -0
  147. package/build/src/host/admin_console/admin_shell_controller.d.ts +27 -0
  148. package/build/src/host/admin_console/admin_shell_controller.js +167 -0
  149. package/build/src/host/admin_console/console_audit_controller.d.ts +17 -0
  150. package/build/src/host/admin_console/console_audit_controller.js +25 -0
  151. package/build/src/host/admin_console/console_clients_controller.d.ts +53 -0
  152. package/build/src/host/admin_console/console_clients_controller.js +102 -0
  153. package/build/src/host/admin_console/console_impersonation_controller.d.ts +23 -0
  154. package/build/src/host/admin_console/console_impersonation_controller.js +45 -0
  155. package/build/src/host/admin_console/console_keys_controller.d.ts +24 -0
  156. package/build/src/host/admin_console/console_keys_controller.js +41 -0
  157. package/build/src/host/admin_console/console_orgs_controller.d.ts +96 -0
  158. package/build/src/host/admin_console/console_orgs_controller.js +238 -0
  159. package/build/src/host/admin_console/console_overview_controller.d.ts +27 -0
  160. package/build/src/host/admin_console/console_overview_controller.js +44 -0
  161. package/build/src/host/admin_console/console_roles_controller.d.ts +31 -0
  162. package/build/src/host/admin_console/console_roles_controller.js +108 -0
  163. package/build/src/host/admin_console/console_sessions_controller.d.ts +91 -0
  164. package/build/src/host/admin_console/console_sessions_controller.js +125 -0
  165. package/build/src/host/admin_console/console_settings_controller.d.ts +47 -0
  166. package/build/src/host/admin_console/console_settings_controller.js +115 -0
  167. package/build/src/host/admin_console/console_users_controller.d.ts +110 -0
  168. package/build/src/host/admin_console/console_users_controller.js +212 -0
  169. package/build/src/host/admin_prefix.d.ts +50 -0
  170. package/build/src/host/admin_prefix.js +77 -0
  171. package/build/src/host/admin_sessions_service.d.ts +119 -0
  172. package/build/src/host/admin_sessions_service.js +284 -0
  173. package/build/src/host/admin_stats_service.d.ts +44 -0
  174. package/build/src/host/admin_stats_service.js +130 -0
  175. package/build/src/host/admin_validators.d.ts +247 -0
  176. package/build/src/host/admin_validators.js +197 -0
  177. package/build/src/host/augmentations.d.ts +12 -0
  178. package/build/src/host/augmentations.js +1 -0
  179. package/build/src/host/auth_host_config.d.ts +22 -0
  180. package/build/src/host/auth_host_config.js +13 -0
  181. package/build/src/host/avatar_storage.d.ts +68 -0
  182. package/build/src/host/avatar_storage.js +166 -0
  183. package/build/src/host/bot_protection.d.ts +187 -0
  184. package/build/src/host/bot_protection.js +132 -0
  185. package/build/src/host/branding.d.ts +19 -0
  186. package/build/src/host/branding.js +8 -0
  187. package/build/src/host/config_locks.d.ts +38 -0
  188. package/build/src/host/config_locks.js +70 -0
  189. package/build/src/host/console_session.d.ts +27 -0
  190. package/build/src/host/console_session.js +36 -0
  191. package/build/src/host/controllers/account_apps_controller.d.ts +15 -0
  192. package/build/src/host/controllers/account_apps_controller.js +61 -0
  193. package/build/src/host/controllers/account_confirm_controller.d.ts +24 -0
  194. package/build/src/host/controllers/account_confirm_controller.js +144 -0
  195. package/build/src/host/controllers/account_mfa_controller.d.ts +28 -0
  196. package/build/src/host/controllers/account_mfa_controller.js +208 -0
  197. package/build/src/host/controllers/account_orgs_controller.d.ts +80 -0
  198. package/build/src/host/controllers/account_orgs_controller.js +401 -0
  199. package/build/src/host/controllers/account_security_controller.d.ts +43 -0
  200. package/build/src/host/controllers/account_security_controller.js +546 -0
  201. package/build/src/host/controllers/account_session_controller.d.ts +21 -0
  202. package/build/src/host/controllers/account_session_controller.js +113 -0
  203. package/build/src/host/controllers/account_tokens_controller.d.ts +7 -0
  204. package/build/src/host/controllers/account_tokens_controller.js +67 -0
  205. package/build/src/host/controllers/interaction_controller.d.ts +125 -0
  206. package/build/src/host/controllers/interaction_controller.js +981 -0
  207. package/build/src/host/controllers/pat_introspection_controller.d.ts +22 -0
  208. package/build/src/host/controllers/pat_introspection_controller.js +46 -0
  209. package/build/src/host/controllers/registration_controller.d.ts +18 -0
  210. package/build/src/host/controllers/registration_controller.js +382 -0
  211. package/build/src/host/controllers/social_controller.d.ts +8 -0
  212. package/build/src/host/controllers/social_controller.js +89 -0
  213. package/build/src/host/csrf.d.ts +29 -0
  214. package/build/src/host/csrf.js +14 -0
  215. package/build/src/host/default_mailer.d.ts +112 -0
  216. package/build/src/host/default_mailer.js +417 -0
  217. package/build/src/host/durable/account_deletion_workflow.d.ts +48 -0
  218. package/build/src/host/durable/account_deletion_workflow.js +86 -0
  219. package/build/src/host/durable/account_export_workflow.d.ts +68 -0
  220. package/build/src/host/durable/account_export_workflow.js +93 -0
  221. package/build/src/host/durable/index.d.ts +68 -0
  222. package/build/src/host/durable/index.js +77 -0
  223. package/build/src/host/email_templates.d.ts +39 -0
  224. package/build/src/host/email_templates.js +69 -0
  225. package/build/src/host/geo.d.ts +22 -0
  226. package/build/src/host/geo.js +39 -0
  227. package/build/src/host/i18n.d.ts +1416 -0
  228. package/build/src/host/i18n.js +1583 -0
  229. package/build/src/host/impersonation.d.ts +25 -0
  230. package/build/src/host/impersonation.js +31 -0
  231. package/build/src/host/key_rotation.d.ts +14 -0
  232. package/build/src/host/key_rotation.js +27 -0
  233. package/build/src/host/key_rotation_actions.d.ts +42 -0
  234. package/build/src/host/key_rotation_actions.js +27 -0
  235. package/build/src/host/login_attempt.d.ts +98 -0
  236. package/build/src/host/login_attempt.js +211 -0
  237. package/build/src/host/login_notify.d.ts +27 -0
  238. package/build/src/host/login_notify.js +142 -0
  239. package/build/src/host/middleware/account_auth.d.ts +7 -0
  240. package/build/src/host/middleware/account_auth.js +11 -0
  241. package/build/src/host/otp_lockout.d.ts +103 -0
  242. package/build/src/host/otp_lockout.js +221 -0
  243. package/build/src/host/rate_limit.d.ts +39 -0
  244. package/build/src/host/rate_limit.js +94 -0
  245. package/build/src/host/register_auth_host.d.ts +97 -0
  246. package/build/src/host/register_auth_host.js +466 -0
  247. package/build/src/host/renderers/edge_renderer.d.ts +12 -0
  248. package/build/src/host/renderers/edge_renderer.js +38 -0
  249. package/build/src/host/renderers/inertia_renderer.d.ts +70 -0
  250. package/build/src/host/renderers/inertia_renderer.js +54 -0
  251. package/build/src/host/runtime_settings.d.ts +181 -0
  252. package/build/src/host/runtime_settings.js +307 -0
  253. package/build/src/host/runtime_toggles.d.ts +807 -0
  254. package/build/src/host/runtime_toggles.js +784 -0
  255. package/build/src/host/security_notice_service.d.ts +36 -0
  256. package/build/src/host/security_notice_service.js +87 -0
  257. package/build/src/host/session_context.d.ts +23 -0
  258. package/build/src/host/session_context.js +89 -0
  259. package/build/src/host/sudo_mode.d.ts +56 -0
  260. package/build/src/host/sudo_mode.js +99 -0
  261. package/build/src/host/svg_chart.d.ts +15 -0
  262. package/build/src/host/svg_chart.js +45 -0
  263. package/build/src/host/trusted_device.d.ts +61 -0
  264. package/build/src/host/trusted_device.js +65 -0
  265. package/build/src/host/ui-dist/assets/index-CRWcmsex.js +137 -0
  266. package/build/src/host/ui-dist/assets/index-DTSmD4RU.css +1 -0
  267. package/build/src/host/ui-dist/index.html +17 -0
  268. package/build/src/host/user_agent.d.ts +18 -0
  269. package/build/src/host/user_agent.js +50 -0
  270. package/build/src/host/validators.d.ts +127 -0
  271. package/build/src/host/validators.js +53 -0
  272. package/build/src/keys/jwks_manager.d.ts +6 -0
  273. package/build/src/keys/jwks_manager.js +11 -0
  274. package/build/src/keys/keystore.d.ts +52 -0
  275. package/build/src/keys/keystore.js +68 -0
  276. package/build/src/keys/keystore_codec.d.ts +20 -0
  277. package/build/src/keys/keystore_codec.js +42 -0
  278. package/build/src/keys/keystore_crypto.d.ts +9 -0
  279. package/build/src/keys/keystore_crypto.js +27 -0
  280. package/build/src/keys/keystore_manager.d.ts +43 -0
  281. package/build/src/keys/keystore_manager.js +104 -0
  282. package/build/src/keys/keystore_vault.d.ts +113 -0
  283. package/build/src/keys/keystore_vault.js +265 -0
  284. package/build/src/mixins/json_column.d.ts +33 -0
  285. package/build/src/mixins/json_column.js +38 -0
  286. package/build/src/mixins/with_audit_log.d.ts +19 -0
  287. package/build/src/mixins/with_audit_log.js +39 -0
  288. package/build/src/mixins/with_auth_user.d.ts +24 -0
  289. package/build/src/mixins/with_auth_user.js +46 -0
  290. package/build/src/mixins/with_credentials.d.ts +20 -0
  291. package/build/src/mixins/with_credentials.js +29 -0
  292. package/build/src/mixins/with_mfa.d.ts +35 -0
  293. package/build/src/mixins/with_mfa.js +21 -0
  294. package/build/src/mixins/with_personal_access_token.d.ts +19 -0
  295. package/build/src/mixins/with_personal_access_token.js +42 -0
  296. package/build/src/mixins/with_provider_identity.d.ts +20 -0
  297. package/build/src/mixins/with_provider_identity.js +32 -0
  298. package/build/src/mixins/with_webauthn_credential.d.ts +37 -0
  299. package/build/src/mixins/with_webauthn_credential.js +46 -0
  300. package/build/src/observability/diagnostics_bridge.d.ts +6 -0
  301. package/build/src/observability/diagnostics_bridge.js +27 -0
  302. package/build/src/observability/metrics_controller.d.ts +5 -0
  303. package/build/src/observability/metrics_controller.js +24 -0
  304. package/build/src/observability/metrics_service.d.ts +2 -0
  305. package/build/src/observability/metrics_service.js +7 -0
  306. package/build/src/observability/otel_recorder.d.ts +10 -0
  307. package/build/src/observability/otel_recorder.js +59 -0
  308. package/build/src/observability/telescope/data_providers.d.ts +24 -0
  309. package/build/src/observability/telescope/data_providers.js +235 -0
  310. package/build/src/observability/telescope/extension.d.ts +29 -0
  311. package/build/src/observability/telescope/extension.js +167 -0
  312. package/build/src/observability/telescope/index.d.ts +8 -0
  313. package/build/src/observability/telescope/index.js +8 -0
  314. package/build/src/observability/wire_provider_events.d.ts +12 -0
  315. package/build/src/observability/wire_provider_events.js +30 -0
  316. package/build/src/password/common_passwords.d.ts +31 -0
  317. package/build/src/password/common_passwords.js +73 -0
  318. package/build/src/password/password_manager.d.ts +163 -0
  319. package/build/src/password/password_manager.js +191 -0
  320. package/build/src/password/policy.d.ts +71 -0
  321. package/build/src/password/policy.js +55 -0
  322. package/build/src/password/pwned.d.ts +36 -0
  323. package/build/src/password/pwned.js +52 -0
  324. package/build/src/pat/lucid_pat_store.d.ts +6 -0
  325. package/build/src/pat/lucid_pat_store.js +62 -0
  326. package/build/src/pat/pat_store.d.ts +31 -0
  327. package/build/src/pat/pat_store.js +1 -0
  328. package/build/src/pat/pat_tokens.d.ts +4 -0
  329. package/build/src/pat/pat_tokens.js +9 -0
  330. package/build/src/provider/build_provider.d.ts +45 -0
  331. package/build/src/provider/build_provider.js +219 -0
  332. package/build/src/provider/device_sources.d.ts +6 -0
  333. package/build/src/provider/device_sources.js +65 -0
  334. package/build/src/provider/interaction_actions.d.ts +36 -0
  335. package/build/src/provider/interaction_actions.js +55 -0
  336. package/build/src/provider/key_rotation_scheduler.d.ts +28 -0
  337. package/build/src/provider/key_rotation_scheduler.js +40 -0
  338. package/build/src/provider/keystore_reload.d.ts +21 -0
  339. package/build/src/provider/keystore_reload.js +39 -0
  340. package/build/src/provider/logout_sources.d.ts +13 -0
  341. package/build/src/provider/logout_sources.js +64 -0
  342. package/build/src/provider/oidc_service.d.ts +81 -0
  343. package/build/src/provider/oidc_service.js +254 -0
  344. package/build/src/provider/single_flight_lock.d.ts +18 -0
  345. package/build/src/provider/single_flight_lock.js +31 -0
  346. package/build/src/provider/token_exchange.d.ts +21 -0
  347. package/build/src/provider/token_exchange.js +124 -0
  348. package/build/src/register_routes.d.ts +16 -0
  349. package/build/src/register_routes.js +21 -0
  350. package/build/src/schema/ensure.d.ts +41 -0
  351. package/build/src/schema/ensure.js +262 -0
  352. package/build/stubs/config/authkit.stub +20 -0
  353. package/build/stubs/config/authkit_react.stub +52 -0
  354. package/build/stubs/main.d.ts +1 -0
  355. package/build/stubs/main.js +2 -0
  356. package/build/stubs/models/auth_user.stub +22 -0
  357. package/build/stubs/ui/edge/views/consent.edge +13 -0
  358. package/build/stubs/ui/edge/views/login.edge +19 -0
  359. package/build/stubs/ui/react/components/auth_shell.tsx +67 -0
  360. package/build/stubs/ui/react/pages/account/login.tsx +56 -0
  361. package/build/stubs/ui/react/pages/account/mfa.tsx +132 -0
  362. package/build/stubs/ui/react/pages/account/tokens.tsx +88 -0
  363. package/build/stubs/ui/react/pages/consent.tsx +39 -0
  364. package/build/stubs/ui/react/pages/forgot.tsx +44 -0
  365. package/build/stubs/ui/react/pages/login.tsx +171 -0
  366. package/build/stubs/ui/react/pages/mfa-challenge.tsx +72 -0
  367. package/build/stubs/ui/react/pages/reset.tsx +58 -0
  368. package/build/stubs/ui/react/pages/signup.tsx +78 -0
  369. package/build/stubs/ui/react/pages/verify-email.tsx +24 -0
  370. package/build/types.d.ts +7 -0
  371. package/build/types.js +1 -0
  372. package/package.json +147 -0
  373. package/stubs/config/authkit.stub +20 -0
  374. package/stubs/config/authkit_react.stub +52 -0
  375. package/stubs/main.ts +2 -0
  376. package/stubs/models/auth_user.stub +22 -0
  377. package/stubs/ui/edge/views/consent.edge +13 -0
  378. package/stubs/ui/edge/views/login.edge +19 -0
  379. package/stubs/ui/react/components/auth_shell.tsx +67 -0
  380. package/stubs/ui/react/pages/account/login.tsx +56 -0
  381. package/stubs/ui/react/pages/account/mfa.tsx +132 -0
  382. package/stubs/ui/react/pages/account/tokens.tsx +88 -0
  383. package/stubs/ui/react/pages/consent.tsx +39 -0
  384. package/stubs/ui/react/pages/forgot.tsx +44 -0
  385. package/stubs/ui/react/pages/login.tsx +171 -0
  386. package/stubs/ui/react/pages/mfa-challenge.tsx +72 -0
  387. package/stubs/ui/react/pages/reset.tsx +58 -0
  388. package/stubs/ui/react/pages/signup.tsx +78 -0
  389. package/stubs/ui/react/pages/verify-email.tsx +24 -0
@@ -0,0 +1,43 @@
1
+ import { RedisAdapter } from './redis_adapter.js';
2
+ import { DatabaseAdapter } from './database_adapter.js';
3
+ export const adapters = {
4
+ /**
5
+ * Factory para o adapter Redis. O consumidor precisa ter o @adonisjs/redis
6
+ * configurado, pois o resolver resolve o `RedisManager` pelo token `'redis'`
7
+ * do container e obtém a conexão nomeada via `connection(name)`.
8
+ */
9
+ redis(config) {
10
+ return {
11
+ async resolver(app) {
12
+ const redisManager = await app.container.make('redis');
13
+ const client = redisManager.connection(config.connection);
14
+ const prefix = config.prefix ?? 'authkit';
15
+ return class extends RedisAdapter {
16
+ constructor(name) {
17
+ super(name, client, prefix);
18
+ }
19
+ };
20
+ },
21
+ };
22
+ },
23
+ /**
24
+ * Factory para o adapter de banco (Lucid). Resolve o `Database` manager pelo
25
+ * token `'lucid.db'`. O `DatabaseAdapter` consome o manager diretamente
26
+ * (`db.query()`/`db.table()`); quando uma conexão específica é solicitada,
27
+ * usamos `db.connection(name)` para obter o cliente daquela conexão.
28
+ */
29
+ database(config = {}) {
30
+ return {
31
+ async resolver(app) {
32
+ const db = await app.container.make('lucid.db');
33
+ const connection = config.connection;
34
+ const conn = connection ? db.connection(connection) : db;
35
+ return class extends DatabaseAdapter {
36
+ constructor(name) {
37
+ super(name, conn);
38
+ }
39
+ };
40
+ },
41
+ };
42
+ },
43
+ };
@@ -0,0 +1,24 @@
1
+ import type { Redis } from 'ioredis';
2
+ import type { EnumeratedArtifact, OidcAdapter, OidcPayload } from './adapter_contract.js';
3
+ export declare class RedisAdapter implements OidcAdapter {
4
+ #private;
5
+ private name;
6
+ private redis;
7
+ private prefix;
8
+ constructor(name: string, redis: Redis, prefix: string);
9
+ upsert(id: string, payload: OidcPayload, expiresIn: number): Promise<void>;
10
+ find(id: string): Promise<OidcPayload | undefined>;
11
+ findByUid(uid: string): Promise<OidcPayload | undefined>;
12
+ findByUserCode(userCode: string): Promise<OidcPayload | undefined>;
13
+ consume(id: string): Promise<void>;
14
+ destroy(id: string): Promise<void>;
15
+ revokeByGrantId(grantId: string): Promise<void>;
16
+ /**
17
+ * Enumeração genérica dos artefatos do model deste adapter via SCAN sobre o
18
+ * prefixo de chave do model (`<prefix>:<name>:*`). É limpo porque cada artefato
19
+ * é uma chave única já namespaceada por `prefix` + `name`; SCAN é não-bloqueante
20
+ * (cursor) ao contrário de KEYS. Usado pelo console admin (`Client`, `Session`,
21
+ * `Grant`, tokens).
22
+ */
23
+ list(): Promise<EnumeratedArtifact[]>;
24
+ }
@@ -0,0 +1,122 @@
1
+ const grantable = new Set([
2
+ 'AccessToken',
3
+ 'AuthorizationCode',
4
+ 'RefreshToken',
5
+ 'DeviceCode',
6
+ 'BackchannelAuthenticationRequest',
7
+ ]);
8
+ export class RedisAdapter {
9
+ name;
10
+ redis;
11
+ prefix;
12
+ constructor(name, redis, prefix) {
13
+ this.name = name;
14
+ this.redis = redis;
15
+ this.prefix = prefix;
16
+ }
17
+ #key(id) {
18
+ return `${this.prefix}:${this.name}:${id}`;
19
+ }
20
+ #grantKey(grantId) {
21
+ return `${this.prefix}:grant:${grantId}`;
22
+ }
23
+ #userCodeKey(userCode) {
24
+ return `${this.prefix}:userCode:${userCode}`;
25
+ }
26
+ #uidKey(uid) {
27
+ return `${this.prefix}:uid:${uid}`;
28
+ }
29
+ async upsert(id, payload, expiresIn) {
30
+ const key = this.#key(id);
31
+ const multi = this.redis.multi();
32
+ multi.set(key, JSON.stringify(payload));
33
+ if (grantable.has(this.name) && payload.grantId) {
34
+ const gk = this.#grantKey(payload.grantId);
35
+ multi.rpush(gk, key);
36
+ if (expiresIn)
37
+ multi.expire(gk, expiresIn);
38
+ }
39
+ if (payload.userCode) {
40
+ const uck = this.#userCodeKey(payload.userCode);
41
+ multi.set(uck, id);
42
+ if (expiresIn)
43
+ multi.expire(uck, expiresIn);
44
+ }
45
+ if (this.name === 'Session' && payload.uid) {
46
+ const uk = this.#uidKey(payload.uid);
47
+ multi.set(uk, id);
48
+ if (expiresIn)
49
+ multi.expire(uk, expiresIn);
50
+ }
51
+ if (expiresIn)
52
+ multi.expire(key, expiresIn);
53
+ await multi.exec();
54
+ }
55
+ async find(id) {
56
+ const data = await this.redis.get(this.#key(id));
57
+ if (!data)
58
+ return undefined;
59
+ return JSON.parse(data);
60
+ }
61
+ async findByUid(uid) {
62
+ const id = await this.redis.get(this.#uidKey(uid));
63
+ if (!id)
64
+ return undefined;
65
+ return this.find(id);
66
+ }
67
+ async findByUserCode(userCode) {
68
+ const id = await this.redis.get(this.#userCodeKey(userCode));
69
+ if (!id)
70
+ return undefined;
71
+ return this.find(id);
72
+ }
73
+ async consume(id) {
74
+ const found = await this.find(id);
75
+ if (!found)
76
+ return;
77
+ found.consumed = Math.floor(Date.now() / 1000);
78
+ const ttl = await this.redis.ttl(this.#key(id));
79
+ if (ttl > 0)
80
+ await this.redis.set(this.#key(id), JSON.stringify(found), 'EX', ttl);
81
+ else
82
+ await this.redis.set(this.#key(id), JSON.stringify(found));
83
+ }
84
+ async destroy(id) {
85
+ await this.redis.del(this.#key(id));
86
+ }
87
+ async revokeByGrantId(grantId) {
88
+ const gk = this.#grantKey(grantId);
89
+ const keys = await this.redis.lrange(gk, 0, -1);
90
+ const multi = this.redis.multi();
91
+ keys.forEach((k) => multi.del(k));
92
+ multi.del(gk);
93
+ await multi.exec();
94
+ }
95
+ /**
96
+ * Enumeração genérica dos artefatos do model deste adapter via SCAN sobre o
97
+ * prefixo de chave do model (`<prefix>:<name>:*`). É limpo porque cada artefato
98
+ * é uma chave única já namespaceada por `prefix` + `name`; SCAN é não-bloqueante
99
+ * (cursor) ao contrário de KEYS. Usado pelo console admin (`Client`, `Session`,
100
+ * `Grant`, tokens).
101
+ */
102
+ async list() {
103
+ const prefix = `${this.prefix}:${this.name}:`;
104
+ const result = [];
105
+ let cursor = '0';
106
+ do {
107
+ const [next, keys] = await this.redis.scan(cursor, 'MATCH', `${prefix}*`, 'COUNT', 100);
108
+ cursor = next;
109
+ for (const key of keys) {
110
+ const data = await this.redis.get(key);
111
+ if (!data)
112
+ continue;
113
+ result.push({
114
+ id: key.slice(prefix.length),
115
+ payload: JSON.parse(data),
116
+ });
117
+ }
118
+ } while (cursor !== '0');
119
+ result.sort((a, b) => a.id.localeCompare(b.id));
120
+ return result;
121
+ }
122
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Tipos de eventos de auditoria relevantes para segurança emitidos pelo IdP.
3
+ */
4
+ export type AuditEventType = 'login.success' | 'login.failure' | 'signup' | 'password_reset.issued' | 'password_reset.consumed' | 'pat.issued' | 'pat.revoked' | 'pat.used' | 'impersonation' | 'impersonation.started' | 'mfa.enabled' | 'mfa.disabled' | 'account.locked' | 'passkey.registered' | 'passkey.removed' | 'email_verification.issued' | 'email_verification.consumed' | 'client.created' | 'client.updated' | 'client.deleted' | 'session.revoked_all' | 'password.changed' | 'password.rehashed' | 'email.change_requested' | 'email.changed' | 'login.new_ip_notified' | 'login.new_device' | 'bot_protection.rejected' | 'grant.revoked_by_user' | 'user.created' | 'user.password_reset_sent' | 'user.disabled' | 'user.enabled' | 'user.deleted' | 'profile.updated' | 'account.deleted' | 'account.exported' | 'keys.rotated' | 'organization.created' | 'organization.updated' | 'organization.deleted' | 'organization.member_added' | 'organization.member_removed' | 'organization.member_role_changed' | 'organization.member_role_updated' | 'organization.switched' | 'organization.deactivated' | 'organization.invitation_sent' | 'organization.invitation_accepted' | 'organization.invitation_revoked' | 'email_change.requested' | 'email_change.confirmed' | 'email_change.cancelled' | 'security_notice.sent' | 'settings.updated' | 'maintenance.enabled' | 'maintenance.disabled' | 'trusted_device.revoked' | 'password.expired_change_forced' | 'otp.locked' | 'otp.unlocked' | 'otp.unlock_failed' | 'sudo.confirmed' | 'session.single_enforced' | 'account.expired_login_blocked' | 'account.expiration_warned';
5
+ /**
6
+ * Evento de auditoria a registrar. O timestamp é definido pelo sink (não aqui).
7
+ */
8
+ export interface AuditEvent {
9
+ type: AuditEventType;
10
+ accountId?: string | null;
11
+ email?: string | null;
12
+ clientId?: string | null;
13
+ /** Impersonation: quem agiu (o admin). */
14
+ actorId?: string | null;
15
+ ip?: string | null;
16
+ metadata?: Record<string, unknown>;
17
+ }
18
+ /**
19
+ * Evento de auditoria já persistido (lido de volta pelo console admin). Carrega
20
+ * o id e o timestamp atribuídos pelo sink.
21
+ */
22
+ export interface StoredAuditEvent extends AuditEvent {
23
+ id: string;
24
+ createdAt: Date | string | null;
25
+ }
26
+ /** Filtros de listagem do log de auditoria (console admin). */
27
+ export interface ListAuditParams {
28
+ /** Página (1-based). Default: 1. */
29
+ page?: number;
30
+ /** Itens por página. Default: 20. */
31
+ limit?: number;
32
+ /** Filtra por tipo de evento exato. */
33
+ type?: string;
34
+ /** Filtra pelo subject (accountId) do evento. */
35
+ subject?: string;
36
+ }
37
+ /** Página de eventos + total absoluto. */
38
+ export interface AuditPage {
39
+ data: StoredAuditEvent[];
40
+ total: number;
41
+ }
42
+ /**
43
+ * Sink plugável de auditoria. Implementações devem ser best-effort: `record`
44
+ * NUNCA deve lançar para dentro do caminho da request.
45
+ *
46
+ * `list` é OPCIONAL: sinks customizados podem só implementar `record` (write-only).
47
+ * O console admin degrada graciosamente ("consulta não suportada") quando o sink
48
+ * configurado não fornece `list`.
49
+ */
50
+ export interface AuditSink {
51
+ record(event: AuditEvent): Promise<void>;
52
+ /** Lista eventos paginados (opcional — só sinks que suportam consulta). */
53
+ list?(params: ListAuditParams): Promise<AuditPage>;
54
+ /**
55
+ * Anonimiza (LGPD/GDPR) os eventos de uma conta SEM apagar o histórico: remove
56
+ * os identificadores pessoais (`email`, `ip`) e re-escreve o `accountId` para um
57
+ * pseudônimo estável (`anon:<hash>`), preservando a linha do tempo, o `type` e a
58
+ * forma para auditoria/forense. OPCIONAL: sinks write-only podem omitir — a
59
+ * deleção de conta segue funcionando (best-effort), só não anonimiza.
60
+ * Retorna a quantidade de linhas afetadas.
61
+ */
62
+ anonymizeAccount?(accountId: string): Promise<number>;
63
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ import type { AuditSink } from './audit_sink.js';
2
+ /**
3
+ * Implementação default do {@link AuditSink} sobre um model Lucid composto de
4
+ * `withAuditLog()`. Insere o evento na tabela `audit_logs` e suporta consulta
5
+ * paginada para o console admin.
6
+ *
7
+ * BEST-EFFORT: erros de inserção são logados via `console.error` e engolidos —
8
+ * a auditoria nunca deve lançar para dentro do caminho da request.
9
+ */
10
+ export declare function lucidAuditSink(Model: any): AuditSink;
@@ -0,0 +1,99 @@
1
+ import { createHash } from 'node:crypto';
2
+ /**
3
+ * Pseudônimo estável para um accountId anonimizado: `anon:<sha256(accountId)[:16]>`.
4
+ * Mantém os eventos de uma conta deletada correlacionáveis ENTRE SI (mesmo
5
+ * pseudônimo) sem permitir reidentificar a pessoa (one-way).
6
+ */
7
+ function anonId(accountId) {
8
+ return `anon:${createHash('sha256').update(accountId).digest('hex').slice(0, 16)}`;
9
+ }
10
+ /**
11
+ * Implementação default do {@link AuditSink} sobre um model Lucid composto de
12
+ * `withAuditLog()`. Insere o evento na tabela `audit_logs` e suporta consulta
13
+ * paginada para o console admin.
14
+ *
15
+ * BEST-EFFORT: erros de inserção são logados via `console.error` e engolidos —
16
+ * a auditoria nunca deve lançar para dentro do caminho da request.
17
+ */
18
+ export function lucidAuditSink(Model) {
19
+ return {
20
+ async record(event) {
21
+ try {
22
+ await Model.create({
23
+ type: event.type,
24
+ accountId: event.accountId ?? null,
25
+ email: event.email ?? null,
26
+ clientId: event.clientId ?? null,
27
+ actorId: event.actorId ?? null,
28
+ ip: event.ip ?? null,
29
+ metadata: event.metadata ?? null,
30
+ });
31
+ }
32
+ catch (error) {
33
+ // eslint-disable-next-line no-console
34
+ console.error('[authkit] audit sink falhou ao registrar evento', event.type, error);
35
+ }
36
+ },
37
+ async list(params) {
38
+ const page = Math.max(1, params.page ?? 1);
39
+ const limit = Math.max(1, params.limit ?? 20);
40
+ const base = () => {
41
+ const q = Model.query();
42
+ if (params.type)
43
+ q.where('type', params.type);
44
+ if (params.subject)
45
+ q.where('accountId', params.subject);
46
+ return q;
47
+ };
48
+ const countResult = await base().count('* as total');
49
+ const total = Number(countResult[0]?.$extras?.total ?? 0);
50
+ const rows = await base()
51
+ .orderBy('createdAt', 'desc')
52
+ .offset((page - 1) * limit)
53
+ .limit(limit);
54
+ const data = rows.map((row) => ({
55
+ id: String(row.id),
56
+ type: row.type,
57
+ accountId: row.accountId ?? null,
58
+ email: row.email ?? null,
59
+ clientId: row.clientId ?? null,
60
+ actorId: row.actorId ?? null,
61
+ ip: row.ip ?? null,
62
+ metadata: row.metadata ?? undefined,
63
+ // createdAt é um luxon DateTime no Lucid; normaliza para ISO string.
64
+ createdAt: row.createdAt?.toISO?.() ?? row.createdAt ?? null,
65
+ }));
66
+ return { data, total };
67
+ },
68
+ /**
69
+ * Anonimiza (LGPD/GDPR) as linhas de audit cujo subject (`accountId`) OU ator
70
+ * (`actorId`) é a conta dada: substitui o id pelo pseudônimo estável e zera os
71
+ * identificadores pessoais (`email`, `ip`). Preserva `type`, `clientId`,
72
+ * `metadata` e o timestamp — o histórico continua íntegro, só não é mais
73
+ * reidentificável. Best-effort: erros são logados e engolidos (retorna 0).
74
+ */
75
+ async anonymizeAccount(accountId) {
76
+ if (!accountId)
77
+ return 0;
78
+ const pseudonym = anonId(accountId);
79
+ try {
80
+ // accountId === conta deletada → subject.
81
+ const asSubject = await Model.query()
82
+ .where('accountId', accountId)
83
+ .update({ accountId: pseudonym, email: null, ip: null });
84
+ // actorId === conta deletada (ações que ela fez como admin) → ator.
85
+ const asActor = await Model.query()
86
+ .where('actorId', accountId)
87
+ .update({ actorId: pseudonym });
88
+ // O retorno do update varia por dialeto (número ou array). Normaliza.
89
+ const n = (v) => (Array.isArray(v) ? Number(v[0] ?? 0) : Number(v ?? 0));
90
+ return n(asSubject) + n(asActor);
91
+ }
92
+ catch (error) {
93
+ // eslint-disable-next-line no-console
94
+ console.error('[authkit] audit sink falhou ao anonimizar conta', accountId, error);
95
+ return 0;
96
+ }
97
+ },
98
+ };
99
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Ace command: `authkit:accounts:expire-scan`
3
+ *
4
+ * Varre contas para detectar expiração por inatividade (account_expiration setting).
5
+ *
6
+ * Mecânica honesta:
7
+ * - "Última atividade" = último `login.success` da conta no audit (list capability).
8
+ * - Sem audit queryável → aborta com aviso (feature indisponível).
9
+ * - Usa `audit.list({ type: 'login.success', subject: accountId })` por conta — de
10
+ * forma paginada sobre `accountStore.listAccounts`. Esse padrão é O(accounts) em
11
+ * queries ao audit, o que é compatível com volumes moderados (<= 50k contas).
12
+ * Para volumes maiores, o operador deve implementar um sink com agregação nativa.
13
+ * - `--warn`: envia e-mail de aviso ("sua conta expira em N dias — faça login") via
14
+ * hook `mail.onAccountExpirationWarning` + template default. Deduplicado via
15
+ * audit `account.expiration_warned` — não re-avisa quem já foi avisado dentro
16
+ * da janela de `warnDays` dias.
17
+ * - `--dry-run`: reporta sem enviar e-mails nem auditar.
18
+ * - `--json`: output machine-readable.
19
+ *
20
+ * Designed pra cron do host (ex.: `0 2 * * * node ace authkit:accounts:expire-scan --warn`).
21
+ */
22
+ import type { ApplicationService } from '@adonisjs/core/types';
23
+ export interface ExpireScanResult {
24
+ /** Contas já expiradas (bloqueadas no login). */
25
+ expired: Array<{
26
+ accountId: string;
27
+ email: string;
28
+ lastActivityDaysAgo: number | null;
29
+ }>;
30
+ /** Contas a expirar em até warnDays dias. */
31
+ warnSoon: Array<{
32
+ accountId: string;
33
+ email: string;
34
+ expiresInDays: number;
35
+ }>;
36
+ /** Avisos enviados por e-mail (quando --warn). */
37
+ warned: number;
38
+ /** Contas ignoradas por já terem sido avisadas na janela. */
39
+ deduped: number;
40
+ /** Resumo do scan. */
41
+ summary: {
42
+ scanned: number;
43
+ expired: number;
44
+ warnSoon: number;
45
+ inactiveDays: number;
46
+ warnDays: number;
47
+ auditSupported: boolean;
48
+ dryRun: boolean;
49
+ };
50
+ }
51
+ export declare function runExpireScan(app: ApplicationService, opts: {
52
+ dryRun?: boolean;
53
+ warn?: boolean;
54
+ json?: boolean;
55
+ logger: {
56
+ info: (m: string) => void;
57
+ warn: (m: string) => void;
58
+ error: (m: string) => void;
59
+ };
60
+ }): Promise<ExpireScanResult>;
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Ace command: `authkit:accounts:expire-scan`
3
+ *
4
+ * Varre contas para detectar expiração por inatividade (account_expiration setting).
5
+ *
6
+ * Mecânica honesta:
7
+ * - "Última atividade" = último `login.success` da conta no audit (list capability).
8
+ * - Sem audit queryável → aborta com aviso (feature indisponível).
9
+ * - Usa `audit.list({ type: 'login.success', subject: accountId })` por conta — de
10
+ * forma paginada sobre `accountStore.listAccounts`. Esse padrão é O(accounts) em
11
+ * queries ao audit, o que é compatível com volumes moderados (<= 50k contas).
12
+ * Para volumes maiores, o operador deve implementar um sink com agregação nativa.
13
+ * - `--warn`: envia e-mail de aviso ("sua conta expira em N dias — faça login") via
14
+ * hook `mail.onAccountExpirationWarning` + template default. Deduplicado via
15
+ * audit `account.expiration_warned` — não re-avisa quem já foi avisado dentro
16
+ * da janela de `warnDays` dias.
17
+ * - `--dry-run`: reporta sem enviar e-mails nem auditar.
18
+ * - `--json`: output machine-readable.
19
+ *
20
+ * Designed pra cron do host (ex.: `0 2 * * * node ace authkit:accounts:expire-scan --warn`).
21
+ */
22
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
23
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
24
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
25
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
26
+ });
27
+ }
28
+ return path;
29
+ };
30
+ import { resolveAuthkitConfig } from './resolve_config.js';
31
+ import { RuntimeSettings } from '../host/runtime_settings.js';
32
+ import { resolveEffectiveAccountExpiration } from '../host/runtime_toggles.js';
33
+ // ---------------------------------------------------------------------------
34
+ // Core logic (pura, testável sem Ace)
35
+ // ---------------------------------------------------------------------------
36
+ export async function runExpireScan(app, opts) {
37
+ const { dryRun = false, warn = false, logger } = opts;
38
+ // ---- resolve config ----
39
+ const raw = app.config?.get?.('authkit') ?? null;
40
+ const cfg = await resolveAuthkitConfig(app, raw);
41
+ if (!cfg) {
42
+ logger.error('[expire-scan] Could not resolve authkit config.');
43
+ return emptyResult(dryRun, 0, 0);
44
+ }
45
+ const accountStore = cfg.accountStore;
46
+ const audit = cfg.audit;
47
+ // ---- audit capability check ----
48
+ if (typeof audit?.list !== 'function') {
49
+ logger.warn('[expire-scan] Audit sink does not support list(). Account expiration requires a queryable audit (e.g. lucidAuditSink). Feature unavailable.');
50
+ return emptyResult(dryRun, 0, 0, false);
51
+ }
52
+ // ---- resolve setting ----
53
+ let runtimeSettings = null;
54
+ try {
55
+ const db = await app.container.make('lucid.db');
56
+ const connection = accountStore?.connectionName;
57
+ runtimeSettings = new RuntimeSettings(db, connection ? { connection } : {});
58
+ }
59
+ catch {
60
+ // Sem DB → usa defaults.
61
+ }
62
+ const expiration = runtimeSettings
63
+ ? await resolveEffectiveAccountExpiration(runtimeSettings)
64
+ : { enabled: false, inactiveDays: 365, warnDays: 14 };
65
+ if (!expiration.enabled && !dryRun) {
66
+ logger.warn('[expire-scan] account_expiration setting is disabled. Pass --dry-run to scan anyway.');
67
+ return emptyResult(dryRun, expiration.inactiveDays, expiration.warnDays);
68
+ }
69
+ const { inactiveDays, warnDays } = expiration;
70
+ const nowMs = Date.now();
71
+ const expiryCutoffMs = nowMs - inactiveDays * 24 * 60 * 60 * 1000;
72
+ const warnCutoffMs = nowMs - (inactiveDays - warnDays) * 24 * 60 * 60 * 1000;
73
+ // ---- scan accounts ----
74
+ const expired = [];
75
+ const warnSoon = [];
76
+ let warnedCount = 0;
77
+ let dedupedCount = 0;
78
+ let scanned = 0;
79
+ let page = 1;
80
+ const limit = 100;
81
+ while (true) {
82
+ const batch = await accountStore.listAccounts({ page, limit });
83
+ if (batch.data.length === 0)
84
+ break;
85
+ for (const account of batch.data) {
86
+ scanned++;
87
+ const lastMs = await getLastLoginMs(audit, account.id);
88
+ if (lastMs === null) {
89
+ // Nunca logou → trata como ativa (conta nova).
90
+ continue;
91
+ }
92
+ const daysAgo = (nowMs - lastMs) / (24 * 60 * 60 * 1000);
93
+ if (lastMs < expiryCutoffMs) {
94
+ // Expirada.
95
+ expired.push({
96
+ accountId: account.id,
97
+ email: account.email,
98
+ lastActivityDaysAgo: Math.floor(daysAgo),
99
+ });
100
+ }
101
+ else if (warnDays > 0 && lastMs < warnCutoffMs) {
102
+ // A expirar em breve.
103
+ const daysUntilExpiry = Math.ceil((lastMs + inactiveDays * 24 * 60 * 60 * 1000 - nowMs) / (24 * 60 * 60 * 1000));
104
+ warnSoon.push({ accountId: account.id, email: account.email, expiresInDays: daysUntilExpiry });
105
+ if (warn && !dryRun) {
106
+ // Verifica se já foi avisado dentro da janela de warnDays dias.
107
+ const alreadyWarned = await wasWarnedRecently(audit, account.id, warnDays);
108
+ if (alreadyWarned) {
109
+ dedupedCount++;
110
+ }
111
+ else {
112
+ // Envia e-mail de aviso.
113
+ await sendWarnEmail(cfg, account, daysUntilExpiry);
114
+ // Audita para deduplicação.
115
+ await audit.record({
116
+ type: 'account.expiration_warned',
117
+ accountId: account.id,
118
+ email: account.email,
119
+ metadata: { expiresInDays: daysUntilExpiry },
120
+ });
121
+ warnedCount++;
122
+ }
123
+ }
124
+ }
125
+ }
126
+ if (batch.data.length < limit)
127
+ break;
128
+ page++;
129
+ }
130
+ const result = {
131
+ expired,
132
+ warnSoon,
133
+ warned: warnedCount,
134
+ deduped: dedupedCount,
135
+ summary: {
136
+ scanned,
137
+ expired: expired.length,
138
+ warnSoon: warnSoon.length,
139
+ inactiveDays,
140
+ warnDays,
141
+ auditSupported: true,
142
+ dryRun,
143
+ },
144
+ };
145
+ return result;
146
+ }
147
+ // ---------------------------------------------------------------------------
148
+ // Helpers
149
+ // ---------------------------------------------------------------------------
150
+ /** Retorna o timestamp em ms do último login.success da conta, ou null se nunca logou. */
151
+ async function getLastLoginMs(audit, accountId) {
152
+ try {
153
+ const result = await audit.list({ type: 'login.success', subject: accountId, page: 1, limit: 1 });
154
+ if (result.data.length === 0)
155
+ return null;
156
+ const createdAt = result.data[0].createdAt;
157
+ if (!createdAt)
158
+ return null;
159
+ const ms = createdAt instanceof Date ? createdAt.getTime() : Date.parse(createdAt);
160
+ return Number.isFinite(ms) ? ms : null;
161
+ }
162
+ catch {
163
+ return null;
164
+ }
165
+ }
166
+ /**
167
+ * Verifica se a conta já foi avisada sobre expiração dentro da janela de `warnDays` dias.
168
+ * Usa o audit `account.expiration_warned` para deduplicação.
169
+ */
170
+ async function wasWarnedRecently(audit, accountId, warnDays) {
171
+ try {
172
+ const result = await audit.list({ type: 'account.expiration_warned', subject: accountId, page: 1, limit: 1 });
173
+ if (result.data.length === 0)
174
+ return false;
175
+ const lastWarned = result.data[0].createdAt;
176
+ if (!lastWarned)
177
+ return false;
178
+ const lastMs = lastWarned instanceof Date ? lastWarned.getTime() : Date.parse(lastWarned);
179
+ if (!Number.isFinite(lastMs))
180
+ return false;
181
+ const windowMs = warnDays * 24 * 60 * 60 * 1000;
182
+ return Date.now() - lastMs < windowMs;
183
+ }
184
+ catch {
185
+ return false;
186
+ }
187
+ }
188
+ /** Envia e-mail de aviso de expiração iminente. Best-effort (silencioso em caso de erro). */
189
+ async function sendWarnEmail(cfg, account, expiresInDays) {
190
+ try {
191
+ if (cfg.mail?.onAccountExpirationWarning) {
192
+ await cfg.mail.onAccountExpirationWarning({ email: account.email, expiresInDays });
193
+ return;
194
+ }
195
+ // Default: tenta via @adonisjs/mail se disponível.
196
+ const mailSpecifier = '@adonisjs/mail/services/main';
197
+ const mailMod = await import(__rewriteRelativeImportExtension(mailSpecifier)).catch(() => null);
198
+ const mail = mailMod?.default ?? null;
199
+ if (!mail)
200
+ return; // Sem mail configurado — silêncio.
201
+ await mail.send((message) => {
202
+ message
203
+ .to(account.email)
204
+ .subject(`Your account will be deactivated due to inactivity`)
205
+ .html(`<p>Your account will be deactivated in <strong>${expiresInDays} day(s)</strong> due to inactivity. Sign in to keep your account active.</p>`)
206
+ .text(`Your account will be deactivated in ${expiresInDays} day(s) due to inactivity. Sign in to keep your account active.`);
207
+ });
208
+ }
209
+ catch {
210
+ // Best-effort: nunca propaga erros de e-mail.
211
+ }
212
+ }
213
+ function emptyResult(dryRun, inactiveDays, warnDays, auditSupported = false) {
214
+ return {
215
+ expired: [],
216
+ warnSoon: [],
217
+ warned: 0,
218
+ deduped: 0,
219
+ summary: {
220
+ scanned: 0,
221
+ expired: 0,
222
+ warnSoon: 0,
223
+ inactiveDays,
224
+ warnDays,
225
+ auditSupported,
226
+ dryRun,
227
+ },
228
+ };
229
+ }