@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,827 @@
1
+ /**
2
+ * Account Self-Service JSON API
3
+ *
4
+ * Endpoints JSON sob `/account/api/*` (session-authed via accountGuard, mutating
5
+ * com CSRF). Reusa a lógica dos controllers de conta existentes sem duplicar regras.
6
+ *
7
+ * Mapa de rotas:
8
+ * GET /account/api/me → perfil + flags
9
+ * GET /account/api/security → overview de segurança (sessões, MFA, etc.)
10
+ * PATCH /account/api/profile → atualizar nome/avatar
11
+ * POST /account/api/password → trocar senha (sudo + senha atual)
12
+ * POST /account/api/email-change → solicitar troca de e-mail (sudo)
13
+ * POST /account/api/email-change/cancel → cancelar troca pendente
14
+ * GET /account/api/sessions → sessões ativas
15
+ * DELETE /account/api/sessions/:id → revogar uma sessão
16
+ * POST /account/api/sessions/revoke-others → revogar todas exceto a atual
17
+ * POST /account/api/sessions/revoke-all → revogar TODAS + logout global (signedOut: true)
18
+ * GET /account/api/apps → grants (apps autorizados)
19
+ * DELETE /account/api/apps/:clientId → revogar consentimento de um app
20
+ * GET /account/api/mfa → status MFA (totp + passkeys + recovery)
21
+ * GET /account/api/passkeys → lista passkeys
22
+ * DELETE /account/api/passkeys/:id → remover passkey
23
+ * GET /account/api/tokens → lista PATs
24
+ * POST /account/api/tokens → criar PAT (sudo)
25
+ * DELETE /account/api/tokens/:id → revogar PAT (sudo)
26
+ * GET /account/api/orgs → lista orgs do usuário
27
+ * GET /account/api/orgs/:id → detalhe de org (requer membership)
28
+ * GET /account/api/orgs/invitations → convites pendentes
29
+ */
30
+ import '../augmentations.js';
31
+ import { ACCOUNT_SESSION_KEY } from '../middleware/account_auth.js';
32
+ import { supportsAccountSecurity, supportsProfile, supportsPasskeys, supportsOrganizations, } from '../../accounts/account_store.js';
33
+ import { changePasswordValidator, changeEmailValidator, updateProfileValidator, } from '../validators.js';
34
+ import { AdminSessionsService } from '../admin_sessions_service.js';
35
+ import { enrichSessionsWithContext } from '../session_context.js';
36
+ import { PasswordPolicyError } from '../../password/password_manager.js';
37
+ import { resolveRuntimeSettings } from '../runtime_settings.js';
38
+ import { resolveEffectiveEmailChange, resolveEffectivePasswordHistory, } from '../runtime_toggles.js';
39
+ import { dispatchSecurityNotice } from '../security_notice_service.js';
40
+ import { requireSudo, isSudoActive, SUDO_MODE_DEFAULTS, resolveEffectiveSudoMode } from '../sudo_mode.js';
41
+ import { translate } from '../i18n.js';
42
+ import { storeAvatar, isDriveAvailable, AvatarUploadError } from '../avatar_storage.js';
43
+ import { sendEmailChangeConfirmationEmail, sendEmailChangeNoticeEmail, } from '../default_mailer.js';
44
+ import { ACTIVE_ORG_COOKIE } from '../active_org_cookie.js';
45
+ // ---------------------------------------------------------------------------
46
+ // Helpers (shared with existing controllers — kept local to avoid coupling)
47
+ // ---------------------------------------------------------------------------
48
+ async function resolvePasswordHistorySettings(ctx) {
49
+ try {
50
+ const rs = await resolveRuntimeSettings(ctx);
51
+ if (rs && (await rs.isTablePresent())) {
52
+ return await resolveEffectivePasswordHistory(rs);
53
+ }
54
+ }
55
+ catch {
56
+ /* fail-safe */
57
+ }
58
+ return { enabled: false, count: 5 };
59
+ }
60
+ async function resolveEmailChangeSettings(ctx) {
61
+ try {
62
+ const rs = await resolveRuntimeSettings(ctx);
63
+ if (rs && (await rs.isTablePresent())) {
64
+ return await resolveEffectiveEmailChange(rs);
65
+ }
66
+ }
67
+ catch {
68
+ /* fail-safe */
69
+ }
70
+ return { enabled: true, ttlHours: 24, requirePassword: true };
71
+ }
72
+ /** Erro JSON padrão. */
73
+ function apiErr(code, message) {
74
+ return { error: { code, message } };
75
+ }
76
+ // ---------------------------------------------------------------------------
77
+ // Controller
78
+ // ---------------------------------------------------------------------------
79
+ export default class AccountApiController {
80
+ // ─── GET /account/api/me ─────────────────────────────────────────────────
81
+ /** Perfil + flags do usuário logado. */
82
+ async me(ctx) {
83
+ const service = await ctx.containerResolver.make('authkit.server');
84
+ const cfg = service.config;
85
+ const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
86
+ const account = await cfg.accountStore.findById(userId);
87
+ if (!account)
88
+ return ctx.response.unauthorized(apiErr('unauthorized', 'Not authenticated.'));
89
+ // Sudo mode ativo?
90
+ let sudoActive = false;
91
+ try {
92
+ const sudoSettings = await resolveRuntimeSettings(ctx);
93
+ const resolved = sudoSettings ? await resolveEffectiveSudoMode(sudoSettings) : SUDO_MODE_DEFAULTS;
94
+ sudoActive = resolved.enabled ? isSudoActive(ctx, resolved.graceMinutes) : true;
95
+ }
96
+ catch {
97
+ /* fail-safe */
98
+ }
99
+ // Contagem de passkeys (capability-probed).
100
+ let passkeyCount = 0;
101
+ if (supportsPasskeys(cfg.accountStore)) {
102
+ try {
103
+ const passkeys = await cfg.accountStore.listPasskeys(userId);
104
+ passkeyCount = passkeys.length;
105
+ }
106
+ catch {
107
+ /* fail-safe */
108
+ }
109
+ }
110
+ // MFA ativo?
111
+ let mfaEnabled = false;
112
+ try {
113
+ const mfaState = await cfg.accountStore.getMfaState?.(userId);
114
+ mfaEnabled = mfaState?.enabled ?? false;
115
+ }
116
+ catch {
117
+ /* fail-safe */
118
+ }
119
+ // hasPassword: true quando a conta tem senha (verifyCredentials retorna algo com
120
+ // o próprio e-mail; usamos flag de capacidade — stores passwordless retornam null).
121
+ // Na prática, a presença de `changePassword` no store indica suporte a senha.
122
+ const hasPassword = supportsAccountSecurity(cfg.accountStore);
123
+ return {
124
+ id: account.id,
125
+ email: account.email,
126
+ emailVerified: account.emailVerified ?? null,
127
+ name: account.name ?? null,
128
+ avatarUrl: account.avatarUrl ?? null,
129
+ globalRoles: account.globalRoles ?? [],
130
+ hasPassword,
131
+ mfaEnabled,
132
+ passkeyCount,
133
+ sudoActive,
134
+ capabilities: {
135
+ securitySupported: supportsAccountSecurity(cfg.accountStore),
136
+ profileSupported: supportsProfile(cfg.accountStore),
137
+ passkeysSupported: supportsPasskeys(cfg.accountStore),
138
+ orgsSupported: supportsOrganizations(cfg.accountStore),
139
+ tokensSupported: !!cfg.patStore,
140
+ avatarUploadSupported: await isDriveAvailable(),
141
+ sessionsSupported: new AdminSessionsService(service).canList,
142
+ },
143
+ };
144
+ }
145
+ // ─── GET /account/api/security ───────────────────────────────────────────
146
+ /** Overview de segurança: sessões ativas, status MFA, passkeys, last pw change. */
147
+ async securityOverview(ctx) {
148
+ const service = await ctx.containerResolver.make('authkit.server');
149
+ const cfg = service.config;
150
+ const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
151
+ const account = await cfg.accountStore.findById(userId);
152
+ if (!account)
153
+ return ctx.response.unauthorized(apiErr('unauthorized', 'Not authenticated.'));
154
+ // Sessões ativas (capability-probed).
155
+ const adminSessions = new AdminSessionsService(service);
156
+ const sessionsSupported = adminSessions.canList;
157
+ const rawSessions = sessionsSupported ? await adminSessions.listSessions(userId) : [];
158
+ const enriched = await enrichSessionsWithContext(cfg, userId, rawSessions);
159
+ // MFA status.
160
+ let mfaEnabled = false;
161
+ let totpEnrolled = false;
162
+ try {
163
+ const mfaState = await cfg.accountStore.getMfaState?.(userId);
164
+ mfaEnabled = mfaState?.enabled ?? false;
165
+ totpEnrolled = mfaState?.enabled ?? false;
166
+ }
167
+ catch {
168
+ /* fail-safe */
169
+ }
170
+ // Passkeys.
171
+ const passkeysSupported = supportsPasskeys(cfg.accountStore);
172
+ let passkeys = [];
173
+ if (passkeysSupported) {
174
+ try {
175
+ passkeys = await cfg.accountStore.listPasskeys(userId);
176
+ }
177
+ catch {
178
+ /* fail-safe */
179
+ }
180
+ }
181
+ // Pending email change.
182
+ const pendingEmail = account.pendingEmail ?? null;
183
+ return {
184
+ email: account.email,
185
+ pendingEmail,
186
+ securitySupported: supportsAccountSecurity(cfg.accountStore),
187
+ profileSupported: supportsProfile(cfg.accountStore),
188
+ sessionsSupported,
189
+ activeSessions: enriched.map((s) => ({
190
+ id: s.id,
191
+ loginTs: s.loginTs ? new Date(s.loginTs * 1000).toISOString() : null,
192
+ browser: s.browser ?? null,
193
+ os: s.os ?? null,
194
+ ip: s.ip ?? null,
195
+ location: s.location ?? null,
196
+ amr: s.amr ?? [],
197
+ })),
198
+ mfa: {
199
+ enabled: mfaEnabled,
200
+ totpEnrolled,
201
+ passkeyCount: passkeys.length,
202
+ passkeysSupported,
203
+ },
204
+ };
205
+ }
206
+ // ─── PATCH /account/api/profile ─────────────────────────────────────────
207
+ /** Atualizar nome + avatar do perfil. */
208
+ async updateProfile(ctx) {
209
+ const service = await ctx.containerResolver.make('authkit.server');
210
+ const cfg = service.config;
211
+ const store = cfg.accountStore;
212
+ const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
213
+ if (!supportsProfile(store)) {
214
+ return ctx.response.status(422).send(apiErr('capability_unsupported', translate(cfg.messages, 'account.profile.not_supported')));
215
+ }
216
+ const { name, avatarUrl } = await ctx.request.validateUsing(updateProfileValidator);
217
+ let resolvedAvatarUrl = avatarUrl ?? null;
218
+ let via = 'url';
219
+ const file = ctx.request.file('avatar', {
220
+ size: `${cfg.uploads.avatars.maxSizeMb}mb`,
221
+ extnames: ['jpg', 'jpeg', 'png', 'webp'],
222
+ });
223
+ if (file) {
224
+ try {
225
+ const uploadedUrl = await storeAvatar(ctx, cfg.uploads, file, userId, {
226
+ extname: translate(cfg.messages, 'account.profile.avatar_invalid_type'),
227
+ size: translate(cfg.messages, 'account.profile.avatar_too_large'),
228
+ });
229
+ if (uploadedUrl) {
230
+ resolvedAvatarUrl = uploadedUrl;
231
+ via = 'upload';
232
+ }
233
+ }
234
+ catch (error) {
235
+ if (error instanceof AvatarUploadError) {
236
+ return ctx.response.badRequest(apiErr('avatar_error', error.message));
237
+ }
238
+ throw error;
239
+ }
240
+ }
241
+ const updated = await store.updateProfile(userId, {
242
+ name: name ?? null,
243
+ avatarUrl: resolvedAvatarUrl,
244
+ });
245
+ await cfg.audit?.record({
246
+ type: 'profile.updated',
247
+ accountId: userId,
248
+ ip: ctx.request.ip?.() ?? null,
249
+ metadata: { via },
250
+ });
251
+ return {
252
+ id: updated?.id ?? userId,
253
+ name: updated?.name ?? null,
254
+ avatarUrl: updated?.avatarUrl ?? null,
255
+ };
256
+ }
257
+ // ─── POST /account/api/password ─────────────────────────────────────────
258
+ /** Trocar senha. Exige sudo + senha atual (mesmo comportamento da tela). */
259
+ async changePassword(ctx) {
260
+ const service = await ctx.containerResolver.make('authkit.server');
261
+ const cfg = service.config;
262
+ const store = cfg.accountStore;
263
+ const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
264
+ if (!supportsAccountSecurity(store)) {
265
+ return ctx.response.status(422).send(apiErr('capability_unsupported', translate(cfg.messages, 'account.security.not_supported')));
266
+ }
267
+ // Sudo gate.
268
+ const sudoSettings = await resolveRuntimeSettings(ctx);
269
+ const sudoResult = await requireSudo(ctx, sudoSettings);
270
+ if (sudoResult !== true) {
271
+ // Para a API JSON, devolvemos 403 em vez de redirecionar.
272
+ return ctx.response.status(403).send(apiErr('sudo_required', 'Identity confirmation required.'));
273
+ }
274
+ const { currentPassword, newPassword } = await ctx.request.validateUsing(changePasswordValidator);
275
+ const account = await store.findById(userId);
276
+ const verified = account ? await store.verifyCredentials(account.email, currentPassword) : null;
277
+ if (!verified) {
278
+ return ctx.response.status(422).send(apiErr('invalid_credentials', translate(cfg.messages, 'errors.invalid_credentials')));
279
+ }
280
+ // Histórico de senhas.
281
+ const { supportsPasswordHistory } = await import('../../accounts/account_store.js');
282
+ if (supportsPasswordHistory(store)) {
283
+ const histSettings = await resolvePasswordHistorySettings(ctx);
284
+ if (histSettings.enabled) {
285
+ const pepperedNew = cfg.accountStore.__passwordManager?.applyCurrentPepper?.(newPassword) ?? newPassword;
286
+ const reused = await store.isPasswordReused(userId, pepperedNew, histSettings.count);
287
+ if (reused) {
288
+ return ctx.response.status(422).send(apiErr('password_reused', translate(cfg.messages, 'password.reused', { count: histSettings.count })));
289
+ }
290
+ const rawRow = await cfg.accountStore.__getRawRow?.(userId);
291
+ if (rawRow?.password) {
292
+ await store.recordPasswordHistory(userId, rawRow.password);
293
+ await store.prunePasswordHistory(userId, histSettings.count);
294
+ }
295
+ }
296
+ }
297
+ try {
298
+ await store.changePassword(userId, newPassword);
299
+ }
300
+ catch (error) {
301
+ if (error instanceof PasswordPolicyError) {
302
+ return ctx.response.status(422).send(apiErr('password_policy', translate(cfg.messages, error.key, error.params)));
303
+ }
304
+ throw error;
305
+ }
306
+ await cfg.audit?.record({
307
+ type: 'password.changed',
308
+ accountId: userId,
309
+ ip: ctx.request.ip?.() ?? null,
310
+ });
311
+ if (account) {
312
+ await dispatchSecurityNotice(ctx, {
313
+ account: { id: userId, email: account.email },
314
+ kind: 'password_changed',
315
+ ip: ctx.request.ip?.() ?? null,
316
+ timestamp: new Date().toISOString(),
317
+ }, cfg.mail, cfg.audit);
318
+ }
319
+ return { ok: true };
320
+ }
321
+ // ─── POST /account/api/email-change ─────────────────────────────────────
322
+ /** Solicitar troca de e-mail. Exige sudo. */
323
+ async requestEmailChange(ctx) {
324
+ const service = await ctx.containerResolver.make('authkit.server');
325
+ const cfg = service.config;
326
+ const store = cfg.accountStore;
327
+ const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
328
+ if (!supportsAccountSecurity(store)) {
329
+ return ctx.response.status(422).send(apiErr('capability_unsupported', translate(cfg.messages, 'account.security.not_supported')));
330
+ }
331
+ const sudoSettings = await resolveRuntimeSettings(ctx);
332
+ const sudoResult = await requireSudo(ctx, sudoSettings);
333
+ if (sudoResult !== true) {
334
+ return ctx.response.status(403).send(apiErr('sudo_required', 'Identity confirmation required.'));
335
+ }
336
+ const emailChangeSettings = await resolveEmailChangeSettings(ctx);
337
+ if (!emailChangeSettings.enabled) {
338
+ return ctx.response.status(422).send(apiErr('disabled', translate(cfg.messages, 'account.security.email_change_disabled')));
339
+ }
340
+ const { currentPassword, newEmail } = await ctx.request.validateUsing(changeEmailValidator);
341
+ const account = await store.findById(userId);
342
+ if (emailChangeSettings.requirePassword) {
343
+ if (!currentPassword) {
344
+ return ctx.response.status(422).send(apiErr('invalid_credentials', translate(cfg.messages, 'errors.invalid_credentials')));
345
+ }
346
+ const verified = account ? await store.verifyCredentials(account.email, currentPassword) : null;
347
+ if (!verified) {
348
+ return ctx.response.status(422).send(apiErr('invalid_credentials', translate(cfg.messages, 'errors.invalid_credentials')));
349
+ }
350
+ }
351
+ const issued = await store.requestEmailChange(userId, newEmail);
352
+ if (!issued) {
353
+ return ctx.response.status(409).send(apiErr('email_taken', translate(cfg.messages, 'errors.email_taken')));
354
+ }
355
+ await cfg.audit?.record({
356
+ type: 'email_change.requested',
357
+ accountId: userId,
358
+ email: newEmail,
359
+ ip: ctx.request.ip?.() ?? null,
360
+ });
361
+ const origin = `${ctx.request.protocol()}://${ctx.request.host()}`;
362
+ const confirmUrl = `${origin}/account/email/confirm?token=${encodeURIComponent(issued.token)}`;
363
+ if (cfg.mail?.onEmailChangeConfirm) {
364
+ await cfg.mail.onEmailChangeConfirm({
365
+ email: newEmail,
366
+ confirmUrl,
367
+ token: issued.token,
368
+ oldEmail: account?.email ?? '',
369
+ });
370
+ }
371
+ else if (cfg.mail?.onEmailVerification) {
372
+ await cfg.mail.onEmailVerification({ email: newEmail, verifyUrl: confirmUrl, token: issued.token });
373
+ }
374
+ else {
375
+ await sendEmailChangeConfirmationEmail(ctx, { email: newEmail, confirmUrl });
376
+ }
377
+ if (account) {
378
+ if (cfg.mail?.onEmailChangeNotice) {
379
+ await cfg.mail.onEmailChangeNotice({ email: account.email, newEmail });
380
+ }
381
+ else {
382
+ await sendEmailChangeNoticeEmail(ctx, { email: account.email, newEmail });
383
+ }
384
+ }
385
+ return { ok: true, email: newEmail };
386
+ }
387
+ // ─── POST /account/api/email-change/cancel ──────────────────────────────
388
+ /** Cancelar troca de e-mail pendente. */
389
+ async cancelEmailChange(ctx) {
390
+ const service = await ctx.containerResolver.make('authkit.server');
391
+ const cfg = service.config;
392
+ const store = cfg.accountStore;
393
+ const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
394
+ if (!supportsAccountSecurity(store)) {
395
+ return ctx.response.status(422).send(apiErr('capability_unsupported', translate(cfg.messages, 'account.security.not_supported')));
396
+ }
397
+ const cancelFn = store.cancelEmailChange;
398
+ if (typeof cancelFn === 'function') {
399
+ await cancelFn.call(store, userId);
400
+ }
401
+ else {
402
+ const account = await store.findById(userId);
403
+ if (account) {
404
+ await store.requestEmailChange(userId, account.email);
405
+ }
406
+ }
407
+ await cfg.audit?.record({
408
+ type: 'email_change.cancelled',
409
+ accountId: userId,
410
+ ip: ctx.request.ip?.() ?? null,
411
+ });
412
+ return { ok: true };
413
+ }
414
+ // ─── GET /account/api/sessions ──────────────────────────────────────────
415
+ /** Sessões ativas do usuário logado. */
416
+ async listSessions(ctx) {
417
+ const service = await ctx.containerResolver.make('authkit.server');
418
+ const cfg = service.config;
419
+ const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
420
+ const adminSessions = new AdminSessionsService(service);
421
+ const supported = adminSessions.canList;
422
+ if (!supported) {
423
+ return { supported: false, sessions: [] };
424
+ }
425
+ const rawSessions = await adminSessions.listSessions(userId);
426
+ const enriched = await enrichSessionsWithContext(cfg, userId, rawSessions);
427
+ return {
428
+ supported: true,
429
+ sessions: enriched.map((s) => ({
430
+ id: s.id,
431
+ loginTs: s.loginTs ? new Date(s.loginTs * 1000).toISOString() : null,
432
+ browser: s.browser ?? null,
433
+ os: s.os ?? null,
434
+ ip: s.ip ?? null,
435
+ location: s.location ?? null,
436
+ amr: s.amr ?? [],
437
+ })),
438
+ };
439
+ }
440
+ // ─── DELETE /account/api/sessions/:id ──────────────────────────────────
441
+ /** Revogar uma sessão específica (verifica que pertence ao usuário logado). */
442
+ async revokeSession(ctx) {
443
+ const service = await ctx.containerResolver.make('authkit.server');
444
+ const cfg = service.config;
445
+ const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
446
+ const sessionId = ctx.request.param('id');
447
+ const adminSessions = new AdminSessionsService(service);
448
+ if (!adminSessions.canList) {
449
+ return ctx.response.status(422).send(apiErr('capability_unsupported', 'Session enumeration not supported by this adapter.'));
450
+ }
451
+ // Verifica que a sessão pertence à conta logada.
452
+ const sessions = await adminSessions.listSessions(userId);
453
+ const target = sessions.find((s) => s.id === sessionId);
454
+ if (!target) {
455
+ return ctx.response.notFound(apiErr('not_found', 'Session not found.'));
456
+ }
457
+ // Revoga via adapter diretamente.
458
+ const AdapterClass = service.config.AdapterClass;
459
+ const sessionAdapter = new AdapterClass('Session');
460
+ await sessionAdapter.destroy(sessionId);
461
+ await cfg.audit?.record({
462
+ type: 'session.revoked',
463
+ accountId: userId,
464
+ ip: ctx.request.ip?.() ?? null,
465
+ metadata: { sessionId, source: 'self' },
466
+ });
467
+ return { ok: true, revoked: sessionId };
468
+ }
469
+ // ─── POST /account/api/sessions/revoke-others ───────────────────────────
470
+ /** Revogar todas as sessões exceto a atual (identificada pelo cookie de sessão). */
471
+ async revokeOtherSessions(ctx) {
472
+ const service = await ctx.containerResolver.make('authkit.server');
473
+ const cfg = service.config;
474
+ const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
475
+ const adminSessions = new AdminSessionsService(service);
476
+ if (!adminSessions.canList) {
477
+ return ctx.response.status(422).send(apiErr('capability_unsupported', 'Session enumeration not supported by this adapter.'));
478
+ }
479
+ // Sem sessão OIDC corrente identificável aqui (o console de conta usa sessão
480
+ // Adonis, não sessão OIDC). Revogamos TODAS as sessões OIDC da conta.
481
+ // Isso é seguro: a sessão Adonis do console não é uma sessão OIDC.
482
+ const result = await adminSessions.revokeAll(userId);
483
+ await cfg.audit?.record({
484
+ type: 'session.revoked_all',
485
+ accountId: userId,
486
+ ip: ctx.request.ip?.() ?? null,
487
+ metadata: { ...result, source: 'self' },
488
+ });
489
+ return { ok: true, ...result };
490
+ }
491
+ // ─── POST /account/api/sessions/revoke-all ──────────────────────────────
492
+ /**
493
+ * Revogar TODAS as sessões OIDC + grants/tokens da conta e encerrar a sessão
494
+ * Adonis do console (logout global). Retorna `{ ok, signedOut: true }` para que
495
+ * a UI redirecione para o login.
496
+ *
497
+ * i18n keys: account.sessions.revoke_all_success (unused server-side — JSON API)
498
+ */
499
+ async revokeAllSessions(ctx) {
500
+ const service = await ctx.containerResolver.make('authkit.server');
501
+ const cfg = service.config;
502
+ const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
503
+ const adminSessions = new AdminSessionsService(service);
504
+ if (!adminSessions.canList) {
505
+ return ctx.response.status(422).send(apiErr('capability_unsupported', 'Session enumeration not supported by this adapter.'));
506
+ }
507
+ // Revogar todas as sessões OIDC + grants da conta.
508
+ const result = await adminSessions.revokeAll(userId);
509
+ await cfg.audit?.record({
510
+ type: 'account.signed_out_all',
511
+ accountId: userId,
512
+ ip: ctx.request.ip?.() ?? null,
513
+ metadata: { ...result, source: 'self' },
514
+ });
515
+ // Encerrar a sessão Adonis do console (logout global do self-service).
516
+ ctx.session.forget(ACCOUNT_SESSION_KEY);
517
+ await ctx.session.regenerate();
518
+ return { ok: true, signedOut: true, ...result };
519
+ }
520
+ // ─── GET /account/api/apps ──────────────────────────────────────────────
521
+ /** Apps com acesso (grants) do usuário logado. */
522
+ async listApps(ctx) {
523
+ const service = await ctx.containerResolver.make('authkit.server');
524
+ const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
525
+ const adminSessions = new AdminSessionsService(service);
526
+ const supported = adminSessions.canList;
527
+ if (!supported) {
528
+ return { supported: false, apps: [] };
529
+ }
530
+ const grants = await adminSessions.listGrants(userId);
531
+ return {
532
+ supported: true,
533
+ apps: grants
534
+ .filter((g) => !!g.clientId)
535
+ .map((g) => ({
536
+ clientId: g.clientId,
537
+ accessTokens: g.accessTokens,
538
+ refreshTokens: g.refreshTokens,
539
+ })),
540
+ };
541
+ }
542
+ // ─── DELETE /account/api/apps/:clientId ─────────────────────────────────
543
+ /** Revogar consentimento de um app (clientId). */
544
+ async revokeApp(ctx) {
545
+ const service = await ctx.containerResolver.make('authkit.server');
546
+ const cfg = service.config;
547
+ const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
548
+ const clientId = ctx.request.param('clientId');
549
+ const adminSessions = new AdminSessionsService(service);
550
+ if (!adminSessions.canList) {
551
+ return ctx.response.status(422).send(apiErr('capability_unsupported', 'Session enumeration not supported by this adapter.'));
552
+ }
553
+ const result = await adminSessions.revokeClientGrants(userId, clientId);
554
+ await cfg.audit?.record({
555
+ type: 'grant.revoked_by_user',
556
+ accountId: userId,
557
+ clientId,
558
+ ip: ctx.request.ip?.() ?? null,
559
+ metadata: { ...result },
560
+ });
561
+ return { ok: true, ...result };
562
+ }
563
+ // ─── GET /account/api/mfa ────────────────────────────────────────────────
564
+ /** Status completo do MFA: TOTP + passkeys + recovery. */
565
+ async mfaStatus(ctx) {
566
+ const service = await ctx.containerResolver.make('authkit.server');
567
+ const cfg = service.config;
568
+ const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
569
+ const mfaState = await cfg.accountStore.getMfaState?.(userId);
570
+ const enabled = mfaState?.enabled ?? false;
571
+ const passkeysSupported = supportsPasskeys(cfg.accountStore);
572
+ let passkeys = [];
573
+ if (passkeysSupported) {
574
+ try {
575
+ passkeys = await cfg.accountStore.listPasskeys(userId);
576
+ }
577
+ catch {
578
+ /* fail-safe */
579
+ }
580
+ }
581
+ return {
582
+ enabled,
583
+ totp: { enrolled: enabled },
584
+ passkeys: {
585
+ supported: passkeysSupported,
586
+ count: passkeys.length,
587
+ items: passkeys.map((p) => ({
588
+ id: p.id,
589
+ label: p.label ?? null,
590
+ createdAt: p.createdAt ?? null,
591
+ })),
592
+ },
593
+ // Recovery codes are shown once via the existing POST /account/mfa/confirm flow.
594
+ recovery: { available: enabled },
595
+ };
596
+ }
597
+ // ─── GET /account/api/passkeys ──────────────────────────────────────────
598
+ /** Lista passkeys do usuário logado. */
599
+ async listPasskeys(ctx) {
600
+ const service = await ctx.containerResolver.make('authkit.server');
601
+ const cfg = service.config;
602
+ const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
603
+ if (!supportsPasskeys(cfg.accountStore)) {
604
+ return { supported: false, passkeys: [] };
605
+ }
606
+ const passkeys = await cfg.accountStore.listPasskeys(userId);
607
+ return {
608
+ supported: true,
609
+ passkeys: passkeys.map((p) => ({
610
+ id: p.id,
611
+ label: p.label ?? null,
612
+ createdAt: p.createdAt ?? null,
613
+ })),
614
+ };
615
+ }
616
+ // ─── DELETE /account/api/passkeys/:id ───────────────────────────────────
617
+ /** Remover uma passkey. Exige sudo. */
618
+ async removePasskey(ctx) {
619
+ const service = await ctx.containerResolver.make('authkit.server');
620
+ const cfg = service.config;
621
+ const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
622
+ if (!supportsPasskeys(cfg.accountStore)) {
623
+ return ctx.response.status(422).send(apiErr('capability_unsupported', translate(cfg.messages, 'errors.passkeys_unavailable')));
624
+ }
625
+ const sudoSettings = await resolveRuntimeSettings(ctx);
626
+ const sudoResult = await requireSudo(ctx, sudoSettings);
627
+ if (sudoResult !== true) {
628
+ return ctx.response.status(403).send(apiErr('sudo_required', 'Identity confirmation required.'));
629
+ }
630
+ const credentialId = ctx.request.param('id');
631
+ await cfg.accountStore.removePasskey?.(userId, credentialId);
632
+ await cfg.audit?.record({
633
+ type: 'passkey.removed',
634
+ accountId: userId,
635
+ ip: ctx.request.ip?.() ?? null,
636
+ metadata: { credentialId },
637
+ });
638
+ const account = await cfg.accountStore.findById(userId);
639
+ if (account) {
640
+ await dispatchSecurityNotice(ctx, {
641
+ account: { id: userId, email: account.email },
642
+ kind: 'passkey_removed',
643
+ ip: ctx.request.ip?.() ?? null,
644
+ timestamp: new Date().toISOString(),
645
+ }, cfg.mail, cfg.audit);
646
+ }
647
+ return { ok: true, removed: credentialId };
648
+ }
649
+ // ─── GET /account/api/tokens ─────────────────────────────────────────────
650
+ /** Lista PATs (Personal Access Tokens) do usuário logado. */
651
+ async listTokens(ctx) {
652
+ const service = await ctx.containerResolver.make('authkit.server');
653
+ const cfg = service.config;
654
+ if (!cfg.patStore) {
655
+ return { supported: false, tokens: [] };
656
+ }
657
+ const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
658
+ const tokens = await cfg.patStore.listForAccount(userId);
659
+ return {
660
+ supported: true,
661
+ tokens: tokens.map((t) => ({
662
+ id: t.id,
663
+ name: t.name,
664
+ scopes: t.scopes,
665
+ audience: t.audience,
666
+ lastUsedAt: t.lastUsedAt,
667
+ createdAt: t.createdAt,
668
+ })),
669
+ };
670
+ }
671
+ // ─── POST /account/api/tokens ────────────────────────────────────────────
672
+ /** Criar um PAT. Exige sudo. O secret é retornado UMA vez. */
673
+ async createToken(ctx) {
674
+ const service = await ctx.containerResolver.make('authkit.server');
675
+ const cfg = service.config;
676
+ if (!cfg.patStore) {
677
+ return ctx.response.status(422).send(apiErr('capability_unsupported', 'PAT store not configured.'));
678
+ }
679
+ const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
680
+ const sudoSettings = await resolveRuntimeSettings(ctx);
681
+ const sudoResult = await requireSudo(ctx, sudoSettings);
682
+ if (sudoResult !== true) {
683
+ return ctx.response.status(403).send(apiErr('sudo_required', 'Identity confirmation required.'));
684
+ }
685
+ const name = ctx.request.input('name', 'Token').trim() || 'Token';
686
+ const { token, pat } = await cfg.patStore.issue({ accountId: userId, name });
687
+ await cfg.audit?.record({
688
+ type: 'pat.issued',
689
+ accountId: userId,
690
+ ip: ctx.request.ip?.() ?? null,
691
+ metadata: { patId: pat.id, name: pat.name },
692
+ });
693
+ ctx.response.status(201);
694
+ return {
695
+ id: pat.id,
696
+ name: pat.name,
697
+ scopes: pat.scopes,
698
+ audience: pat.audience,
699
+ createdAt: pat.createdAt,
700
+ // Secret shown ONCE.
701
+ secret: token,
702
+ };
703
+ }
704
+ // ─── DELETE /account/api/tokens/:id ─────────────────────────────────────
705
+ /** Revogar um PAT. Exige sudo. */
706
+ async revokeToken(ctx) {
707
+ const service = await ctx.containerResolver.make('authkit.server');
708
+ const cfg = service.config;
709
+ if (!cfg.patStore) {
710
+ return ctx.response.status(422).send(apiErr('capability_unsupported', 'PAT store not configured.'));
711
+ }
712
+ const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
713
+ const sudoSettings = await resolveRuntimeSettings(ctx);
714
+ const sudoResult = await requireSudo(ctx, sudoSettings);
715
+ if (sudoResult !== true) {
716
+ return ctx.response.status(403).send(apiErr('sudo_required', 'Identity confirmation required.'));
717
+ }
718
+ const patId = ctx.request.param('id');
719
+ const revoked = await cfg.patStore.revoke(userId, patId);
720
+ if (!revoked) {
721
+ return ctx.response.notFound(apiErr('not_found', 'Token not found.'));
722
+ }
723
+ await cfg.audit?.record({
724
+ type: 'pat.revoked',
725
+ accountId: userId,
726
+ ip: ctx.request.ip?.() ?? null,
727
+ metadata: { patId },
728
+ });
729
+ return { ok: true, revoked: patId };
730
+ }
731
+ // ─── GET /account/api/orgs ───────────────────────────────────────────────
732
+ /** Lista orgs do usuário logado. Alias consolidado dos /account/orgs/json existentes. */
733
+ async listOrgs(ctx) {
734
+ const service = await ctx.containerResolver.make('authkit.server');
735
+ const cfg = service.config;
736
+ const store = cfg.accountStore;
737
+ if (!supportsOrganizations(store)) {
738
+ return { supported: false, orgs: [], activeOrgId: null };
739
+ }
740
+ const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
741
+ const orgs = await store.listOrgsForAccount(userId);
742
+ const activeOrgRaw = ctx.request.cookie(ACTIVE_ORG_COOKIE);
743
+ const activeOrgId = activeOrgRaw ? activeOrgRaw.split('\t')[0] : null;
744
+ return {
745
+ supported: true,
746
+ activeOrgId,
747
+ orgs: orgs.map((org) => ({
748
+ id: org.id,
749
+ name: org.name,
750
+ slug: org.slug,
751
+ logoUrl: org.logoUrl ?? null,
752
+ role: org.role,
753
+ isActive: org.id === activeOrgId,
754
+ })),
755
+ };
756
+ }
757
+ // ─── GET /account/api/orgs/:id ──────────────────────────────────────────
758
+ /** Detalhe de uma org (requer membership). */
759
+ async showOrg(ctx) {
760
+ const service = await ctx.containerResolver.make('authkit.server');
761
+ const cfg = service.config;
762
+ const store = cfg.accountStore;
763
+ if (!supportsOrganizations(store)) {
764
+ return ctx.response.notFound(apiErr('not_supported', 'Organizations not supported.'));
765
+ }
766
+ const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
767
+ const orgId = ctx.request.param('id');
768
+ const membership = await store.getOrgMembership(orgId, userId);
769
+ if (!membership) {
770
+ return ctx.response.notFound(apiErr('not_found', 'Organization not found or not a member.'));
771
+ }
772
+ const org = await store.findOrgById(orgId);
773
+ if (!org) {
774
+ return ctx.response.notFound(apiErr('not_found', 'Organization not found.'));
775
+ }
776
+ const canManage = membership.role === 'owner' || membership.role === 'admin';
777
+ const members = canManage ? await store.listOrgMembers(orgId) : [];
778
+ const enrichedMembers = await Promise.all(members.map(async (m) => {
779
+ const account = await store.findById(m.accountId);
780
+ return {
781
+ accountId: m.accountId,
782
+ email: account?.email ?? null,
783
+ role: m.role,
784
+ joinedAt: m.joinedAt,
785
+ };
786
+ }));
787
+ return {
788
+ id: org.id,
789
+ name: org.name,
790
+ slug: org.slug,
791
+ logoUrl: org.logoUrl ?? null,
792
+ role: membership.role,
793
+ canManage,
794
+ members: enrichedMembers,
795
+ };
796
+ }
797
+ // ─── GET /account/api/orgs/invitations ─────────────────────────────────
798
+ /** Convites pendentes para o e-mail do usuário logado. */
799
+ async listOrgInvitations(ctx) {
800
+ const service = await ctx.containerResolver.make('authkit.server');
801
+ const cfg = service.config;
802
+ const store = cfg.accountStore;
803
+ if (!supportsOrganizations(store)) {
804
+ return { supported: false, invitations: [] };
805
+ }
806
+ const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
807
+ const account = await store.findById(userId);
808
+ if (!account) {
809
+ return ctx.response.unauthorized(apiErr('unauthorized', 'Not authenticated.'));
810
+ }
811
+ const invitations = await store.listPendingInvitationsForEmail(account.email);
812
+ const enriched = await Promise.all(invitations.map(async (inv) => {
813
+ const org = await store.findOrgById(inv.organizationId);
814
+ return {
815
+ id: inv.id,
816
+ organizationId: inv.organizationId,
817
+ orgName: org?.name ?? inv.organizationId,
818
+ orgSlug: org?.slug ?? inv.organizationId,
819
+ email: inv.email,
820
+ role: inv.role,
821
+ expiresAt: inv.expiresAt,
822
+ createdAt: inv.createdAt,
823
+ };
824
+ }));
825
+ return { supported: true, invitations: enriched };
826
+ }
827
+ }