@adonis-agora/authkit-server 0.47.0 → 0.48.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 (188) hide show
  1. package/build/commands/clients_create.js +4 -4
  2. package/build/commands/configure.js +3 -2
  3. package/build/commands/doctor.js +8 -5
  4. package/build/commands/eject.js +1 -1
  5. package/build/commands/expire_scan.js +7 -5
  6. package/build/commands/import_users.js +1 -1
  7. package/build/commands/keys_rotate.js +7 -4
  8. package/build/host/views/account/apps.edge +5 -5
  9. package/build/host/views/account/login.edge +1 -1
  10. package/build/host/views/account/mfa.edge +7 -7
  11. package/build/host/views/account/orgs.edge +5 -5
  12. package/build/host/views/account/security.edge +10 -10
  13. package/build/host/views/account/tokens.edge +5 -5
  14. package/build/index.d.ts +7 -7
  15. package/build/index.js +5 -5
  16. package/build/providers/authkit_server_provider.d.ts +2 -2
  17. package/build/providers/authkit_server_provider.js +20 -11
  18. package/build/services/main.d.ts +1 -1
  19. package/build/services/main.js +2 -2
  20. package/build/src/accounts/lucid_account_store.d.ts +3 -3
  21. package/build/src/accounts/lucid_account_store.js +6 -8
  22. package/build/src/accounts/lucid_store/core.js +1 -1
  23. package/build/src/accounts/lucid_store/mfa.js +1 -1
  24. package/build/src/accounts/lucid_store/organizations.js +2 -4
  25. package/build/src/accounts/lucid_store/password_hygiene.js +1 -4
  26. package/build/src/accounts/lucid_store/shared.d.ts +1 -1
  27. package/build/src/accounts/lucid_stores.d.ts +3 -3
  28. package/build/src/accounts/lucid_stores.js +2 -2
  29. package/build/src/adapters/database_adapter.js +3 -1
  30. package/build/src/adapters/factory.js +1 -1
  31. package/build/src/adapters/redis_adapter.js +2 -1
  32. package/build/src/commands/expire_scan_command.js +19 -5
  33. package/build/src/commands/settings_commands.js +22 -5
  34. package/build/src/define_config.d.ts +20 -20
  35. package/build/src/define_config.js +62 -67
  36. package/build/src/doctor/checks.js +45 -20
  37. package/build/src/events/dispatcher.d.ts +1 -1
  38. package/build/src/events/dispatcher.js +10 -10
  39. package/build/src/host/account_api/account_api_controller.js +95 -39
  40. package/build/src/host/account_deletion_ops.d.ts +4 -4
  41. package/build/src/host/account_deletion_ops.js +5 -5
  42. package/build/src/host/account_deletion_service.d.ts +2 -2
  43. package/build/src/host/account_deletion_service.js +2 -2
  44. package/build/src/host/account_export_service.d.ts +1 -1
  45. package/build/src/host/account_export_service.js +4 -6
  46. package/build/src/host/account_home.d.ts +0 -8
  47. package/build/src/host/account_home.js +6 -4
  48. package/build/src/host/account_login_url.d.ts +6 -3
  49. package/build/src/host/account_login_url.js +16 -8
  50. package/build/src/host/account_paths.d.ts +120 -0
  51. package/build/src/host/account_paths.js +161 -0
  52. package/build/src/host/admin_api/admin_orgs_service.d.ts +2 -2
  53. package/build/src/host/admin_api/admin_orgs_service.js +3 -2
  54. package/build/src/host/admin_api/admin_users_service.d.ts +12 -12
  55. package/build/src/host/admin_api/admin_users_service.js +20 -20
  56. package/build/src/host/admin_api/api_clients_controller.js +2 -2
  57. package/build/src/host/admin_api/api_misc_controller.js +2 -2
  58. package/build/src/host/admin_api/api_orgs_controller.js +3 -3
  59. package/build/src/host/admin_api/api_settings_controller.js +11 -5
  60. package/build/src/host/admin_api/api_users_controller.d.ts +2 -2
  61. package/build/src/host/admin_api/api_users_controller.js +37 -38
  62. package/build/src/host/admin_api/dto.d.ts +4 -4
  63. package/build/src/host/admin_clients_service.js +5 -4
  64. package/build/src/host/admin_console/admin_shell_controller.js +4 -2
  65. package/build/src/host/admin_console/console_audit_controller.js +1 -1
  66. package/build/src/host/admin_console/console_clients_controller.js +5 -3
  67. package/build/src/host/admin_console/console_impersonation_controller.js +1 -1
  68. package/build/src/host/admin_console/console_keys_controller.js +2 -4
  69. package/build/src/host/admin_console/console_orgs_controller.js +3 -3
  70. package/build/src/host/admin_console/console_roles_controller.js +30 -12
  71. package/build/src/host/admin_console/console_sessions_controller.js +3 -3
  72. package/build/src/host/admin_console/console_settings_controller.js +11 -5
  73. package/build/src/host/admin_console/console_users_controller.d.ts +2 -2
  74. package/build/src/host/admin_console/console_users_controller.js +46 -47
  75. package/build/src/host/admin_prefix.js +2 -2
  76. package/build/src/host/admin_sessions_service.js +1 -3
  77. package/build/src/host/adonis_auth_sync.d.ts +1 -1
  78. package/build/src/host/adonis_auth_user_provider.d.ts +1 -1
  79. package/build/src/host/app_key.js +1 -1
  80. package/build/src/host/avatar_storage.js +1 -3
  81. package/build/src/host/config_locks.js +1 -3
  82. package/build/src/host/console_session.js +1 -1
  83. package/build/src/host/controllers/account_apps_controller.js +3 -2
  84. package/build/src/host/controllers/account_confirm_controller.js +2 -6
  85. package/build/src/host/controllers/account_mfa_controller.js +63 -12
  86. package/build/src/host/controllers/account_orgs_controller.js +47 -27
  87. package/build/src/host/controllers/account_security_controller.d.ts +2 -2
  88. package/build/src/host/controllers/account_security_controller.js +114 -115
  89. package/build/src/host/controllers/account_session_controller.js +5 -5
  90. package/build/src/host/controllers/account_tokens_controller.js +3 -2
  91. package/build/src/host/controllers/interaction_controller.js +59 -22
  92. package/build/src/host/controllers/registration_controller.js +28 -13
  93. package/build/src/host/default_mailer.js +11 -7
  94. package/build/src/host/durable/account_deletion_workflow.d.ts +2 -2
  95. package/build/src/host/durable/account_deletion_workflow.js +14 -14
  96. package/build/src/host/durable/account_export_workflow.d.ts +2 -2
  97. package/build/src/host/durable/account_export_workflow.js +12 -12
  98. package/build/src/host/durable/index.d.ts +5 -5
  99. package/build/src/host/durable/index.js +6 -6
  100. package/build/src/host/i18n.d.ts +1376 -1376
  101. package/build/src/host/i18n.js +1380 -1380
  102. package/build/src/host/impersonation.js +1 -7
  103. package/build/src/host/key_rotation_actions.d.ts +1 -1
  104. package/build/src/host/login_attempt.js +35 -6
  105. package/build/src/host/login_notify.js +1 -1
  106. package/build/src/host/otp_lockout.js +1 -3
  107. package/build/src/host/rate_limit.js +1 -1
  108. package/build/src/host/register_auth_host.d.ts +30 -0
  109. package/build/src/host/register_auth_host.js +120 -74
  110. package/build/src/host/renderers/edge_renderer.js +15 -2
  111. package/build/src/host/renderers/inertia_renderer.d.ts +9 -1
  112. package/build/src/host/renderers/inertia_renderer.js +9 -1
  113. package/build/src/host/runtime_settings.js +1 -1
  114. package/build/src/host/runtime_toggles.d.ts +3 -3
  115. package/build/src/host/runtime_toggles.js +69 -24
  116. package/build/src/host/security_notice_service.d.ts +1 -1
  117. package/build/src/host/security_notice_service.js +1 -1
  118. package/build/src/host/session_context.js +1 -1
  119. package/build/src/host/sudo/index.d.ts +3 -3
  120. package/build/src/host/sudo/index.js +3 -3
  121. package/build/src/host/sudo/methods/magic_link.js +6 -5
  122. package/build/src/host/sudo/methods/passkey.js +7 -4
  123. package/build/src/host/sudo/methods/password.js +8 -3
  124. package/build/src/host/sudo/runtime.js +6 -13
  125. package/build/src/host/sudo_mode.js +3 -2
  126. package/build/src/host/ui-dist/assets/index-BQoTDiLV.js +137 -0
  127. package/build/src/host/ui-dist/index.html +1 -1
  128. package/build/src/keys/jwks_manager.js +1 -1
  129. package/build/src/keys/keystore.js +1 -1
  130. package/build/src/keys/keystore_manager.d.ts +2 -2
  131. package/build/src/keys/keystore_manager.js +2 -2
  132. package/build/src/keys/keystore_vault.js +8 -4
  133. package/build/src/mixins/with_audit_log.d.ts +1 -1
  134. package/build/src/mixins/with_auth_user.d.ts +1 -1
  135. package/build/src/mixins/with_auth_user.js +1 -1
  136. package/build/src/mixins/with_credentials.d.ts +1 -1
  137. package/build/src/mixins/with_mfa.d.ts +1 -1
  138. package/build/src/mixins/with_personal_access_token.d.ts +1 -1
  139. package/build/src/mixins/with_provider_identity.d.ts +1 -1
  140. package/build/src/mixins/with_webauthn_credential.d.ts +1 -1
  141. package/build/src/observability/diagnostics_bridge.js +2 -2
  142. package/build/src/observability/metrics_service.js +1 -1
  143. package/build/src/observability/telescope/data_providers.d.ts +1 -1
  144. package/build/src/observability/telescope/data_providers.js +29 -43
  145. package/build/src/observability/telescope/extension.d.ts +1 -1
  146. package/build/src/observability/telescope/extension.js +58 -58
  147. package/build/src/observability/telescope/index.d.ts +2 -2
  148. package/build/src/observability/telescope/index.js +2 -2
  149. package/build/src/observability/wire_provider_events.d.ts +1 -1
  150. package/build/src/observability/wire_provider_events.js +11 -11
  151. package/build/src/password/common_passwords.js +1 -1
  152. package/build/src/password/password_manager.d.ts +1 -1
  153. package/build/src/password/password_manager.js +4 -4
  154. package/build/src/pat/lucid_pat_store.js +2 -6
  155. package/build/src/pat/pat_tokens.js +1 -1
  156. package/build/src/provider/interaction_actions.js +1 -1
  157. package/build/src/provider/key_rotation_scheduler.js +3 -1
  158. package/build/src/provider/logout_sources.js +2 -8
  159. package/build/src/provider/oidc_service.d.ts +2 -2
  160. package/build/src/provider/oidc_service.js +22 -14
  161. package/build/src/provider/single_flight_lock.js +1 -3
  162. package/build/src/provider/token_exchange.d.ts +1 -1
  163. package/build/src/schema/ensure.js +3 -1
  164. package/build/stubs/ui/react/components/auth_shell.tsx +19 -19
  165. package/build/stubs/ui/react/pages/account/login.tsx +6 -7
  166. package/build/stubs/ui/react/pages/account/mfa.tsx +16 -15
  167. package/build/stubs/ui/react/pages/account/tokens.tsx +27 -17
  168. package/build/stubs/ui/react/pages/consent.tsx +12 -12
  169. package/build/stubs/ui/react/pages/forgot.tsx +7 -7
  170. package/build/stubs/ui/react/pages/login.tsx +22 -24
  171. package/build/stubs/ui/react/pages/mfa-challenge.tsx +9 -10
  172. package/build/stubs/ui/react/pages/reset.tsx +10 -10
  173. package/build/stubs/ui/react/pages/signup.tsx +30 -16
  174. package/build/stubs/ui/react/pages/verify-email.tsx +6 -6
  175. package/package.json +1 -1
  176. package/stubs/main.ts +2 -2
  177. package/stubs/ui/react/components/auth_shell.tsx +19 -19
  178. package/stubs/ui/react/pages/account/login.tsx +6 -7
  179. package/stubs/ui/react/pages/account/mfa.tsx +16 -15
  180. package/stubs/ui/react/pages/account/tokens.tsx +27 -17
  181. package/stubs/ui/react/pages/consent.tsx +12 -12
  182. package/stubs/ui/react/pages/forgot.tsx +7 -7
  183. package/stubs/ui/react/pages/login.tsx +22 -24
  184. package/stubs/ui/react/pages/mfa-challenge.tsx +9 -10
  185. package/stubs/ui/react/pages/reset.tsx +10 -10
  186. package/stubs/ui/react/pages/signup.tsx +30 -16
  187. package/stubs/ui/react/pages/verify-email.tsx +6 -6
  188. package/build/src/host/ui-dist/assets/index-BmI_J87g.js +0 -137
@@ -1,22 +1,23 @@
1
- import "../augmentations.js";
2
- import { ACCOUNT_SESSION_KEY } from "../middleware/account_auth.js";
3
- import { getAccountLoginUrl } from "../account_login_url.js";
4
- import { supportsAccountSecurity, supportsAccountDeletion, supportsProfile, supportsPasswordHistory, } from "../../accounts/account_store.js";
5
- import { changePasswordValidator, changeEmailValidator, deleteAccountValidator, updateProfileValidator, } from "../validators.js";
6
- import { sendEmailChangeConfirmationEmail, sendEmailChangeNoticeEmail, sendEmailChangedCompletedEmail, } from "../default_mailer.js";
7
- import { storeAvatar, isAvatarUploadSupported, AvatarUploadError, } from "../avatar_storage.js";
8
- import { translate } from "../i18n.js";
9
- import { TRUSTED_DEVICE_COOKIE } from "../trusted_device.js";
10
- import { AccountDeletionService } from "../account_deletion_service.js";
11
- import { AccountExportService } from "../account_export_service.js";
12
- import { AdminSessionsService } from "../admin_sessions_service.js";
13
- import { enrichSessionsWithContext } from "../session_context.js";
14
- import { PasswordPolicyError } from "../../password/password_manager.js";
15
- import { resolveRuntimeSettings } from "../runtime_settings.js";
16
- import { resolveEffectiveEmailChange, resolveEffectivePasswordHistory, } from "../runtime_toggles.js";
17
- import { dispatchSecurityNotice } from "../security_notice_service.js";
18
- import { requireSudo } from "../sudo_mode.js";
19
- import { syncAdonisAuthLogout } from "../adonis_auth_sync.js";
1
+ import '../augmentations.js';
2
+ import { supportsAccountDeletion, supportsAccountSecurity, supportsPasswordHistory, supportsProfile, } from '../../accounts/account_store.js';
3
+ import { PasswordPolicyError } from '../../password/password_manager.js';
4
+ import { AccountDeletionService } from '../account_deletion_service.js';
5
+ import { AccountExportService } from '../account_export_service.js';
6
+ import { getAccountLoginUrl } from '../account_login_url.js';
7
+ import { accountPath } from '../account_paths.js';
8
+ import { AdminSessionsService } from '../admin_sessions_service.js';
9
+ import { syncAdonisAuthLogout } from '../adonis_auth_sync.js';
10
+ import { AvatarUploadError, isAvatarUploadSupported, storeAvatar } from '../avatar_storage.js';
11
+ import { sendEmailChangeConfirmationEmail, sendEmailChangeNoticeEmail, sendEmailChangedCompletedEmail, } from '../default_mailer.js';
12
+ import { translate } from '../i18n.js';
13
+ import { ACCOUNT_SESSION_KEY } from '../middleware/account_auth.js';
14
+ import { resolveRuntimeSettings } from '../runtime_settings.js';
15
+ import { resolveEffectiveEmailChange, resolveEffectivePasswordHistory, } from '../runtime_toggles.js';
16
+ import { dispatchSecurityNotice } from '../security_notice_service.js';
17
+ import { enrichSessionsWithContext } from '../session_context.js';
18
+ import { requireSudo } from '../sudo_mode.js';
19
+ import { TRUSTED_DEVICE_COOKIE } from '../trusted_device.js';
20
+ import { changeEmailValidator, changePasswordValidator, deleteAccountValidator, updateProfileValidator, } from '../validators.js';
20
21
  /** Resolve os password history settings em runtime (fail-safe). */
21
22
  async function resolvePasswordHistorySettings(ctx) {
22
23
  try {
@@ -52,7 +53,7 @@ async function resolveEmailChangeSettings(ctx) {
52
53
  */
53
54
  export default class AccountSecurityController {
54
55
  async index(ctx) {
55
- const service = await ctx.containerResolver.make("authkit.server");
56
+ const service = await ctx.containerResolver.make('authkit.server');
56
57
  const cfg = service.config;
57
58
  const render = cfg.render;
58
59
  const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
@@ -64,35 +65,35 @@ export default class AccountSecurityController {
64
65
  const ownSessions = sessionsSupported
65
66
  ? await enrichSessionsWithContext(cfg, userId, await adminSessions.listSessions(userId))
66
67
  : [];
67
- return render(ctx, "account/security", {
68
+ return render(ctx, 'account/security', {
68
69
  csrfToken: ctx.request.csrfToken,
69
70
  supported: supportsAccountSecurity(cfg.accountStore),
70
71
  profileSupported: supportsProfile(cfg.accountStore),
71
72
  // Só mostramos o input de arquivo se ALGUM backend (drive OU media) puder armazenar.
72
73
  avatarUploadSupported: await isAvatarUploadSupported(cfg.uploads),
73
- email: account?.email ?? "",
74
- name: account?.name ?? "",
75
- avatarUrl: account?.avatarUrl ?? "",
76
- passwordChanged: ctx.session.flashMessages.get("passwordChanged") ?? null,
77
- emailChangeRequested: ctx.session.flashMessages.get("emailChangeRequested") ?? null,
78
- emailChanged: ctx.session.flashMessages.get("emailChanged") ?? null,
79
- profileUpdated: ctx.session.flashMessages.get("profileUpdated") ?? null,
80
- error: ctx.session.flashMessages.get("securityError") ?? null,
74
+ email: account?.email ?? '',
75
+ name: account?.name ?? '',
76
+ avatarUrl: account?.avatarUrl ?? '',
77
+ passwordChanged: ctx.session.flashMessages.get('passwordChanged') ?? null,
78
+ emailChangeRequested: ctx.session.flashMessages.get('emailChangeRequested') ?? null,
79
+ emailChanged: ctx.session.flashMessages.get('emailChanged') ?? null,
80
+ profileUpdated: ctx.session.flashMessages.get('profileUpdated') ?? null,
81
+ error: ctx.session.flashMessages.get('securityError') ?? null,
81
82
  trustedDevicesEnabled: cfg.trustedDevices.enabled,
82
- trustedDevicesRevoked: ctx.session.flashMessages.get("trustedDevicesRevoked") ?? null,
83
+ trustedDevicesRevoked: ctx.session.flashMessages.get('trustedDevicesRevoked') ?? null,
83
84
  sessionsSupported,
84
85
  sessions: ownSessions.map((s) => ({
85
- loginTs: s.loginTs ? new Date(s.loginTs * 1000).toISOString() : "",
86
- browser: s.browser ?? "",
87
- os: s.os ?? "",
88
- ip: s.ip ?? "",
89
- location: s.location ?? "",
86
+ loginTs: s.loginTs ? new Date(s.loginTs * 1000).toISOString() : '',
87
+ browser: s.browser ?? '',
88
+ os: s.os ?? '',
89
+ ip: s.ip ?? '',
90
+ location: s.location ?? '',
90
91
  })),
91
92
  // Export de dados (portabilidade) sempre disponível para a conta logada.
92
93
  exportSupported: true,
93
94
  // Deleção de conta (LGPD): só quando o store suporta hard delete.
94
95
  deletionSupported: supportsAccountDeletion(cfg.accountStore),
95
- deleteError: ctx.session.flashMessages.get("deleteError") ?? null,
96
+ deleteError: ctx.session.flashMessages.get('deleteError') ?? null,
96
97
  });
97
98
  }
98
99
  /**
@@ -101,20 +102,20 @@ export default class AccountSecurityController {
101
102
  * NUNCA inclui segredos. Audita `account.exported`.
102
103
  */
103
104
  async exportData(ctx) {
104
- const service = await ctx.containerResolver.make("authkit.server");
105
+ const service = await ctx.containerResolver.make('authkit.server');
105
106
  const cfg = service.config;
106
107
  const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
107
108
  if (cfg.accountLifecycle?.durable) {
108
109
  // OPT-IN durável: enfileira o workflow de export (coleta + persiste artefato
109
110
  // + entrega async). Isolado no subpath durável — só carregado neste ramo.
110
- const { resolveWorkflowEngine, enqueueAccountExport } = await import("../durable/index.js");
111
+ const { resolveWorkflowEngine, enqueueAccountExport } = await import('../durable/index.js');
111
112
  const engine = await resolveWorkflowEngine(ctx.containerResolver);
112
113
  await enqueueAccountExport(engine, {
113
114
  accountId: userId,
114
115
  ip: ctx.request.ip?.() ?? null,
115
116
  });
116
- ctx.session.flash("exportRequested", translate(cfg.messages, "account.export.requested"));
117
- return ctx.response.redirect("/account/security");
117
+ ctx.session.flash('exportRequested', translate(cfg.messages, 'account.export.requested'));
118
+ return ctx.response.redirect(accountPath('security'));
118
119
  }
119
120
  // Caminho SÍNCRONO de sempre (byte-idêntico): download inline do JSON.
120
121
  const payload = await new AccountExportService(service).export(userId);
@@ -122,12 +123,12 @@ export default class AccountSecurityController {
122
123
  return ctx.response.notFound();
123
124
  }
124
125
  await cfg.audit?.record({
125
- type: "account.exported",
126
+ type: 'account.exported',
126
127
  accountId: userId,
127
128
  ip: ctx.request.ip?.() ?? null,
128
129
  });
129
- ctx.response.header("content-type", "application/json; charset=utf-8");
130
- ctx.response.header("content-disposition", `attachment; filename="authkit-data-export-${userId}.json"`);
130
+ ctx.response.header('content-type', 'application/json; charset=utf-8');
131
+ ctx.response.header('content-disposition', `attachment; filename="authkit-data-export-${userId}.json"`);
131
132
  return ctx.response.send(JSON.stringify(payload, null, 2));
132
133
  }
133
134
  /**
@@ -137,12 +138,12 @@ export default class AccountSecurityController {
137
138
  * completo e encerra a sessão.
138
139
  */
139
140
  async deleteAccount(ctx) {
140
- const service = await ctx.containerResolver.make("authkit.server");
141
+ const service = await ctx.containerResolver.make('authkit.server');
141
142
  const cfg = service.config;
142
143
  const store = cfg.accountStore;
143
144
  const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
144
145
  if (!supportsAccountDeletion(store)) {
145
- return ctx.response.redirect("/account/security");
146
+ return ctx.response.redirect(accountPath('security'));
146
147
  }
147
148
  // Sudo mode gate (defesa em profundidade — confirmação inline continua abaixo).
148
149
  const sudoSettingsDel = await resolveRuntimeSettings(ctx);
@@ -161,25 +162,24 @@ export default class AccountSecurityController {
161
162
  confirmed = !!(await store.verifyCredentials(account.email, currentPassword));
162
163
  }
163
164
  if (!confirmed && confirmEmail) {
164
- confirmed =
165
- confirmEmail.trim().toLowerCase() === account.email.toLowerCase();
165
+ confirmed = confirmEmail.trim().toLowerCase() === account.email.toLowerCase();
166
166
  }
167
167
  if (!confirmed) {
168
- ctx.session.flash("deleteError", translate(cfg.messages, "account.delete.invalid_confirmation"));
169
- return ctx.response.redirect("/account/security");
168
+ ctx.session.flash('deleteError', translate(cfg.messages, 'account.delete.invalid_confirmation'));
169
+ return ctx.response.redirect(accountPath('security'));
170
170
  }
171
171
  const actor = {
172
172
  actorId: userId,
173
173
  ip: ctx.request.ip?.() ?? null,
174
- source: "self",
174
+ source: 'self',
175
175
  };
176
176
  if (cfg.accountLifecycle?.durable) {
177
177
  // OPT-IN durável (self-service): logout IMEDIATO (revoga as sessões/grants
178
178
  // OIDC do ator de forma síncrona) + enfileira o resto do cascade async.
179
179
  // Isolado no subpath durável — só carregado neste ramo.
180
- const { resolveWorkflowEngine, enqueueAccountDeletion } = await import("../durable/index.js");
180
+ const { resolveWorkflowEngine, enqueueAccountDeletion } = await import('../durable/index.js');
181
181
  try {
182
- const { revokeSessions } = await import("../account_deletion_ops.js");
182
+ const { revokeSessions } = await import('../account_deletion_ops.js');
183
183
  await revokeSessions(service, userId);
184
184
  }
185
185
  catch {
@@ -196,7 +196,7 @@ export default class AccountSecurityController {
196
196
  // Encerra a sessão e leva ao login com a mensagem de sucesso.
197
197
  ctx.session.forget(ACCOUNT_SESSION_KEY);
198
198
  await syncAdonisAuthLogout(ctx, cfg);
199
- ctx.session.flash("accountDeleted", translate(cfg.messages, "account.delete.deleted"));
199
+ ctx.session.flash('accountDeleted', translate(cfg.messages, 'account.delete.deleted'));
200
200
  return ctx.response.redirect(getAccountLoginUrl());
201
201
  }
202
202
  /**
@@ -206,52 +206,52 @@ export default class AccountSecurityController {
206
206
  * server-side; re-enrolar o MFA invalida a confiança em TODOS os dispositivos.
207
207
  */
208
208
  async revokeTrustedDevices(ctx) {
209
- const service = await ctx.containerResolver.make("authkit.server");
209
+ const service = await ctx.containerResolver.make('authkit.server');
210
210
  const cfg = service.config;
211
211
  ctx.response.clearCookie(TRUSTED_DEVICE_COOKIE);
212
212
  const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
213
213
  await cfg.audit?.record({
214
- type: "trusted_device.revoked",
214
+ type: 'trusted_device.revoked',
215
215
  accountId: userId,
216
216
  ip: ctx.request.ip?.() ?? null,
217
217
  });
218
- ctx.session.flash("trustedDevicesRevoked", translate(cfg.messages, "account.security.trusted_devices_revoked"));
219
- return ctx.response.redirect("/account/security");
218
+ ctx.session.flash('trustedDevicesRevoked', translate(cfg.messages, 'account.security.trusted_devices_revoked'));
219
+ return ctx.response.redirect(accountPath('security'));
220
220
  }
221
221
  /** POST /account/security/profile — atualiza nome + avatar do próprio perfil. */
222
222
  async updateProfile(ctx) {
223
- const service = await ctx.containerResolver.make("authkit.server");
223
+ const service = await ctx.containerResolver.make('authkit.server');
224
224
  const cfg = service.config;
225
225
  const store = cfg.accountStore;
226
226
  const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
227
227
  if (!supportsProfile(store)) {
228
- return ctx.response.redirect("/account/security");
228
+ return ctx.response.redirect(accountPath('security'));
229
229
  }
230
230
  const { name, avatarUrl } = await ctx.request.validateUsing(updateProfileValidator);
231
231
  // Upload de avatar via o drive do app (opt-in pela presença do arquivo).
232
232
  // Se um arquivo for enviado e o drive estiver disponível, a URL resultante
233
233
  // tem prioridade sobre o input de URL; senão caímos no avatarUrl (texto).
234
234
  let resolvedAvatarUrl = avatarUrl ?? null;
235
- let via = "url";
236
- const file = ctx.request.file("avatar", {
235
+ let via = 'url';
236
+ const file = ctx.request.file('avatar', {
237
237
  size: `${cfg.uploads.avatars.maxSizeMb}mb`,
238
- extnames: ["jpg", "jpeg", "png", "webp"],
238
+ extnames: ['jpg', 'jpeg', 'png', 'webp'],
239
239
  });
240
240
  if (file) {
241
241
  try {
242
242
  const uploadedUrl = await storeAvatar(ctx, cfg.uploads, file, userId, {
243
- extname: translate(cfg.messages, "account.profile.avatar_invalid_type"),
244
- size: translate(cfg.messages, "account.profile.avatar_too_large"),
243
+ extname: translate(cfg.messages, 'account.profile.avatar_invalid_type'),
244
+ size: translate(cfg.messages, 'account.profile.avatar_too_large'),
245
245
  });
246
246
  if (uploadedUrl) {
247
247
  resolvedAvatarUrl = uploadedUrl;
248
- via = "upload";
248
+ via = 'upload';
249
249
  }
250
250
  }
251
251
  catch (error) {
252
252
  if (error instanceof AvatarUploadError) {
253
- ctx.session.flash("securityError", error.message);
254
- return ctx.response.redirect("/account/security");
253
+ ctx.session.flash('securityError', error.message);
254
+ return ctx.response.redirect(accountPath('security'));
255
255
  }
256
256
  throw error;
257
257
  }
@@ -263,21 +263,21 @@ export default class AccountSecurityController {
263
263
  avatarUrl: resolvedAvatarUrl,
264
264
  });
265
265
  await cfg.audit?.record({
266
- type: "profile.updated",
266
+ type: 'profile.updated',
267
267
  accountId: userId,
268
268
  ip: ctx.request.ip?.() ?? null,
269
269
  metadata: { via },
270
270
  });
271
- ctx.session.flash("profileUpdated", translate(cfg.messages, "account.profile.updated"));
272
- return ctx.response.redirect("/account/security");
271
+ ctx.session.flash('profileUpdated', translate(cfg.messages, 'account.profile.updated'));
272
+ return ctx.response.redirect(accountPath('security'));
273
273
  }
274
274
  async changePassword(ctx) {
275
- const service = await ctx.containerResolver.make("authkit.server");
275
+ const service = await ctx.containerResolver.make('authkit.server');
276
276
  const cfg = service.config;
277
277
  const store = cfg.accountStore;
278
278
  const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
279
279
  if (!supportsAccountSecurity(store)) {
280
- return ctx.response.redirect("/account/security");
280
+ return ctx.response.redirect(accountPath('security'));
281
281
  }
282
282
  // Sudo mode gate (defesa em profundidade — a verificação de senha ATUAL continua abaixo).
283
283
  const sudoSettings = await resolveRuntimeSettings(ctx);
@@ -287,12 +287,10 @@ export default class AccountSecurityController {
287
287
  const { currentPassword, newPassword } = await ctx.request.validateUsing(changePasswordValidator);
288
288
  const account = await store.findById(userId);
289
289
  // Confirma a senha ATUAL pelo e-mail da conta.
290
- const verified = account
291
- ? await store.verifyCredentials(account.email, currentPassword)
292
- : null;
290
+ const verified = account ? await store.verifyCredentials(account.email, currentPassword) : null;
293
291
  if (!verified) {
294
- ctx.session.flash("securityError", translate(cfg.messages, "errors.invalid_credentials"));
295
- return ctx.response.redirect("/account/security");
292
+ ctx.session.flash('securityError', translate(cfg.messages, 'errors.invalid_credentials'));
293
+ return ctx.response.redirect(accountPath('security'));
296
294
  }
297
295
  // Verificação de histórico de senhas (disallow_password_reuse).
298
296
  // Capability-probed: sem `isPasswordReused` ou com setting desligada → no-op.
@@ -301,13 +299,14 @@ export default class AccountSecurityController {
301
299
  if (histSettings.enabled) {
302
300
  // Aplica o pepper à senha candidata antes de comparar com os hashes históricos
303
301
  // (que foram gravados já com pepper). Pepper ausente → identidade.
304
- const pepperedNew = cfg.accountStore.__passwordManager?.applyCurrentPepper?.(newPassword) ?? newPassword;
302
+ const pepperedNew = cfg.accountStore.__passwordManager?.applyCurrentPepper?.(newPassword) ??
303
+ newPassword;
305
304
  const reused = await store.isPasswordReused(userId, pepperedNew, histSettings.count);
306
305
  if (reused) {
307
- ctx.session.flash("securityError", translate(cfg.messages, "password.reused", {
306
+ ctx.session.flash('securityError', translate(cfg.messages, 'password.reused', {
308
307
  count: histSettings.count,
309
308
  }));
310
- return ctx.response.redirect("/account/security");
309
+ return ctx.response.redirect(accountPath('security'));
311
310
  }
312
311
  // Grava o hash ATUAL no histórico antes de trocar.
313
312
  // O hash atual está na linha do DB — buscamos via store.findById + raw model.
@@ -324,13 +323,13 @@ export default class AccountSecurityController {
324
323
  catch (error) {
325
324
  // Política de senha violada → flash com a regra e volta à tela de segurança.
326
325
  if (error instanceof PasswordPolicyError) {
327
- ctx.session.flash("securityError", translate(cfg.messages, error.key, error.params));
328
- return ctx.response.redirect("/account/security");
326
+ ctx.session.flash('securityError', translate(cfg.messages, error.key, error.params));
327
+ return ctx.response.redirect(accountPath('security'));
329
328
  }
330
329
  throw error;
331
330
  }
332
331
  await cfg.audit?.record({
333
- type: "password.changed",
332
+ type: 'password.changed',
334
333
  accountId: userId,
335
334
  ip: ctx.request.ip?.() ?? null,
336
335
  });
@@ -343,7 +342,7 @@ export default class AccountSecurityController {
343
342
  const sessions = new AdminSessionsService(service);
344
343
  const result = await sessions.revokeAll(userId);
345
344
  await cfg.audit?.record({
346
- type: "session.revoked_all",
345
+ type: 'session.revoked_all',
347
346
  accountId: userId,
348
347
  actorId: userId,
349
348
  ip: ctx.request.ip?.() ?? null,
@@ -352,7 +351,7 @@ export default class AccountSecurityController {
352
351
  grants: result.grants,
353
352
  accessTokens: result.accessTokens,
354
353
  refreshTokens: result.refreshTokens,
355
- source: "password-change",
354
+ source: 'password-change',
356
355
  },
357
356
  });
358
357
  }
@@ -363,21 +362,21 @@ export default class AccountSecurityController {
363
362
  if (account) {
364
363
  await dispatchSecurityNotice(ctx, {
365
364
  account: { id: userId, email: account.email },
366
- kind: "password_changed",
365
+ kind: 'password_changed',
367
366
  ip: ctx.request.ip?.() ?? null,
368
367
  timestamp: new Date().toISOString(),
369
368
  }, cfg.mail, cfg.audit);
370
369
  }
371
- ctx.session.flash("passwordChanged", translate(cfg.messages, "account.security.password_changed"));
372
- return ctx.response.redirect("/account/security");
370
+ ctx.session.flash('passwordChanged', translate(cfg.messages, 'account.security.password_changed'));
371
+ return ctx.response.redirect(accountPath('security'));
373
372
  }
374
373
  async changeEmail(ctx) {
375
- const service = await ctx.containerResolver.make("authkit.server");
374
+ const service = await ctx.containerResolver.make('authkit.server');
376
375
  const cfg = service.config;
377
376
  const store = cfg.accountStore;
378
377
  const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
379
378
  if (!supportsAccountSecurity(store)) {
380
- return ctx.response.redirect("/account/security");
379
+ return ctx.response.redirect(accountPath('security'));
381
380
  }
382
381
  // Sudo mode gate.
383
382
  const sudoSettingsEmail = await resolveRuntimeSettings(ctx);
@@ -387,45 +386,45 @@ export default class AccountSecurityController {
387
386
  // Resolve os settings de troca de e-mail em runtime.
388
387
  const emailChangeSettings = await resolveEmailChangeSettings(ctx);
389
388
  if (!emailChangeSettings.enabled) {
390
- ctx.session.flash("securityError", translate(cfg.messages, "account.security.email_change_disabled"));
391
- return ctx.response.redirect("/account/security");
389
+ ctx.session.flash('securityError', translate(cfg.messages, 'account.security.email_change_disabled'));
390
+ return ctx.response.redirect(accountPath('security'));
392
391
  }
393
392
  const { currentPassword, newEmail } = await ctx.request.validateUsing(changeEmailValidator);
394
393
  const account = await store.findById(userId);
395
394
  // requirePassword: se ligado (default true), exige senha atual.
396
395
  if (emailChangeSettings.requirePassword) {
397
396
  if (!currentPassword) {
398
- ctx.session.flash("securityError", translate(cfg.messages, "errors.invalid_credentials"));
399
- return ctx.response.redirect("/account/security");
397
+ ctx.session.flash('securityError', translate(cfg.messages, 'errors.invalid_credentials'));
398
+ return ctx.response.redirect(accountPath('security'));
400
399
  }
401
400
  const verified = account
402
401
  ? await store.verifyCredentials(account.email, currentPassword)
403
402
  : null;
404
403
  if (!verified) {
405
- ctx.session.flash("securityError", translate(cfg.messages, "errors.invalid_credentials"));
406
- return ctx.response.redirect("/account/security");
404
+ ctx.session.flash('securityError', translate(cfg.messages, 'errors.invalid_credentials'));
405
+ return ctx.response.redirect(accountPath('security'));
407
406
  }
408
407
  }
409
408
  const issued = await store.requestEmailChange(userId, newEmail);
410
409
  if (!issued) {
411
- ctx.session.flash("securityError", translate(cfg.messages, "errors.email_taken"));
412
- return ctx.response.redirect("/account/security");
410
+ ctx.session.flash('securityError', translate(cfg.messages, 'errors.email_taken'));
411
+ return ctx.response.redirect(accountPath('security'));
413
412
  }
414
413
  await cfg.audit?.record({
415
- type: "email_change.requested",
414
+ type: 'email_change.requested',
416
415
  accountId: userId,
417
416
  email: newEmail,
418
417
  ip: ctx.request.ip?.() ?? null,
419
418
  });
420
419
  const origin = `${ctx.request.protocol()}://${ctx.request.host()}`;
421
- const confirmUrl = `${origin}/account/email/confirm?token=${encodeURIComponent(issued.token)}`;
420
+ const confirmUrl = `${origin}${accountPath('emailConfirm')}?token=${encodeURIComponent(issued.token)}`;
422
421
  // 1. Link de confirmação → NOVO e-mail (hook onEmailChangeConfirm > onEmailVerification > default).
423
422
  if (cfg.mail?.onEmailChangeConfirm) {
424
423
  await cfg.mail.onEmailChangeConfirm({
425
424
  email: newEmail,
426
425
  confirmUrl,
427
426
  token: issued.token,
428
- oldEmail: account?.email ?? "",
427
+ oldEmail: account?.email ?? '',
429
428
  });
430
429
  }
431
430
  else if (cfg.mail?.onEmailVerification) {
@@ -454,22 +453,22 @@ export default class AccountSecurityController {
454
453
  });
455
454
  }
456
455
  }
457
- ctx.session.flash("emailChangeRequested", translate(cfg.messages, "account.security.email_change_requested", {
456
+ ctx.session.flash('emailChangeRequested', translate(cfg.messages, 'account.security.email_change_requested', {
458
457
  email: newEmail,
459
458
  }));
460
- return ctx.response.redirect("/account/security");
459
+ return ctx.response.redirect(accountPath('security'));
461
460
  }
462
461
  /**
463
462
  * POST /account/security/email/cancel — cancela uma troca de e-mail pendente
464
463
  * (limpa o token pending sem alterar o e-mail da conta).
465
464
  */
466
465
  async cancelEmailChange(ctx) {
467
- const service = await ctx.containerResolver.make("authkit.server");
466
+ const service = await ctx.containerResolver.make('authkit.server');
468
467
  const cfg = service.config;
469
468
  const store = cfg.accountStore;
470
469
  const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
471
470
  if (!supportsAccountSecurity(store)) {
472
- return ctx.response.redirect("/account/security");
471
+ return ctx.response.redirect(accountPath('security'));
473
472
  }
474
473
  // Implementação: issuePendingToken(userId, null) limpa o token; reutilizamos
475
474
  // requestEmailChange com um valor sentinela → não, melhor usar cancelEmailChange
@@ -478,7 +477,7 @@ export default class AccountSecurityController {
478
477
  // A forma mais limpa é verificar se o store tem cancelEmailChange; se não tiver,
479
478
  // chamamos requestEmailChange com o próprio e-mail atual (limpa o pending antigo).
480
479
  const cancelFn = store.cancelEmailChange;
481
- if (typeof cancelFn === "function") {
480
+ if (typeof cancelFn === 'function') {
482
481
  await cancelFn.call(store, userId);
483
482
  }
484
483
  else {
@@ -492,27 +491,27 @@ export default class AccountSecurityController {
492
491
  }
493
492
  }
494
493
  await cfg.audit?.record({
495
- type: "email_change.cancelled",
494
+ type: 'email_change.cancelled',
496
495
  accountId: userId,
497
496
  ip: ctx.request.ip?.() ?? null,
498
497
  });
499
- ctx.session.flash("emailChangeRequested", translate(cfg.messages, "account.security.email_change_cancelled"));
500
- return ctx.response.redirect("/account/security");
498
+ ctx.session.flash('emailChangeRequested', translate(cfg.messages, 'account.security.email_change_cancelled'));
499
+ return ctx.response.redirect(accountPath('security'));
501
500
  }
502
501
  /** GET /account/email/confirm?token=... — consome o token e aplica o novo e-mail. */
503
502
  async confirmEmail(ctx) {
504
- const service = await ctx.containerResolver.make("authkit.server");
503
+ const service = await ctx.containerResolver.make('authkit.server');
505
504
  const cfg = service.config;
506
505
  const store = cfg.accountStore;
507
506
  const render = cfg.render;
508
507
  if (!supportsAccountSecurity(store)) {
509
- return render(ctx, "account/email-confirmed", { ok: false });
508
+ return render(ctx, 'account/email-confirmed', { ok: false });
510
509
  }
511
- const token = ctx.request.qs().token ?? "";
510
+ const token = ctx.request.qs().token ?? '';
512
511
  const result = await store.confirmEmailChange(token);
513
512
  if (result.ok) {
514
513
  await cfg.audit?.record({
515
- type: "email_change.confirmed",
514
+ type: 'email_change.confirmed',
516
515
  accountId: result.account.id,
517
516
  email: result.newEmail,
518
517
  ip: ctx.request.ip?.() ?? null,
@@ -538,12 +537,12 @@ export default class AccountSecurityController {
538
537
  // Notificação de segurança ao NOVO e-mail (email_changed) — best-effort.
539
538
  await dispatchSecurityNotice(ctx, {
540
539
  account: { id: result.account.id, email: result.newEmail },
541
- kind: "email_changed",
540
+ kind: 'email_changed',
542
541
  ip: ctx.request.ip?.() ?? null,
543
542
  timestamp: new Date().toISOString(),
544
543
  metadata: { oldEmail: result.oldEmail, newEmail: result.newEmail },
545
544
  }, cfg.mail, cfg.audit);
546
545
  }
547
- return render(ctx, "account/email-confirmed", { ok: result.ok });
546
+ return render(ctx, 'account/email-confirmed', { ok: result.ok });
548
547
  }
549
548
  }
@@ -1,13 +1,13 @@
1
1
  import '../augmentations.js';
2
- import { ACCOUNT_SESSION_KEY } from '../middleware/account_auth.js';
2
+ import { accountHome } from '../account_home.js';
3
+ import { getAccountLoginUrl } from '../account_login_url.js';
4
+ import { syncAdonisAuthLogin, syncAdonisAuthLogout } from '../adonis_auth_sync.js';
3
5
  import { translate } from '../i18n.js';
4
6
  import { attemptPasswordLogin } from '../login_attempt.js';
5
7
  import { notifyLoginSuccess } from '../login_notify.js';
6
- import { markSudo } from '../sudo_mode.js';
7
- import { accountHome } from '../account_home.js';
8
- import { getAccountLoginUrl } from '../account_login_url.js';
8
+ import { ACCOUNT_SESSION_KEY } from '../middleware/account_auth.js';
9
9
  import { resolveRuntimeSettings } from '../runtime_settings.js';
10
- import { syncAdonisAuthLogin, syncAdonisAuthLogout } from '../adonis_auth_sync.js';
10
+ import { markSudo } from '../sudo_mode.js';
11
11
  /**
12
12
  * Valida um valor de `return_to` recebido da query-string ou de um campo hidden.
13
13
  *
@@ -1,4 +1,5 @@
1
1
  import '../augmentations.js';
2
+ import { accountPath } from '../account_paths.js';
2
3
  import { ACCOUNT_SESSION_KEY } from '../middleware/account_auth.js';
3
4
  import { resolveRuntimeSettings } from '../runtime_settings.js';
4
5
  import { requireSudo } from '../sudo_mode.js';
@@ -49,7 +50,7 @@ export default class AccountTokensController {
49
50
  ip: ctx.request.ip?.() ?? null,
50
51
  metadata: { patId: pat.id, name: pat.name },
51
52
  });
52
- return ctx.response.redirect('/account/tokens');
53
+ return ctx.response.redirect(accountPath('tokens'));
53
54
  }
54
55
  async destroy(ctx) {
55
56
  const service = await ctx.containerResolver.make('authkit.server');
@@ -73,6 +74,6 @@ export default class AccountTokensController {
73
74
  metadata: { patId },
74
75
  });
75
76
  }
76
- return ctx.response.redirect('/account/tokens');
77
+ return ctx.response.redirect(accountPath('tokens'));
77
78
  }
78
79
  }