@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,25 @@
1
+ import type { ResolvedServerConfig } from '../define_config.js';
2
+ /**
3
+ * Parâmetros PRONTOS do RFC 8693 Token Exchange para o admin assumir a identidade
4
+ * de um usuário-alvo. NÃO é um bypass de auth: o exchange real (no
5
+ * `token_endpoint`) exige um access token do PRÓPRIO admin como `subject_token`
6
+ * (que o handler valida ter a role admin) e um client habilitado ao grant
7
+ * token-exchange. O painel só monta os parâmetros + um curl pronto — honesto com
8
+ * o mecanismo que já existe em `provider/token_exchange.ts`.
9
+ */
10
+ export interface ImpersonationPanel {
11
+ tokenEndpoint: string;
12
+ grantType: string;
13
+ subjectTokenType: string;
14
+ requestedSubject: string;
15
+ /** Client habilitado ao grant token-exchange usado no exemplo. */
16
+ clientId: string;
17
+ /** Comando curl pronto (subject_token como placeholder a preencher). */
18
+ curl: string;
19
+ }
20
+ /**
21
+ * Monta o painel de impersonation para o `targetId`. Retorna `null` quando NENHUM
22
+ * client da config tem o grant token-exchange habilitado (sem ele o fluxo não
23
+ * funciona) — a UI então mostra a instrução de habilitar o grant.
24
+ */
25
+ export declare function buildImpersonationPanel(cfg: Pick<ResolvedServerConfig, 'issuer' | 'clients'>, targetId: string): ImpersonationPanel | null;
@@ -0,0 +1,31 @@
1
+ const TOKEN_EXCHANGE = 'urn:ietf:params:oauth:grant-type:token-exchange';
2
+ const ACCESS_TOKEN_TYPE = 'urn:ietf:params:oauth:token-type:access_token';
3
+ /**
4
+ * Monta o painel de impersonation para o `targetId`. Retorna `null` quando NENHUM
5
+ * client da config tem o grant token-exchange habilitado (sem ele o fluxo não
6
+ * funciona) — a UI então mostra a instrução de habilitar o grant.
7
+ */
8
+ export function buildImpersonationPanel(cfg, targetId) {
9
+ const client = cfg.clients.find((c) => (c.grants ?? []).includes(TOKEN_EXCHANGE));
10
+ if (!client)
11
+ return null;
12
+ const tokenEndpoint = `${cfg.issuer.replace(/\/+$/, '')}/token`;
13
+ const auth = client.clientSecret
14
+ ? ` -u '${client.clientId}:${client.clientSecret}' \\\n`
15
+ : ` -d 'client_id=${client.clientId}' \\\n`;
16
+ const curl = `curl -X POST '${tokenEndpoint}' \\\n` +
17
+ auth +
18
+ ` -d 'grant_type=${TOKEN_EXCHANGE}' \\\n` +
19
+ ` -d 'subject_token=<ADMIN_ACCESS_TOKEN>' \\\n` +
20
+ ` -d 'subject_token_type=${ACCESS_TOKEN_TYPE}' \\\n` +
21
+ ` -d 'requested_subject=${targetId}' \\\n` +
22
+ ` -d 'scope=openid profile email'`;
23
+ return {
24
+ tokenEndpoint,
25
+ grantType: TOKEN_EXCHANGE,
26
+ subjectTokenType: ACCESS_TOKEN_TYPE,
27
+ requestedSubject: targetId,
28
+ clientId: client.clientId,
29
+ curl,
30
+ };
31
+ }
@@ -0,0 +1,14 @@
1
+ import type { SettingsCapability } from './runtime_settings.js';
2
+ export interface KeyRotationSetting {
3
+ enabled?: boolean;
4
+ maxAgeDays?: number;
5
+ keep?: number;
6
+ }
7
+ export interface ResolvedKeyRotationSetting {
8
+ enabled: boolean;
9
+ maxAgeDays: number;
10
+ keep: number;
11
+ }
12
+ export declare const KEY_ROTATION_DEFAULTS: ResolvedKeyRotationSetting;
13
+ /** Resolve a setting `key_rotation` em runtime (fail-safe → defaults). */
14
+ export declare function resolveEffectiveKeyRotation(settings: SettingsCapability): Promise<ResolvedKeyRotationSetting>;
@@ -0,0 +1,27 @@
1
+ import { SETTING_KEYS } from './runtime_toggles.js';
2
+ export const KEY_ROTATION_DEFAULTS = {
3
+ enabled: false,
4
+ maxAgeDays: 90,
5
+ keep: 2,
6
+ };
7
+ /** Resolve a setting `key_rotation` em runtime (fail-safe → defaults). */
8
+ export async function resolveEffectiveKeyRotation(settings) {
9
+ try {
10
+ const raw = await settings.getSetting(SETTING_KEYS.KEY_ROTATION);
11
+ if (raw === null || raw === undefined)
12
+ return KEY_ROTATION_DEFAULTS;
13
+ if (typeof raw !== 'object' || Array.isArray(raw))
14
+ return KEY_ROTATION_DEFAULTS;
15
+ const s = raw;
16
+ return {
17
+ enabled: typeof s.enabled === 'boolean' ? s.enabled : KEY_ROTATION_DEFAULTS.enabled,
18
+ maxAgeDays: typeof s.maxAgeDays === 'number' && s.maxAgeDays >= 1
19
+ ? Math.floor(s.maxAgeDays)
20
+ : KEY_ROTATION_DEFAULTS.maxAgeDays,
21
+ keep: typeof s.keep === 'number' && s.keep >= 1 ? Math.floor(s.keep) : KEY_ROTATION_DEFAULTS.keep,
22
+ };
23
+ }
24
+ catch {
25
+ return KEY_ROTATION_DEFAULTS;
26
+ }
27
+ }
@@ -0,0 +1,42 @@
1
+ import { type ResolvedKeyRotationSetting } from './key_rotation.js';
2
+ import type { SettingsCapability } from './runtime_settings.js';
3
+ import type { ManagedKeyInfo } from '../keys/keystore.js';
4
+ /**
5
+ * Ações compartilhadas de rotação de chave. Funções puras-ish que tanto a Admin
6
+ * REST API (Bearer) quanto o console (sessão) chamam, evitando duplicar a lógica
7
+ * de status (idade + política + ETA) e de rotação (validação de keep/retire).
8
+ */
9
+ export interface KeysStatus {
10
+ ageDays: number;
11
+ policy: ResolvedKeyRotationSetting;
12
+ nextRotationInDays: number | null;
13
+ keys: ManagedKeyInfo[];
14
+ }
15
+ /**
16
+ * Status da chave de assinatura managed. Retorna null quando o serviço não tem
17
+ * keystore gerenciável (jwks não é managed+store) — o controller traduz para 501.
18
+ * A política vem do runtime settings; sem settings, usa defaults (rotação off).
19
+ */
20
+ export declare function buildKeysStatus(svc: {
21
+ keystoreAgeDays(): Promise<number | null>;
22
+ listManagedKeys(): Promise<ManagedKeyInfo[]>;
23
+ }, settings: SettingsCapability | null): Promise<KeysStatus | null>;
24
+ /**
25
+ * Rotaciona a chave agora. Valida `keep` (>= 1, default 2) e `retire` do body.
26
+ * A aplicação ao vivo + auditoria (keys.rotated) acontecem dentro de svc.rotateKeys.
27
+ */
28
+ export declare function rotateNow(svc: {
29
+ rotateKeys(keep: number, retire?: boolean): Promise<{
30
+ newKid: string;
31
+ retiredKids: string[];
32
+ keptKids: string[];
33
+ }>;
34
+ }, body: {
35
+ retire?: boolean;
36
+ keep?: number;
37
+ } | undefined): Promise<{
38
+ rotated: true;
39
+ newKid: string;
40
+ retiredKids: string[];
41
+ keptKids: string[];
42
+ }>;
@@ -0,0 +1,27 @@
1
+ import { resolveEffectiveKeyRotation } from './key_rotation.js';
2
+ /**
3
+ * Status da chave de assinatura managed. Retorna null quando o serviço não tem
4
+ * keystore gerenciável (jwks não é managed+store) — o controller traduz para 501.
5
+ * A política vem do runtime settings; sem settings, usa defaults (rotação off).
6
+ */
7
+ export async function buildKeysStatus(svc, settings) {
8
+ const ageDays = await svc.keystoreAgeDays();
9
+ if (ageDays === null)
10
+ return null;
11
+ const policy = settings
12
+ ? await resolveEffectiveKeyRotation(settings)
13
+ : { enabled: false, maxAgeDays: 90, keep: 2 };
14
+ const nextRotationInDays = policy.enabled ? Math.max(0, policy.maxAgeDays - ageDays) : null;
15
+ const keys = await svc.listManagedKeys();
16
+ return { ageDays, policy, nextRotationInDays, keys };
17
+ }
18
+ /**
19
+ * Rotaciona a chave agora. Valida `keep` (>= 1, default 2) e `retire` do body.
20
+ * A aplicação ao vivo + auditoria (keys.rotated) acontecem dentro de svc.rotateKeys.
21
+ */
22
+ export async function rotateNow(svc, body) {
23
+ const retire = body?.retire === true;
24
+ const keep = typeof body?.keep === 'number' && body.keep >= 1 ? Math.floor(body.keep) : 2;
25
+ const res = await svc.rotateKeys(keep, retire);
26
+ return { rotated: true, ...res };
27
+ }
@@ -0,0 +1,98 @@
1
+ import type { AuthAccount } from '../accounts/account_store.js';
2
+ import type { AuditSink } from '../audit/audit_sink.js';
3
+ import type { ResolvedServerConfig } from '../define_config.js';
4
+ import type { SettingsCapability } from './runtime_settings.js';
5
+ /**
6
+ * `requireVerifiedEmail` efetivo (config ou runtime setting) E o store sabe dizer
7
+ * se o e-mail está verificado E a conta NÃO está verificada → bloqueia.
8
+ * Capability-probed: sem {@link EmailVerificationStatusCapability} a checagem é
9
+ * no-op (não bloqueia). Compartilhado pelos três fluxos de login (senha, magic
10
+ * link, passkey-first).
11
+ *
12
+ * Suporta `graceDays`: se a conta foi criada há menos de `graceDays` dias e ainda
13
+ * não verificou o e-mail, o login é permitido (mas a UI mostra um banner avisando).
14
+ *
15
+ * @param settings - SettingsCapability para ler o runtime setting. Quando
16
+ * ausente (chamadores que ainda não injetam o settings), faz fallback ao config.
17
+ */
18
+ export declare function isEmailUnverifiedBlock(cfg: ResolvedServerConfig, accountId: string, settings?: SettingsCapability): Promise<boolean>;
19
+ /**
20
+ * Verifica se a senha da conta está vencida (password expiration).
21
+ * Capability-probed: sem `getPasswordChangedAt` → retorna false.
22
+ * Quando `password_expiration.enabled` é false ou a coluna não existe → false.
23
+ *
24
+ * @returns true se a senha expirou e deve ser trocada antes de completar o login.
25
+ */
26
+ export declare function isPasswordExpired(cfg: ResolvedServerConfig, accountId: string, settings: SettingsCapability): Promise<boolean>;
27
+ /**
28
+ * Verifica se a conta está expirada por inatividade (account_expiration setting).
29
+ *
30
+ * "Última atividade" = timestamp do último `login.success` da conta no audit.
31
+ * Capability-probed: sem `audit.list` → retorna false (feature indisponível).
32
+ * Quando enabled=false ou sem audit queryável → false (no-op).
33
+ *
34
+ * @param audit - AuditSink. Quando ausente ou sem `list`, retorna false.
35
+ * @param accountId - ID da conta a verificar.
36
+ * @param settings - SettingsCapability para ler a setting `account_expiration`.
37
+ * @param logger - logger mínimo opcional. Quando presente, registra falhas de DB
38
+ * no caminho fail-safe (sem ele, o erro continua silencioso por compat).
39
+ * @returns true se a conta está expirada e o login deve ser bloqueado.
40
+ */
41
+ export declare function isAccountExpired(audit: AuditSink | null | undefined, accountId: string, settings: SettingsCapability, logger?: LoginAttemptLogger): Promise<boolean>;
42
+ /** Logger mínimo (subconjunto do logger do AdonisJS) para o caminho fail-safe. */
43
+ export interface LoginAttemptLogger {
44
+ warn(obj: unknown, msg?: string): void;
45
+ }
46
+ /** Entrada de uma tentativa de login por senha (keyed por email). */
47
+ export interface PasswordLoginInput {
48
+ email: string;
49
+ password: string;
50
+ /** IP da request (para auditoria + lockout). null quando indisponível. */
51
+ ip: string | null;
52
+ /**
53
+ * client_id da interaction OIDC, quando existir. Só é incluído no evento de
54
+ * auditoria nos fluxos que o têm (interaction); ausente no console de conta.
55
+ */
56
+ clientId?: string | null;
57
+ /**
58
+ * SettingsCapability para ler o runtime setting de `require_verified_email`.
59
+ * Quando ausente, faz fallback ao valor de `cfg.login.requireVerifiedEmail`.
60
+ */
61
+ settings?: SettingsCapability;
62
+ /**
63
+ * Logger mínimo opcional. Repassado aos caminhos fail-safe (ex.: checagem de
64
+ * expiração por inatividade) para que falhas de infra não fiquem silenciosas.
65
+ */
66
+ logger?: LoginAttemptLogger;
67
+ }
68
+ /** Resultado de {@link attemptPasswordLogin}. */
69
+ export type PasswordLoginResult = {
70
+ ok: true;
71
+ account: AuthAccount;
72
+ } | {
73
+ ok: false;
74
+ locked: boolean;
75
+ retryAfterSec?: number;
76
+ disabled?: boolean;
77
+ unverified?: boolean;
78
+ unverifiedGraceDays?: number;
79
+ passwordExpired?: boolean;
80
+ accountExpired?: boolean;
81
+ account?: AuthAccount;
82
+ };
83
+ /**
84
+ * Sequência canônica de login por senha + bloqueio progressivo, compartilhada
85
+ * pelos dois fluxos que pedem senha (interaction OIDC e console de conta):
86
+ *
87
+ * 1. `lockout.isLocked(email)` — se travada, NÃO verifica a senha; devolve
88
+ * `{ ok: false, locked: true, retryAfterSec }` (o controller renderiza o erro).
89
+ * 2. `verifyCredentials(email, password)` — em falha: emite `login.failure`
90
+ * (com `clientId` apenas quando fornecido), registra a falha no lockout
91
+ * (`{ sink: cfg.audit, ip }`) e devolve `{ ok: false, locked: false }`.
92
+ * 3. Em sucesso: limpa o contador de falhas e devolve `{ ok: true, account }`.
93
+ *
94
+ * O evento `login.success` e a finalização da sessão/interaction ficam a cargo de
95
+ * cada controller (os fluxos diferem: MFA gate na interaction, sessão no console),
96
+ * assim como toda a renderização.
97
+ */
98
+ export declare function attemptPasswordLogin(cfg: ResolvedServerConfig, input: PasswordLoginInput): Promise<PasswordLoginResult>;
@@ -0,0 +1,211 @@
1
+ import { supportsAccountStatus, supportsEmailVerificationStatus, supportsPasswordExpiration, } from '../accounts/account_store.js';
2
+ import { createAccountLockout } from './account_lockout.js';
3
+ import { resolveEffectiveRequireVerifiedEmail, resolveEffectiveRequireVerifiedEmailFull, resolveEffectivePasswordExpiration, resolveEffectiveAccountExpiration, resolveEffectiveLockout, } from './runtime_toggles.js';
4
+ /**
5
+ * `requireVerifiedEmail` efetivo (config ou runtime setting) E o store sabe dizer
6
+ * se o e-mail está verificado E a conta NÃO está verificada → bloqueia.
7
+ * Capability-probed: sem {@link EmailVerificationStatusCapability} a checagem é
8
+ * no-op (não bloqueia). Compartilhado pelos três fluxos de login (senha, magic
9
+ * link, passkey-first).
10
+ *
11
+ * Suporta `graceDays`: se a conta foi criada há menos de `graceDays` dias e ainda
12
+ * não verificou o e-mail, o login é permitido (mas a UI mostra um banner avisando).
13
+ *
14
+ * @param settings - SettingsCapability para ler o runtime setting. Quando
15
+ * ausente (chamadores que ainda não injetam o settings), faz fallback ao config.
16
+ */
17
+ export async function isEmailUnverifiedBlock(cfg, accountId, settings) {
18
+ // Resolve o valor efetivo: runtime setting sobrescreve o config estático.
19
+ const requireVerified = settings
20
+ ? await resolveEffectiveRequireVerifiedEmail(cfg.login?.requireVerifiedEmail ?? false, settings)
21
+ : (cfg.login?.requireVerifiedEmail ?? false);
22
+ if (!requireVerified)
23
+ return false;
24
+ if (!supportsEmailVerificationStatus(cfg.accountStore))
25
+ return false;
26
+ const verified = await cfg.accountStore.isEmailVerified(accountId);
27
+ if (verified)
28
+ return false;
29
+ // Grace period: se a setting tem graceDays e a conta foi criada recentemente,
30
+ // permite o login.
31
+ if (settings) {
32
+ const full = await resolveEffectiveRequireVerifiedEmailFull(cfg.login?.requireVerifiedEmail ?? false, settings);
33
+ if (full.graceDays > 0) {
34
+ // Busca a conta para checar created_at (se o store suportar).
35
+ const account = await cfg.accountStore.findById(accountId);
36
+ if (account) {
37
+ // Tenta ler `created_at` via duck-typing (não é parte do contrato AccountStore).
38
+ const raw = account;
39
+ const createdAt = raw.createdAt instanceof Date
40
+ ? raw.createdAt
41
+ : typeof raw.createdAt === 'string'
42
+ ? new Date(raw.createdAt)
43
+ : null;
44
+ if (createdAt) {
45
+ const graceCutoff = new Date(createdAt.getTime() + full.graceDays * 24 * 60 * 60 * 1000);
46
+ if (new Date() <= graceCutoff) {
47
+ return false; // dentro da janela de graça → não bloqueia
48
+ }
49
+ }
50
+ }
51
+ }
52
+ }
53
+ return true;
54
+ }
55
+ /**
56
+ * Verifica se a senha da conta está vencida (password expiration).
57
+ * Capability-probed: sem `getPasswordChangedAt` → retorna false.
58
+ * Quando `password_expiration.enabled` é false ou a coluna não existe → false.
59
+ *
60
+ * @returns true se a senha expirou e deve ser trocada antes de completar o login.
61
+ */
62
+ export async function isPasswordExpired(cfg, accountId, settings) {
63
+ const expiration = await resolveEffectivePasswordExpiration(settings);
64
+ if (!expiration.enabled)
65
+ return false;
66
+ if (!supportsPasswordExpiration(cfg.accountStore))
67
+ return false;
68
+ const changedAt = await cfg.accountStore.getPasswordChangedAt(accountId);
69
+ if (!changedAt) {
70
+ // Senha nunca trocada (coluna NULL = conta legacy) → considera vencida quando
71
+ // expiration está ligada (seguro: força a troca na 1ª vez).
72
+ return true;
73
+ }
74
+ const maxAgeMs = expiration.maxAgeDays * 24 * 60 * 60 * 1000;
75
+ return Date.now() - changedAt.getTime() > maxAgeMs;
76
+ }
77
+ /**
78
+ * Verifica se a conta está expirada por inatividade (account_expiration setting).
79
+ *
80
+ * "Última atividade" = timestamp do último `login.success` da conta no audit.
81
+ * Capability-probed: sem `audit.list` → retorna false (feature indisponível).
82
+ * Quando enabled=false ou sem audit queryável → false (no-op).
83
+ *
84
+ * @param audit - AuditSink. Quando ausente ou sem `list`, retorna false.
85
+ * @param accountId - ID da conta a verificar.
86
+ * @param settings - SettingsCapability para ler a setting `account_expiration`.
87
+ * @param logger - logger mínimo opcional. Quando presente, registra falhas de DB
88
+ * no caminho fail-safe (sem ele, o erro continua silencioso por compat).
89
+ * @returns true se a conta está expirada e o login deve ser bloqueado.
90
+ */
91
+ export async function isAccountExpired(audit, accountId, settings, logger) {
92
+ const expiration = await resolveEffectiveAccountExpiration(settings);
93
+ if (!expiration.enabled)
94
+ return false;
95
+ // Sem audit queryável → feature indisponível → não bloqueia.
96
+ if (typeof audit?.list !== 'function')
97
+ return false;
98
+ try {
99
+ // Busca o último login.success da conta (ordem desc, limit 1).
100
+ const result = await audit.list({ type: 'login.success', subject: accountId, page: 1, limit: 1 });
101
+ if (result.data.length === 0) {
102
+ // Nunca logou → considera ativa (conta nova, não inativa).
103
+ return false;
104
+ }
105
+ const lastLogin = result.data[0];
106
+ const createdAt = lastLogin.createdAt;
107
+ if (!createdAt)
108
+ return false;
109
+ const lastMs = createdAt instanceof Date ? createdAt.getTime() : Date.parse(createdAt);
110
+ if (!Number.isFinite(lastMs))
111
+ return false;
112
+ const cutoffMs = Date.now() - expiration.inactiveDays * 24 * 60 * 60 * 1000;
113
+ return lastMs < cutoffMs;
114
+ }
115
+ catch (err) {
116
+ // Fail-safe: erro de DB → não bloqueia. Loga: enquanto a query de audit estiver
117
+ // quebrada, a expiração por inatividade não é aplicada (admin precisa saber).
118
+ logger?.warn({ err, accountId }, 'authkit: account-expiration check failed (audit query error) — not blocking login (fail-safe); inactivity enforcement is disabled until the audit store recovers.');
119
+ return false;
120
+ }
121
+ }
122
+ /**
123
+ * Sequência canônica de login por senha + bloqueio progressivo, compartilhada
124
+ * pelos dois fluxos que pedem senha (interaction OIDC e console de conta):
125
+ *
126
+ * 1. `lockout.isLocked(email)` — se travada, NÃO verifica a senha; devolve
127
+ * `{ ok: false, locked: true, retryAfterSec }` (o controller renderiza o erro).
128
+ * 2. `verifyCredentials(email, password)` — em falha: emite `login.failure`
129
+ * (com `clientId` apenas quando fornecido), registra a falha no lockout
130
+ * (`{ sink: cfg.audit, ip }`) e devolve `{ ok: false, locked: false }`.
131
+ * 3. Em sucesso: limpa o contador de falhas e devolve `{ ok: true, account }`.
132
+ *
133
+ * O evento `login.success` e a finalização da sessão/interaction ficam a cargo de
134
+ * cada controller (os fluxos diferem: MFA gate na interaction, sessão no console),
135
+ * assim como toda a renderização.
136
+ */
137
+ export async function attemptPasswordLogin(cfg, input) {
138
+ const { email, password, ip } = input;
139
+ // Lockout efetivo: runtime setting `lockout` sobrescreve o config estático.
140
+ // FAIL-SAFE → cfg.lockout (resolveEffectiveLockout já trata erros internamente).
141
+ // `store` é infra e permanece no config estático (não vem da setting).
142
+ const effectiveLockout = input.settings
143
+ ? { ...(await resolveEffectiveLockout(input.settings, cfg.lockout)), store: cfg.lockout.store }
144
+ : cfg.lockout;
145
+ const lockout = createAccountLockout(effectiveLockout);
146
+ // Bloqueio progressivo (keyed por email): se travada, não verifica a senha.
147
+ const lock = await lockout.isLocked(email);
148
+ if (lock.locked) {
149
+ return { ok: false, locked: true, retryAfterSec: lock.retryAfterSec };
150
+ }
151
+ const account = await cfg.accountStore.verifyCredentials(email, password);
152
+ if (!account) {
153
+ // `clientId` só entra no evento quando o fluxo o fornece (interaction).
154
+ await cfg.audit?.record(input.clientId !== undefined
155
+ ? { type: 'login.failure', email, ip, clientId: input.clientId }
156
+ : { type: 'login.failure', email, ip });
157
+ await lockout.recordFailure(email, { sink: cfg.audit, ip });
158
+ return { ok: false, locked: false };
159
+ }
160
+ // Conta desabilitada: rejeita o login (mesmo com senha correta). A capacidade é
161
+ // opcional — só checada quando o store a implementa. Emite `login.failure` (com
162
+ // motivo `disabled` no metadata) e NÃO registra falha no lockout (não é tentativa
163
+ // de adivinhar senha).
164
+ if (supportsAccountStatus(cfg.accountStore) && (await cfg.accountStore.isDisabled(account.id))) {
165
+ await cfg.audit?.record(input.clientId !== undefined
166
+ ? { type: 'login.failure', email, ip, clientId: input.clientId, metadata: { reason: 'disabled' } }
167
+ : { type: 'login.failure', email, ip, metadata: { reason: 'disabled' } });
168
+ return { ok: false, locked: false, disabled: true };
169
+ }
170
+ // E-mail não verificado (LGPD/compliance): rejeita o login se a política exige
171
+ // verificação E o store sabe responder. Não conta como falha de senha (a senha
172
+ // estava correta) — limpa o contador e emite login.failure com motivo `unverified`.
173
+ if (await isEmailUnverifiedBlock(cfg, account.id, input.settings)) {
174
+ await lockout.clearFailures(email);
175
+ await cfg.audit?.record(input.clientId !== undefined
176
+ ? { type: 'login.failure', email, ip, clientId: input.clientId, metadata: { reason: 'unverified' } }
177
+ : { type: 'login.failure', email, ip, metadata: { reason: 'unverified' } });
178
+ return { ok: false, locked: false, unverified: true };
179
+ }
180
+ // Senha expirada (password expiration): se a senha está vencida, sinaliza ao
181
+ // controller para forçar a troca ANTES de completar o login.
182
+ // Capability-probed: sem `getPasswordChangedAt` ou sem a setting → no-op.
183
+ if (input.settings) {
184
+ const expired = await isPasswordExpired(cfg, account.id, input.settings);
185
+ if (expired) {
186
+ // Não é uma falha de credenciais — limpa o contador.
187
+ await lockout.clearFailures(email);
188
+ await cfg.audit?.record(input.clientId !== undefined
189
+ ? { type: 'password.expired_change_forced', accountId: account.id, email, ip, clientId: input.clientId }
190
+ : { type: 'password.expired_change_forced', accountId: account.id, email, ip });
191
+ return { ok: false, locked: false, passwordExpired: true, account: account };
192
+ }
193
+ }
194
+ // Conta expirada por inatividade (account_expiration setting): bloqueia o login
195
+ // se a conta não teve login.success há mais de `inactiveDays` dias (via audit).
196
+ // Capability-probed: sem audit.list ou sem setting → no-op.
197
+ // Reativação: fluxo de reset de senha (link exibido pelo controller na mensagem).
198
+ if (input.settings) {
199
+ const expired = await isAccountExpired(cfg.audit, account.id, input.settings, input.logger);
200
+ if (expired) {
201
+ await lockout.clearFailures(email);
202
+ await cfg.audit?.record(input.clientId !== undefined
203
+ ? { type: 'account.expired_login_blocked', accountId: account.id, email, ip, clientId: input.clientId }
204
+ : { type: 'account.expired_login_blocked', accountId: account.id, email, ip });
205
+ return { ok: false, locked: false, accountExpired: true };
206
+ }
207
+ }
208
+ // Senha correta: limpa o contador de falhas (o lockout protege a etapa de senha).
209
+ await lockout.clearFailures(email);
210
+ return { ok: true, account };
211
+ }
@@ -0,0 +1,27 @@
1
+ import type { HttpContext } from '@adonisjs/core/http';
2
+ import type { ResolvedServerConfig } from '../define_config.js';
3
+ /** Dados de um login bem-sucedido a auditar/notificar. */
4
+ export interface LoginSuccessInput {
5
+ accountId: string;
6
+ email?: string | null;
7
+ ip?: string | null;
8
+ clientId?: string | null;
9
+ /**
10
+ * Sinal "dispositivo já confiável" quando o caller JÁ resolveu o cookie de
11
+ * confiança (ex.: o controller pulou o MFA por causa dele). Quando omitido, a
12
+ * notificação resolve o cookie por conta própria (best-effort). `true` =
13
+ * dispositivo conhecido → NÃO notifica novo dispositivo.
14
+ */
15
+ trustedDevice?: boolean;
16
+ /** Metadata extra a anexar ao evento login.success (ex.: { mfa: 'totp' }). */
17
+ metadata?: Record<string, unknown>;
18
+ }
19
+ /**
20
+ * Centraliza o pós-login bem-sucedido: registra o evento `login.success` e dispara
21
+ * (best-effort) o alerta de NOVO acesso quando o IP nunca foi visto para a conta.
22
+ *
23
+ * É fire-and-forget e FAIL-SAFE: a notificação roda DEPOIS do audit e qualquer erro
24
+ * é engolido — NUNCA bloqueia nem lança no caminho do login. Substitui as chamadas
25
+ * `cfg.audit?.record({ type: 'login.success', ... })` espalhadas pelos controllers.
26
+ */
27
+ export declare function notifyLoginSuccess(ctx: HttpContext, cfg: ResolvedServerConfig, input: LoginSuccessInput): Promise<void>;
@@ -0,0 +1,142 @@
1
+ import { sendNewLoginEmail, sendNewDeviceLoginEmail } from './default_mailer.js';
2
+ import { TRUSTED_DEVICE_COOKIE, isTrustedDeviceValid } from './trusted_device.js';
3
+ /**
4
+ * Centraliza o pós-login bem-sucedido: registra o evento `login.success` e dispara
5
+ * (best-effort) o alerta de NOVO acesso quando o IP nunca foi visto para a conta.
6
+ *
7
+ * É fire-and-forget e FAIL-SAFE: a notificação roda DEPOIS do audit e qualquer erro
8
+ * é engolido — NUNCA bloqueia nem lança no caminho do login. Substitui as chamadas
9
+ * `cfg.audit?.record({ type: 'login.success', ... })` espalhadas pelos controllers.
10
+ */
11
+ export async function notifyLoginSuccess(ctx, cfg, input) {
12
+ const { accountId, email, ip, clientId, metadata, trustedDevice } = input;
13
+ // User-agent da request: anexado ao `login.success` em `metadata.userAgent` para
14
+ // dar contexto de dispositivo às sessões (o admin/account o lê via join por
15
+ // accountId+loginTs). Sem migração — usa a coluna JSON `metadata` já existente.
16
+ const ua = ctx.request.header?.('user-agent') ?? null;
17
+ // 1) Audit do login.success (mesmo formato de antes + userAgent em metadata).
18
+ await cfg.audit?.record({
19
+ type: 'login.success',
20
+ accountId,
21
+ email: email ?? null,
22
+ ip: ip ?? null,
23
+ clientId: clientId ?? null,
24
+ metadata: ua ? { ...(metadata ?? {}), userAgent: ua } : metadata,
25
+ });
26
+ // 2) Sinal de DISPOSITIVO NOVO: login sem cookie de confiança válido para a
27
+ // conta. Auditamos `login.new_device` (mesmo sem mail configurado) e, se
28
+ // `notifications.newDeviceEmail`, enviamos o alerta. Resolvido aqui (síncrono
29
+ // com o audit) porque depende do cookie da REQUEST atual; o e-mail em si é
30
+ // fire-and-forget. `trustedDevice === true` (o caller já decidiu) pula tudo.
31
+ const onNewDevice = trustedDevice === true ? false : !hasTrustedDeviceCookie(ctx, accountId);
32
+ if (onNewDevice) {
33
+ await cfg.audit?.record({
34
+ type: 'login.new_device',
35
+ accountId,
36
+ email: email ?? null,
37
+ ip: ip ?? null,
38
+ clientId: clientId ?? null,
39
+ });
40
+ }
41
+ const userAgent = ua;
42
+ // 3) Alertas por e-mail (fire-and-forget): nunca propaga erro pro login.
43
+ if (!cfg.notifications.newLoginEmail && !(onNewDevice && cfg.notifications.newDeviceEmail))
44
+ return;
45
+ void (async () => {
46
+ // Resolve o e-mail quando o caller não o forneceu (ex.: fluxo de MFA só tem o
47
+ // accountId em escopo). Best-effort.
48
+ let resolvedEmail = email ?? null;
49
+ if (!resolvedEmail) {
50
+ resolvedEmail = (await cfg.accountStore.findById(accountId))?.email ?? null;
51
+ }
52
+ if (cfg.notifications.newLoginEmail) {
53
+ await maybeNotifyNewLogin(ctx, cfg, { accountId, email: resolvedEmail, ip: ip ?? null });
54
+ }
55
+ if (onNewDevice && cfg.notifications.newDeviceEmail && resolvedEmail) {
56
+ await notifyNewDevice(ctx, cfg, {
57
+ accountId,
58
+ email: resolvedEmail,
59
+ ip: ip ?? null,
60
+ userAgent,
61
+ });
62
+ }
63
+ })().catch((error) => {
64
+ // Defensivo: o próprio handler de erro nunca pode lançar (logger pode faltar
65
+ // em contextos mínimos). Best-effort.
66
+ ctx.logger?.error?.({ err: error, accountId }, 'authkit: falha no alerta de login');
67
+ });
68
+ }
69
+ /**
70
+ * `true` se há um cookie de dispositivo confiável VÁLIDO (estrutura íntegra, conta
71
+ * casa, não expirou) para `accountId`. Best-effort: qualquer erro de leitura →
72
+ * `false` (tratado como dispositivo novo). NÃO checa o re-enrollment de MFA
73
+ * (mfaEnabledAt) — o sinal de "dispositivo conhecido" é a mera presença de um
74
+ * cookie de confiança válido, independente do estado do MFA.
75
+ */
76
+ function hasTrustedDeviceCookie(ctx, accountId) {
77
+ try {
78
+ const payload = ctx.request.encryptedCookie?.(TRUSTED_DEVICE_COOKIE);
79
+ return isTrustedDeviceValid(payload, { accountId });
80
+ }
81
+ catch {
82
+ return false;
83
+ }
84
+ }
85
+ /**
86
+ * Envia o alerta de NOVO DISPOSITIVO: usa o hook `mail.onNewDeviceLogin` (override
87
+ * do host) ou o mailer default. Best-effort — qualquer erro é engolido (já estamos
88
+ * dentro do fire-and-forget de {@link notifyLoginSuccess}).
89
+ */
90
+ async function notifyNewDevice(ctx, cfg, data) {
91
+ const timestamp = new Date().toISOString();
92
+ if (cfg.mail?.onNewDeviceLogin) {
93
+ await cfg.mail.onNewDeviceLogin({
94
+ account: { id: data.accountId, email: data.email },
95
+ ip: data.ip,
96
+ userAgent: data.userAgent,
97
+ timestamp,
98
+ });
99
+ return;
100
+ }
101
+ await sendNewDeviceLoginEmail(ctx, {
102
+ email: data.email,
103
+ ip: data.ip,
104
+ userAgent: data.userAgent,
105
+ when: timestamp,
106
+ });
107
+ }
108
+ /**
109
+ * Verifica se o IP é novo para a conta (consultando o audit sink por
110
+ * `login.success` do subject) e, se for, envia o e-mail de alerta + audita
111
+ * `login.new_ip_notified`. Degrada para no-op quando: sem IP, sem e-mail, sem
112
+ * sink consultável (`list`), ou já houve um login.success deste IP antes.
113
+ */
114
+ async function maybeNotifyNewLogin(ctx, cfg, data) {
115
+ const { accountId, email, ip } = data;
116
+ if (!ip || !email)
117
+ return;
118
+ // Sem consulta do histórico não dá pra decidir se o IP é novo → no-op.
119
+ if (typeof cfg.audit?.list !== 'function')
120
+ return;
121
+ // Lê o histórico de login.success do subject. O evento ATUAL já foi gravado por
122
+ // notifyLoginSuccess, então um IP visto antes aparece com count >= 2 para o IP.
123
+ // Buscamos uma página ampla e contamos as ocorrências deste IP.
124
+ const page = await cfg.audit.list({
125
+ type: 'login.success',
126
+ subject: accountId,
127
+ page: 1,
128
+ limit: 200,
129
+ });
130
+ const sameIpCount = page.data.filter((e) => e.ip === ip).length;
131
+ // > 1 significa que já havia um login.success deste IP antes do atual → não é novo.
132
+ if (sameIpCount > 1)
133
+ return;
134
+ const when = new Date().toISOString();
135
+ await sendNewLoginEmail(ctx, { email, ip, when });
136
+ await cfg.audit?.record({
137
+ type: 'login.new_ip_notified',
138
+ accountId,
139
+ email,
140
+ ip,
141
+ });
142
+ }
@@ -0,0 +1,7 @@
1
+ import '../augmentations.js';
2
+ import type { HttpContext } from '@adonisjs/core/http';
3
+ import type { NextFn } from '@adonisjs/core/types/http';
4
+ export declare const ACCOUNT_SESSION_KEY = "account_user_id";
5
+ export default class AccountAuthMiddleware {
6
+ handle(ctx: HttpContext, next: NextFn): Promise<any>;
7
+ }