@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,17 +1,17 @@
1
1
  import '../augmentations.js';
2
+ import { supportsMagicLink, supportsPasskeys } from '../../accounts/account_store.js';
3
+ import { AdminSessionsService } from '../admin_sessions_service.js';
4
+ import { guardBotProtection, resolveEffectiveBotProtection } from '../bot_protection.js';
2
5
  import { brandFor, isFirstParty } from '../branding.js';
6
+ import { sendMagicLinkEmail } from '../default_mailer.js';
7
+ import { sendOtpUnlockEmail } from '../default_mailer.js';
3
8
  import { translate } from '../i18n.js';
4
9
  import { attemptPasswordLogin, isEmailUnverifiedBlock } from '../login_attempt.js';
5
10
  import { notifyLoginSuccess } from '../login_notify.js';
6
- import { guardBotProtection, resolveEffectiveBotProtection } from '../bot_protection.js';
11
+ import { createOtpLockout, generateOtpUnlockToken, rawToDbOtpUnlockToken, resolveEffectiveOtpLockout, } from '../otp_lockout.js';
7
12
  import { RuntimeSettings, resolveRuntimeSettings } from '../runtime_settings.js';
8
- import { resolveEffectiveMaintenanceMode, resolveEffectiveRegistration, resolveEffectiveAuthMethods, resolveEffectiveSessionPolicy, } from '../runtime_toggles.js';
9
- import { supportsPasskeys, supportsMagicLink } from '../../accounts/account_store.js';
10
- import { sendMagicLinkEmail } from '../default_mailer.js';
13
+ import { resolveEffectiveAuthMethods, resolveEffectiveMaintenanceMode, resolveEffectiveRegistration, resolveEffectiveSessionPolicy, } from '../runtime_toggles.js';
11
14
  import { TRUSTED_DEVICE_COOKIE, buildTrustedDevicePayload, isTrustedDeviceValid, } from '../trusted_device.js';
12
- import { AdminSessionsService } from '../admin_sessions_service.js';
13
- import { createOtpLockout, resolveEffectiveOtpLockout, generateOtpUnlockToken, rawToDbOtpUnlockToken, } from '../otp_lockout.js';
14
- import { sendOtpUnlockEmail } from '../default_mailer.js';
15
15
  /**
16
16
  * Best-effort: returns a RuntimeSettings backed by the container DB, or a no-op
17
17
  * fallback. Contrato NON-NULL preservado — é chamado 6× passando direto p/
@@ -21,7 +21,12 @@ import { sendOtpUnlockEmail } from '../default_mailer.js';
21
21
  */
22
22
  async function getRuntimeSettings(ctx) {
23
23
  const rs = await resolveRuntimeSettings(ctx);
24
- return rs ?? new RuntimeSettings({ table: () => { throw new Error('no-op'); } });
24
+ return (rs ??
25
+ new RuntimeSettings({
26
+ table: () => {
27
+ throw new Error('no-op');
28
+ },
29
+ }));
25
30
  }
26
31
  const SESSION_KEY = 'authkit_login_email';
27
32
  /** accountId aguardando o 2º fator depois da senha verificada. */
@@ -78,7 +83,8 @@ export default class AuthInteractionController {
78
83
  });
79
84
  }
80
85
  }
81
- if (details.prompt.name === 'consent' && isFirstParty(cfg.branding, details.params.client_id)) {
86
+ if (details.prompt.name === 'consent' &&
87
+ isFirstParty(cfg.branding, details.params.client_id)) {
82
88
  // Clients first-party: auto-concede o consent (pula a tela de consent).
83
89
  // interactions.consent monta o Grant + interactionFinished e escreve o
84
90
  // redirect de volta para o client — opera via provider.interactionDetails,
@@ -185,9 +191,7 @@ export default class AuthInteractionController {
185
191
  // Bot protection (plugável): valida o token do widget ANTES de tocar nas
186
192
  // credenciais. Fail-safe: erro/timeout no verify do host PERMITE o fluxo.
187
193
  // We use a cfg-compatible object: pass effectiveBotLogin merged into cfg.
188
- const cfgWithEffectiveBot = effectiveBotLogin !== cfg.botProtection
189
- ? { ...cfg, botProtection: effectiveBotLogin }
190
- : cfg;
194
+ const cfgWithEffectiveBot = effectiveBotLogin !== cfg.botProtection ? { ...cfg, botProtection: effectiveBotLogin } : cfg;
191
195
  if (!(await guardBotProtection(ctx, cfgWithEffectiveBot, 'login', { email, clientId }))) {
192
196
  const found = await cfg.accountStore.findByEmail(email);
193
197
  const account = found
@@ -209,7 +213,14 @@ export default class AuthInteractionController {
209
213
  // ligado precisamos exigir o 2º fator e NÃO podemos chamar interactionFinished
210
214
  // ainda. A sequência verificação + lockout + auditoria de falha é centralizada
211
215
  // em attemptPasswordLogin; a renderização (lookup p/ personalização) fica aqui.
212
- const result = await attemptPasswordLogin(cfg, { email, password, ip, clientId, settings: runtimeSettings, logger: ctx.logger });
216
+ const result = await attemptPasswordLogin(cfg, {
217
+ email,
218
+ password,
219
+ ip,
220
+ clientId,
221
+ settings: runtimeSettings,
222
+ logger: ctx.logger,
223
+ });
213
224
  if (!result.ok) {
214
225
  // Senha expirada: redireciona para o step de troca obrigatória.
215
226
  if (result.passwordExpired && result.account) {
@@ -250,7 +261,9 @@ export default class AuthInteractionController {
250
261
  ? translate(cfg.messages, 'errors.account_expired')
251
262
  : translate(cfg.messages, 'errors.invalid_credentials'),
252
263
  brand,
253
- botProtection: effectiveBotLogin?.on.includes('login') ? effectiveBotLogin.widget : undefined,
264
+ botProtection: effectiveBotLogin?.on.includes('login')
265
+ ? effectiveBotLogin.widget
266
+ : undefined,
254
267
  });
255
268
  }
256
269
  const acc = result.account;
@@ -329,7 +342,10 @@ export default class AuthInteractionController {
329
342
  // remember-me: lê o checkbox do form. Checkbox HTML ausente/desmarcado = sem valor.
330
343
  // Quando rememberEnabled=false, ignora o checkbox (sessão sempre transiente).
331
344
  const rememberChecked = ctx.request.input('remember');
332
- const rememberOn = rememberChecked === 'on' || rememberChecked === '1' || rememberChecked === 'true' || rememberChecked === true;
345
+ const rememberOn = rememberChecked === 'on' ||
346
+ rememberChecked === '1' ||
347
+ rememberChecked === 'true' ||
348
+ rememberChecked === true;
333
349
  const remember = sessionPolicy.rememberEnabled ? rememberOn : false;
334
350
  // single-session: lista sessões ANTES do login para saber quais existem.
335
351
  // Após o completeLogin, a sessão NOVA é criada; revogamos as ANTERIORES.
@@ -389,7 +405,7 @@ export default class AuthInteractionController {
389
405
  const otpLockoutCfg = await resolveEffectiveOtpLockout(runtimeForOtp);
390
406
  const otpLockout = createOtpLockout(otpLockoutCfg, ctx.logger);
391
407
  // Verifica se o fator OTP está travado ANTES de tentar verificar.
392
- if (otpLockoutCfg.enabled && await otpLockout.isLocked(accountId)) {
408
+ if (otpLockoutCfg.enabled && (await otpLockout.isLocked(accountId))) {
393
409
  return render(ctx, 'mfa-challenge', {
394
410
  uid: ctx.request.param('uid'),
395
411
  csrfToken: ctx.request.csrfToken,
@@ -642,7 +658,12 @@ export default class AuthInteractionController {
642
658
  }
643
659
  const acc = await cfg.accountStore.consumeMagicLinkToken(token);
644
660
  if (!acc) {
645
- await cfg.audit?.record({ type: 'login.failure', ip, clientId, metadata: { stage: 'magic_link' } });
661
+ await cfg.audit?.record({
662
+ type: 'login.failure',
663
+ ip,
664
+ clientId,
665
+ metadata: { stage: 'magic_link' },
666
+ });
646
667
  return ctx.response.redirect(`/auth/interaction/${uid}`);
647
668
  }
648
669
  // E-mail não verificado (LGPD/compliance): mesmo com o link válido, não
@@ -707,14 +728,20 @@ export default class AuthInteractionController {
707
728
  // Modo discoverable: sem accountId na sessão, gera options sem restrição
708
729
  // de credenciais (allowCredentials vazio = o browser apresenta todas).
709
730
  if (!supportsPasskeys(cfg.accountStore)) {
710
- return ctx.response.badRequest({ message: translate(cfg.messages, 'errors.passkeys_unavailable'), discoverable: false });
731
+ return ctx.response.badRequest({
732
+ message: translate(cfg.messages, 'errors.passkeys_unavailable'),
733
+ discoverable: false,
734
+ });
711
735
  }
712
736
  // Passa '__discoverable__' como sentinel — o store (LucidStore) detecta e
713
737
  // retorna options com allowCredentials:[] se suportar; caso contrário null.
714
738
  const generated = await cfg.accountStore.generatePasskeyAuthenticationOptions?.('__discoverable__');
715
739
  if (!generated) {
716
740
  // Store não suporta discoverable credentials — autofill não disponível.
717
- return ctx.response.badRequest({ message: translate(cfg.messages, 'errors.passkeys_unavailable'), discoverable: false });
741
+ return ctx.response.badRequest({
742
+ message: translate(cfg.messages, 'errors.passkeys_unavailable'),
743
+ discoverable: false,
744
+ });
718
745
  }
719
746
  ctx.session.put(PASSKEY_AUTH_CHALLENGE_KEY, generated.challenge);
720
747
  return { ...generated.options, _discoverable: true };
@@ -782,7 +809,8 @@ export default class AuthInteractionController {
782
809
  parsed = null;
783
810
  }
784
811
  const ok = parsed
785
- ? ((await cfg.accountStore.verifyPasskeyAuthentication?.(accountId, parsed, challenge)) ?? false)
812
+ ? ((await cfg.accountStore.verifyPasskeyAuthentication?.(accountId, parsed, challenge)) ??
813
+ false)
786
814
  : false;
787
815
  ctx.session.forget(PASSKEY_AUTH_CHALLENGE_KEY);
788
816
  if (!ok) {
@@ -975,14 +1003,23 @@ export default class AuthInteractionController {
975
1003
  }
976
1004
  }
977
1005
  if (!row) {
978
- await cfg.audit?.record({ type: 'otp.unlock_failed', ip, metadata: { reason: 'token_not_found' } });
1006
+ await cfg.audit?.record({
1007
+ type: 'otp.unlock_failed',
1008
+ ip,
1009
+ metadata: { reason: 'token_not_found' },
1010
+ });
979
1011
  return render(ctx, 'otp-unlock', { ok: false });
980
1012
  }
981
1013
  // Verifica TTL.
982
1014
  const { DateTime } = await import('luxon');
983
1015
  const expiresAt = row.passwordResetExpiresAt;
984
1016
  if (!expiresAt || expiresAt < DateTime.now()) {
985
- await cfg.audit?.record({ type: 'otp.unlock_failed', accountId: row.id, ip, metadata: { reason: 'token_expired' } });
1017
+ await cfg.audit?.record({
1018
+ type: 'otp.unlock_failed',
1019
+ accountId: row.id,
1020
+ ip,
1021
+ metadata: { reason: 'token_expired' },
1022
+ });
986
1023
  return render(ctx, 'otp-unlock', { ok: false, expired: true });
987
1024
  }
988
1025
  // Token válido: zera o lock OTP + limpa o token do DB.
@@ -1,15 +1,15 @@
1
1
  import '../augmentations.js';
2
- import { brandFor } from '../branding.js';
3
2
  import { randomBytes } from 'node:crypto';
4
- import { signupValidator, passwordlessSignupValidator, forgotPasswordValidator, resetPasswordValidator, } from '../validators.js';
5
- import { sendPasswordResetEmail, sendEmailVerificationEmail, sendMagicLinkEmail, } from '../default_mailer.js';
6
- import { translate } from '../i18n.js';
7
3
  import { PasswordPolicyError } from '../../password/password_manager.js';
4
+ import { AdminSessionsService } from '../admin_sessions_service.js';
8
5
  import { guardBotProtection, resolveEffectiveBotProtection } from '../bot_protection.js';
6
+ import { brandFor } from '../branding.js';
7
+ import { sendEmailVerificationEmail, sendMagicLinkEmail, sendPasswordResetEmail, } from '../default_mailer.js';
8
+ import { translate } from '../i18n.js';
9
9
  import { RuntimeSettings, resolveRuntimeSettings } from '../runtime_settings.js';
10
- import { resolveEffectiveRegistration, resolveEffectiveMaintenanceMode, resolveEffectiveAuthMethods, } from '../runtime_toggles.js';
10
+ import { resolveEffectiveAuthMethods, resolveEffectiveMaintenanceMode, resolveEffectiveRegistration, } from '../runtime_toggles.js';
11
11
  import { dispatchSecurityNotice } from '../security_notice_service.js';
12
- import { AdminSessionsService } from '../admin_sessions_service.js';
12
+ import { forgotPasswordValidator, passwordlessSignupValidator, resetPasswordValidator, signupValidator, } from '../validators.js';
13
13
  /**
14
14
  * Best-effort: returns a RuntimeSettings backed by the container DB, or a no-op
15
15
  * fallback. Contrato NON-NULL preservado (callers passam direto p/ resolvers que
@@ -19,7 +19,12 @@ import { AdminSessionsService } from '../admin_sessions_service.js';
19
19
  */
20
20
  async function getRuntimeSettings(ctx) {
21
21
  const rs = await resolveRuntimeSettings(ctx);
22
- return rs ?? new RuntimeSettings({ table: () => { throw new Error('no-op'); } });
22
+ return (rs ??
23
+ new RuntimeSettings({
24
+ table: () => {
25
+ throw new Error('no-op');
26
+ },
27
+ }));
23
28
  }
24
29
  export default class AuthRegistrationController {
25
30
  /** GET /auth/interaction/:uid/signup — tela de cadastro (dentro do fluxo OIDC). */
@@ -119,7 +124,9 @@ export default class AuthRegistrationController {
119
124
  csrfToken: ctx.request.csrfToken,
120
125
  error: translate(cfg.messages, 'errors.email_taken'),
121
126
  brand,
122
- botProtection: effectiveBotSignup?.on.includes('signup') ? effectiveBotSignup.widget : undefined,
127
+ botProtection: effectiveBotSignup?.on.includes('signup')
128
+ ? effectiveBotSignup.widget
129
+ : undefined,
123
130
  });
124
131
  }
125
132
  let created;
@@ -139,7 +146,9 @@ export default class AuthRegistrationController {
139
146
  csrfToken: ctx.request.csrfToken,
140
147
  error: translate(cfg.messages, error.key, error.params),
141
148
  brand,
142
- botProtection: effectiveBotSignup?.on.includes('signup') ? effectiveBotSignup.widget : undefined,
149
+ botProtection: effectiveBotSignup?.on.includes('signup')
150
+ ? effectiveBotSignup.widget
151
+ : undefined,
143
152
  });
144
153
  }
145
154
  throw error;
@@ -163,7 +172,9 @@ export default class AuthRegistrationController {
163
172
  csrfToken: ctx.request.csrfToken,
164
173
  error: translate(cfg.messages, 'errors.signup_failed'),
165
174
  brand,
166
- botProtection: effectiveBotSignup?.on.includes('signup') ? effectiveBotSignup.widget : undefined,
175
+ botProtection: effectiveBotSignup?.on.includes('signup')
176
+ ? effectiveBotSignup.widget
177
+ : undefined,
167
178
  });
168
179
  }
169
180
  // Verificação de e-mail (best-effort — não bloqueia nem reverte o login).
@@ -257,7 +268,8 @@ export default class AuthRegistrationController {
257
268
  // auth_methods: se forgotPassword efetivo for false → 404 (endpoint desligado).
258
269
  const authMethods = await resolveEffectiveAuthMethods(runtimeSettings, {
259
270
  configuredSocialProviders: cfg.social?.providers ?? [],
260
- magicLinkCapable: cfg.passwordless?.magicLink && typeof cfg.accountStore.issueMagicLinkToken === 'function',
271
+ magicLinkCapable: cfg.passwordless?.magicLink &&
272
+ typeof cfg.accountStore.issueMagicLinkToken === 'function',
261
273
  configOverrides: cfg.authMethods,
262
274
  });
263
275
  if (!authMethods.forgotPassword) {
@@ -286,7 +298,8 @@ export default class AuthRegistrationController {
286
298
  // auth_methods: se forgotPassword efetivo for false → 404 (endpoint desligado).
287
299
  const authMethods = await resolveEffectiveAuthMethods(runtimeSettings, {
288
300
  configuredSocialProviders: cfg.social?.providers ?? [],
289
- magicLinkCapable: cfg.passwordless?.magicLink && typeof cfg.accountStore.issueMagicLinkToken === 'function',
301
+ magicLinkCapable: cfg.passwordless?.magicLink &&
302
+ typeof cfg.accountStore.issueMagicLinkToken === 'function',
290
303
  configOverrides: cfg.authMethods,
291
304
  });
292
305
  if (!authMethods.forgotPassword) {
@@ -377,7 +390,9 @@ export default class AuthRegistrationController {
377
390
  throw error;
378
391
  }
379
392
  if (!ok) {
380
- return ctx.response.badRequest({ error: translate(cfg.messages, 'errors.invalid_or_expired_token') });
393
+ return ctx.response.badRequest({
394
+ error: translate(cfg.messages, 'errors.invalid_or_expired_token'),
395
+ });
381
396
  }
382
397
  await cfg.audit?.record({
383
398
  type: 'password_reset.consumed',
@@ -6,8 +6,9 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
6
6
  }
7
7
  return path;
8
8
  };
9
+ import { accountPath } from './account_paths.js';
9
10
  import { renderTransactionalEmail } from './email_templates.js';
10
- import { resolveMessages, translate, } from './i18n.js';
11
+ import { resolveMessages, translate } from './i18n.js';
11
12
  let mailServicePromise;
12
13
  /**
13
14
  * Importa o service de mail do HOST de forma preguiçosa e fail-safe.
@@ -191,7 +192,7 @@ export async function sendNewLoginEmail(ctx, data) {
191
192
  heading: translate(t, 'mail.new_login.heading'),
192
193
  intro,
193
194
  ctaLabel: translate(t, 'account.security.title'),
194
- ctaUrl: `${origin}/account/security`,
195
+ ctaUrl: `${origin}${accountPath('security')}`,
195
196
  footnote: translate(t, 'mail.new_login.fallback'),
196
197
  });
197
198
  const sent = await sendEmail(ctx, data.email, content);
@@ -212,7 +213,10 @@ export async function sendNewDeviceLoginEmail(ctx, data) {
212
213
  const brand = resolveBrand(ctx);
213
214
  const { messages: t, locale } = resolveMailMessages(ctx);
214
215
  const origin = `${ctx.request.protocol()}://${ctx.request.host()}`;
215
- const lines = [translate(t, 'mail.new_login.intro'), translate(t, 'mail.new_login.when', { date: data.when })];
216
+ const lines = [
217
+ translate(t, 'mail.new_login.intro'),
218
+ translate(t, 'mail.new_login.when', { date: data.when }),
219
+ ];
216
220
  if (data.userAgent)
217
221
  lines.push(translate(t, 'mail.new_login.device', { device: data.userAgent }));
218
222
  if (data.ip)
@@ -225,7 +229,7 @@ export async function sendNewDeviceLoginEmail(ctx, data) {
225
229
  heading: translate(t, 'mail.new_login.heading'),
226
230
  intro: lines.join(' '),
227
231
  ctaLabel: translate(t, 'account.security.title'),
228
- ctaUrl: `${origin}/account/security`,
232
+ ctaUrl: `${origin}${accountPath('security')}`,
229
233
  footnote: translate(t, 'mail.new_login.fallback'),
230
234
  });
231
235
  const sent = await sendEmail(ctx, data.email, content);
@@ -283,7 +287,7 @@ export async function sendEmailChangeNoticeEmail(ctx, data) {
283
287
  heading: translate(t, 'mail.email_change_notice.heading'),
284
288
  intro: translate(t, 'mail.email_change_notice.intro', { newEmail: data.newEmail }),
285
289
  ctaLabel: translate(t, 'mail.email_change_notice.cta'),
286
- ctaUrl: `${origin}/account/security`,
290
+ ctaUrl: `${origin}${accountPath('security')}`,
287
291
  footnote: translate(t, 'mail.email_change_notice.fallback'),
288
292
  });
289
293
  const sent = await sendEmail(ctx, data.email, content);
@@ -316,7 +320,7 @@ export async function sendEmailChangedCompletedEmail(ctx, data) {
316
320
  newEmail: data.newEmail,
317
321
  }),
318
322
  ctaLabel: translate(t, 'mail.email_changed_completed.cta'),
319
- ctaUrl: `${origin}/account/security`,
323
+ ctaUrl: `${origin}${accountPath('security')}`,
320
324
  footnote: translate(t, 'mail.email_changed_completed.fallback'),
321
325
  });
322
326
  const sent = await sendEmail(ctx, data.oldEmail, content);
@@ -354,7 +358,7 @@ export async function sendSecurityNoticeEmail(ctx, data) {
354
358
  heading: translate(t, 'mail.security_notice.heading'),
355
359
  intro: intro.join(' '),
356
360
  ctaLabel: translate(t, 'account.security.title'),
357
- ctaUrl: `${origin}/account/security`,
361
+ ctaUrl: `${origin}${accountPath('security')}`,
358
362
  footnote: translate(t, 'mail.security_notice.fallback'),
359
363
  });
360
364
  const sent = await sendEmail(ctx, data.email, content);
@@ -1,5 +1,5 @@
1
- import type { OidcService } from "../../provider/oidc_service.js";
2
- import type { DeletionActor } from "../account_deletion_service.js";
1
+ import type { OidcService } from '../../provider/oidc_service.js';
2
+ import type { DeletionActor } from '../account_deletion_service.js';
3
3
  /** Nome canônico do workflow durável de deleção de conta. */
4
4
  export declare const ACCOUNT_DELETE_WORKFLOW = "authkit.account.delete";
5
5
  /** Input do workflow `authkit.account.delete`. */
@@ -1,6 +1,6 @@
1
- import { anonymizeAudit, auditDeleted, deleteAccountAvatar, deleteAccountRow, disableMfa, removeFromOrgs, removePasskeys, revokePats, revokeSessions, snapshotAccount, unlinkProviders, } from "../account_deletion_ops.js";
1
+ import { anonymizeAudit, auditDeleted, deleteAccountAvatar, deleteAccountRow, disableMfa, removeFromOrgs, removePasskeys, revokePats, revokeSessions, snapshotAccount, unlinkProviders, } from '../account_deletion_ops.js';
2
2
  /** Nome canônico do workflow durável de deleção de conta. */
3
- export const ACCOUNT_DELETE_WORKFLOW = "authkit.account.delete";
3
+ export const ACCOUNT_DELETE_WORKFLOW = 'authkit.account.delete';
4
4
  /** Estado inicial vazio do {@link DeletionResult} (acumulado etapa a etapa). */
5
5
  function emptyResult() {
6
6
  return {
@@ -42,7 +42,7 @@ export function defineAccountDeletionWorkflow(deps) {
42
42
  // Snapshot da conta ANTES de destruir (e-mail + avatar) — capturado num step
43
43
  // para ser determinístico no replay. Se a conta não existe (ou já foi
44
44
  // deletada num run anterior), encerra como no-op.
45
- const snapshot = await ctx.step("snapshot", async () => {
45
+ const snapshot = await ctx.step('snapshot', async () => {
46
46
  const cfg = (await deps.oidc()).config;
47
47
  return snapshotAccount(cfg, accountId);
48
48
  });
@@ -50,37 +50,37 @@ export function defineAccountDeletionWorkflow(deps) {
50
50
  return emptyResult();
51
51
  const result = emptyResult();
52
52
  // 1) Audit `account.deleted` ANTES de qualquer destruição.
53
- await ctx.step("audit.deleted", async () => {
53
+ await ctx.step('audit.deleted', async () => {
54
54
  const cfg = (await deps.oidc()).config;
55
55
  await auditDeleted(cfg, snapshot, actor);
56
56
  });
57
57
  // 2) Sessões + grants (cascateia os tokens do oidc-provider).
58
- const revoke = await ctx.step("revoke.sessions", async () => revokeSessions(await deps.oidc(), accountId));
58
+ const revoke = await ctx.step('revoke.sessions', async () => revokeSessions(await deps.oidc(), accountId));
59
59
  result.sessions = revoke.sessions;
60
60
  result.grants = revoke.grants;
61
61
  result.accessTokens = revoke.accessTokens;
62
62
  result.refreshTokens = revoke.refreshTokens;
63
63
  // 3) Personal Access Tokens.
64
- result.pats = (await ctx.step("revoke.pats", async () => revokePats((await deps.oidc()).config, accountId))).pats;
64
+ result.pats = (await ctx.step('revoke.pats', async () => revokePats((await deps.oidc()).config, accountId))).pats;
65
65
  // 4) Passkeys / credenciais WebAuthn.
66
- result.passkeys = (await ctx.step("remove.passkeys", async () => removePasskeys((await deps.oidc()).config, accountId))).passkeys;
66
+ result.passkeys = (await ctx.step('remove.passkeys', async () => removePasskeys((await deps.oidc()).config, accountId))).passkeys;
67
67
  // 5) MFA / TOTP.
68
- await ctx.step("disable.mfa", async () => {
68
+ await ctx.step('disable.mfa', async () => {
69
69
  await disableMfa((await deps.oidc()).config, accountId);
70
70
  });
71
71
  // 6) Identidades de provider linkadas.
72
- result.providerIdentities = (await ctx.step("unlink.providers", async () => unlinkProviders((await deps.oidc()).config, accountId))).providerIdentities;
72
+ result.providerIdentities = (await ctx.step('unlink.providers', async () => unlinkProviders((await deps.oidc()).config, accountId))).providerIdentities;
73
73
  // 6b) Organizations.
74
- const orgResult = await ctx.step("remove.orgs", async () => removeFromOrgs((await deps.oidc()).config, accountId));
74
+ const orgResult = await ctx.step('remove.orgs', async () => removeFromOrgs((await deps.oidc()).config, accountId));
75
75
  result.orgMemberships = orgResult.orgMemberships;
76
76
  result.orgInvitations = orgResult.orgInvitations;
77
77
  // 7) Avatar no drive.
78
- result.avatarDeleted = (await ctx.step("delete.avatar", async () => deleteAccountAvatar((await deps.oidc()).config, accountId, snapshot.avatarUrl))).avatarDeleted;
78
+ result.avatarDeleted = (await ctx.step('delete.avatar', async () => deleteAccountAvatar((await deps.oidc()).config, accountId, snapshot.avatarUrl))).avatarDeleted;
79
79
  // 8) Anonimiza o histórico de audit.
80
- result.auditAnonymized = (await ctx.step("anonymize.audit", async () => anonymizeAudit((await deps.oidc()).config, accountId))).auditAnonymized;
80
+ result.auditAnonymized = (await ctx.step('anonymize.audit', async () => anonymizeAudit((await deps.oidc()).config, accountId))).auditAnonymized;
81
81
  // 9) Deleta a linha da conta (ÚLTIMA etapa, forward-only).
82
- result.ok = (await ctx.step("delete.account", async () => deleteAccountRow((await deps.oidc()).config, accountId))).ok;
82
+ result.ok = (await ctx.step('delete.account', async () => deleteAccountRow((await deps.oidc()).config, accountId))).ok;
83
83
  return result;
84
84
  };
85
- return { name: ACCOUNT_DELETE_WORKFLOW, version: "1", body };
85
+ return { name: ACCOUNT_DELETE_WORKFLOW, version: '1', body };
86
86
  }
@@ -1,5 +1,5 @@
1
- import type { OidcService } from "../../provider/oidc_service.js";
2
- import type { DurableStepCtx } from "./account_deletion_workflow.js";
1
+ import type { OidcService } from '../../provider/oidc_service.js';
2
+ import type { DurableStepCtx } from './account_deletion_workflow.js';
3
3
  /** Nome canônico do workflow durável de export de dados de conta. */
4
4
  export declare const ACCOUNT_EXPORT_WORKFLOW = "authkit.account.export";
5
5
  /** Input do workflow `authkit.account.export`. */
@@ -6,14 +6,14 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
6
6
  }
7
7
  return path;
8
8
  };
9
- import { AccountExportService, } from "../account_export_service.js";
9
+ import { AccountExportService } from '../account_export_service.js';
10
10
  /** Nome canônico do workflow durável de export de dados de conta. */
11
- export const ACCOUNT_EXPORT_WORKFLOW = "authkit.account.export";
11
+ export const ACCOUNT_EXPORT_WORKFLOW = 'authkit.account.export';
12
12
  /** Persistência default: grava o JSON no disk do drive do app (best-effort). */
13
- const defaultPersist = async ({ accountId, runId, json, oidc, }) => {
13
+ const defaultPersist = async ({ accountId, runId, json, oidc }) => {
14
14
  // Indireção via variável: `@adonisjs/drive` é peer/opcional — pode não estar
15
15
  // instalado, então o specifier não é resolvido em build-time.
16
- const specifier = "@adonisjs/drive/services/main";
16
+ const specifier = '@adonisjs/drive/services/main';
17
17
  let drive = null;
18
18
  try {
19
19
  const mod = await import(__rewriteRelativeImportExtension(specifier));
@@ -26,13 +26,13 @@ const defaultPersist = async ({ accountId, runId, json, oidc, }) => {
26
26
  return null;
27
27
  const cfg = oidc.config;
28
28
  const exportsCfg = cfg.uploads.exports;
29
- const directory = (exportsCfg?.directory ?? "authkit/exports").replace(/\/+$/, "");
29
+ const directory = (exportsCfg?.directory ?? 'authkit/exports').replace(/\/+$/, '');
30
30
  const diskName = exportsCfg?.disk ?? cfg.uploads.avatars?.disk;
31
31
  const key = `${directory}/authkit-data-export-${accountId}-${runId}.json`;
32
32
  try {
33
33
  const disk = diskName ? drive.use(diskName) : drive.use();
34
34
  await disk.put(key, json, {
35
- contentType: "application/json; charset=utf-8",
35
+ contentType: 'application/json; charset=utf-8',
36
36
  });
37
37
  return key;
38
38
  }
@@ -61,33 +61,33 @@ export function defineAccountExportWorkflow(deps) {
61
61
  const { accountId } = input;
62
62
  const runId = ctx.runId ?? accountId;
63
63
  // 1) Coleta o payload (reusa a coleta inline do AccountExportService).
64
- const payload = await ctx.step("collect", async () => {
64
+ const payload = await ctx.step('collect', async () => {
65
65
  const oidc = await deps.oidc();
66
66
  return new AccountExportService(oidc).collect(accountId);
67
67
  });
68
68
  if (!payload)
69
69
  return { ok: false, artifactKey: null, bytes: 0 };
70
70
  // 2) Audita o export (account.exported).
71
- await ctx.step("audit", async () => {
71
+ await ctx.step('audit', async () => {
72
72
  const cfg = (await deps.oidc()).config;
73
73
  await cfg.audit?.record({
74
- type: "account.exported",
74
+ type: 'account.exported',
75
75
  accountId,
76
76
  ip: input.ip ?? null,
77
77
  });
78
78
  });
79
79
  // 3) Serializa + persiste o artefato.
80
80
  const json = JSON.stringify(payload, null, 2);
81
- const artifactKey = await ctx.step("persist", async () => {
81
+ const artifactKey = await ctx.step('persist', async () => {
82
82
  const oidc = await deps.oidc();
83
83
  return persist({ accountId, runId, json, oidc });
84
84
  });
85
85
  // 4) Entrega ao titular (signal + e-mail, pluggable).
86
- await ctx.step("deliver", async () => {
86
+ await ctx.step('deliver', async () => {
87
87
  const oidc = await deps.oidc();
88
88
  await deliver({ accountId, runId, artifactKey, oidc });
89
89
  });
90
90
  return { ok: true, artifactKey, bytes: json.length };
91
91
  };
92
- return { name: ACCOUNT_EXPORT_WORKFLOW, version: "1", body };
92
+ return { name: ACCOUNT_EXPORT_WORKFLOW, version: '1', body };
93
93
  }
@@ -26,11 +26,11 @@
26
26
  * // ...e rode um worker (engine.runPending / runOne) p/ o grupo do authkit.
27
27
  * ```
28
28
  */
29
- import type { DeletionActor } from "../account_deletion_service.js";
30
- import { type AccountDeleteWorkflowInput } from "./account_deletion_workflow.js";
31
- import { type AccountExportWorkflowInput } from "./account_export_workflow.js";
32
- export { defineAccountDeletionWorkflow, ACCOUNT_DELETE_WORKFLOW, type AccountDeleteWorkflowInput, type AccountDeletionWorkflowDeps, type DurableStepCtx, type WorkflowBody, } from "./account_deletion_workflow.js";
33
- export { defineAccountExportWorkflow, ACCOUNT_EXPORT_WORKFLOW, type AccountExportWorkflowInput, type AccountExportWorkflowResult, type AccountExportWorkflowDeps, type PersistArtifact, type DeliverArtifact, } from "./account_export_workflow.js";
29
+ import type { DeletionActor } from '../account_deletion_service.js';
30
+ import { type AccountDeleteWorkflowInput } from './account_deletion_workflow.js';
31
+ import { type AccountExportWorkflowInput } from './account_export_workflow.js';
32
+ export { defineAccountDeletionWorkflow, ACCOUNT_DELETE_WORKFLOW, type AccountDeleteWorkflowInput, type AccountDeletionWorkflowDeps, type DurableStepCtx, type WorkflowBody, } from './account_deletion_workflow.js';
33
+ export { defineAccountExportWorkflow, ACCOUNT_EXPORT_WORKFLOW, type AccountExportWorkflowInput, type AccountExportWorkflowResult, type AccountExportWorkflowDeps, type PersistArtifact, type DeliverArtifact, } from './account_export_workflow.js';
34
34
  /**
35
35
  * Superfície mínima do `WorkflowEngine` que o enqueue usa (`start` idempotente por
36
36
  * run-id). Tipada estruturalmente p/ NÃO acoplar o build ao peer opcional.
@@ -26,10 +26,10 @@
26
26
  * // ...e rode um worker (engine.runPending / runOne) p/ o grupo do authkit.
27
27
  * ```
28
28
  */
29
- import { ACCOUNT_DELETE_WORKFLOW, } from "./account_deletion_workflow.js";
30
- import { ACCOUNT_EXPORT_WORKFLOW, } from "./account_export_workflow.js";
31
- export { defineAccountDeletionWorkflow, ACCOUNT_DELETE_WORKFLOW, } from "./account_deletion_workflow.js";
32
- export { defineAccountExportWorkflow, ACCOUNT_EXPORT_WORKFLOW, } from "./account_export_workflow.js";
29
+ import { ACCOUNT_DELETE_WORKFLOW, } from './account_deletion_workflow.js';
30
+ import { ACCOUNT_EXPORT_WORKFLOW, } from './account_export_workflow.js';
31
+ export { defineAccountDeletionWorkflow, ACCOUNT_DELETE_WORKFLOW, } from './account_deletion_workflow.js';
32
+ export { defineAccountExportWorkflow, ACCOUNT_EXPORT_WORKFLOW, } from './account_export_workflow.js';
33
33
  /**
34
34
  * Resolve o `WorkflowEngine` do container — SÓ no caminho durável. Importa
35
35
  * `@adonis-agora/durable` dinamicamente (peer opcional), então o barrel principal
@@ -38,11 +38,11 @@ export { defineAccountExportWorkflow, ACCOUNT_EXPORT_WORKFLOW, } from "./account
38
38
  export async function resolveWorkflowEngine(resolver) {
39
39
  let mod;
40
40
  try {
41
- mod = (await import("@adonis-agora/durable"));
41
+ mod = (await import('@adonis-agora/durable'));
42
42
  }
43
43
  catch {
44
44
  throw new Error('[authkit] durable account-lifecycle is enabled but "@adonis-agora/durable" is not installed. ' +
45
- "Add it as a dependency and register the workflows on your WorkflowEngine.");
45
+ 'Add it as a dependency and register the workflows on your WorkflowEngine.');
46
46
  }
47
47
  return resolver.make(mod.WorkflowEngine);
48
48
  }