@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
@@ -13,13 +13,7 @@ export function buildImpersonationPanel(cfg, targetId) {
13
13
  const auth = client.clientSecret
14
14
  ? ` -u '${client.clientId}:${client.clientSecret}' \\\n`
15
15
  : ` -d 'client_id=${client.clientId}' \\\n`;
16
- const curl = `curl -X POST '${tokenEndpoint}' \\\n` +
17
- auth +
18
- ` -d 'grant_type=${TOKEN_EXCHANGE}' \\\n` +
19
- ` -d 'subject_token=<ADMIN_ACCESS_TOKEN>' \\\n` +
20
- ` -d 'subject_token_type=${ACCESS_TOKEN_TYPE}' \\\n` +
21
- ` -d 'requested_subject=${targetId}' \\\n` +
22
- ` -d 'scope=openid profile email'`;
16
+ const curl = `curl -X POST '${tokenEndpoint}' \\\n${auth} -d 'grant_type=${TOKEN_EXCHANGE}' \\\n -d 'subject_token=<ADMIN_ACCESS_TOKEN>' \\\n -d 'subject_token_type=${ACCESS_TOKEN_TYPE}' \\\n -d 'requested_subject=${targetId}' \\\n -d 'scope=openid profile email'`;
23
17
  return {
24
18
  tokenEndpoint,
25
19
  grantType: TOKEN_EXCHANGE,
@@ -1,6 +1,6 @@
1
+ import type { ManagedKeyInfo } from '../keys/keystore.js';
1
2
  import { type ResolvedKeyRotationSetting } from './key_rotation.js';
2
3
  import type { SettingsCapability } from './runtime_settings.js';
3
- import type { ManagedKeyInfo } from '../keys/keystore.js';
4
4
  /**
5
5
  * Ações compartilhadas de rotação de chave. Funções puras-ish que tanto a Admin
6
6
  * REST API (Bearer) quanto o console (sessão) chamam, evitando duplicar a lógica
@@ -1,6 +1,6 @@
1
1
  import { supportsAccountStatus, supportsEmailVerificationStatus, supportsPasswordExpiration, } from '../accounts/account_store.js';
2
2
  import { createAccountLockout } from './account_lockout.js';
3
- import { resolveEffectiveRequireVerifiedEmail, resolveEffectiveRequireVerifiedEmailFull, resolveEffectivePasswordExpiration, resolveEffectiveAccountExpiration, resolveEffectiveLockout, } from './runtime_toggles.js';
3
+ import { resolveEffectiveAccountExpiration, resolveEffectiveLockout, resolveEffectivePasswordExpiration, resolveEffectiveRequireVerifiedEmail, resolveEffectiveRequireVerifiedEmailFull, } from './runtime_toggles.js';
4
4
  /**
5
5
  * `requireVerifiedEmail` efetivo (config ou runtime setting) E o store sabe dizer
6
6
  * se o e-mail está verificado E a conta NÃO está verificada → bloqueia.
@@ -97,7 +97,12 @@ export async function isAccountExpired(audit, accountId, settings, logger) {
97
97
  return false;
98
98
  try {
99
99
  // Busca o último login.success da conta (ordem desc, limit 1).
100
- const result = await audit.list({ type: 'login.success', subject: accountId, page: 1, limit: 1 });
100
+ const result = await audit.list({
101
+ type: 'login.success',
102
+ subject: accountId,
103
+ page: 1,
104
+ limit: 1,
105
+ });
101
106
  if (result.data.length === 0) {
102
107
  // Nunca logou → considera ativa (conta nova, não inativa).
103
108
  return false;
@@ -163,7 +168,13 @@ export async function attemptPasswordLogin(cfg, input) {
163
168
  // de adivinhar senha).
164
169
  if (supportsAccountStatus(cfg.accountStore) && (await cfg.accountStore.isDisabled(account.id))) {
165
170
  await cfg.audit?.record(input.clientId !== undefined
166
- ? { type: 'login.failure', email, ip, clientId: input.clientId, metadata: { reason: 'disabled' } }
171
+ ? {
172
+ type: 'login.failure',
173
+ email,
174
+ ip,
175
+ clientId: input.clientId,
176
+ metadata: { reason: 'disabled' },
177
+ }
167
178
  : { type: 'login.failure', email, ip, metadata: { reason: 'disabled' } });
168
179
  return { ok: false, locked: false, disabled: true };
169
180
  }
@@ -173,7 +184,13 @@ export async function attemptPasswordLogin(cfg, input) {
173
184
  if (await isEmailUnverifiedBlock(cfg, account.id, input.settings)) {
174
185
  await lockout.clearFailures(email);
175
186
  await cfg.audit?.record(input.clientId !== undefined
176
- ? { type: 'login.failure', email, ip, clientId: input.clientId, metadata: { reason: 'unverified' } }
187
+ ? {
188
+ type: 'login.failure',
189
+ email,
190
+ ip,
191
+ clientId: input.clientId,
192
+ metadata: { reason: 'unverified' },
193
+ }
177
194
  : { type: 'login.failure', email, ip, metadata: { reason: 'unverified' } });
178
195
  return { ok: false, locked: false, unverified: true };
179
196
  }
@@ -186,7 +203,13 @@ export async function attemptPasswordLogin(cfg, input) {
186
203
  // Não é uma falha de credenciais — limpa o contador.
187
204
  await lockout.clearFailures(email);
188
205
  await cfg.audit?.record(input.clientId !== undefined
189
- ? { type: 'password.expired_change_forced', accountId: account.id, email, ip, clientId: input.clientId }
206
+ ? {
207
+ type: 'password.expired_change_forced',
208
+ accountId: account.id,
209
+ email,
210
+ ip,
211
+ clientId: input.clientId,
212
+ }
190
213
  : { type: 'password.expired_change_forced', accountId: account.id, email, ip });
191
214
  return { ok: false, locked: false, passwordExpired: true, account: account };
192
215
  }
@@ -200,7 +223,13 @@ export async function attemptPasswordLogin(cfg, input) {
200
223
  if (expired) {
201
224
  await lockout.clearFailures(email);
202
225
  await cfg.audit?.record(input.clientId !== undefined
203
- ? { type: 'account.expired_login_blocked', accountId: account.id, email, ip, clientId: input.clientId }
226
+ ? {
227
+ type: 'account.expired_login_blocked',
228
+ accountId: account.id,
229
+ email,
230
+ ip,
231
+ clientId: input.clientId,
232
+ }
204
233
  : { type: 'account.expired_login_blocked', accountId: account.id, email, ip });
205
234
  return { ok: false, locked: false, accountExpired: true };
206
235
  }
@@ -1,4 +1,4 @@
1
- import { sendNewLoginEmail, sendNewDeviceLoginEmail } from './default_mailer.js';
1
+ import { sendNewDeviceLoginEmail, sendNewLoginEmail } from './default_mailer.js';
2
2
  import { TRUSTED_DEVICE_COOKIE, isTrustedDeviceValid } from './trusted_device.js';
3
3
  /**
4
4
  * Centraliza o pós-login bem-sucedido: registra o evento `login.success` e dispara
@@ -83,9 +83,7 @@ let limiterPromise;
83
83
  async function loadLimiter() {
84
84
  if (!limiterPromise) {
85
85
  const spec = '@adonisjs/limiter/services/main';
86
- limiterPromise = import(__rewriteRelativeImportExtension(spec))
87
- .then((m) => m.default ?? null)
88
- .catch(() => null);
86
+ limiterPromise = import(__rewriteRelativeImportExtension(spec)).then((m) => m.default ?? null).catch(() => null);
89
87
  }
90
88
  return limiterPromise;
91
89
  }
@@ -78,7 +78,7 @@ export function createAuthThrottles(config) {
78
78
  // Introspecção de PAT: keyed pelo bearer secret quando presente, senão por IP.
79
79
  introspection: buildThrottle('authkit_pat_introspection', config.introspection, config.store, (ctx) => {
80
80
  const auth = ctx.request.header('authorization');
81
- if (auth && auth.toLowerCase().startsWith('bearer ')) {
81
+ if (auth?.toLowerCase().startsWith('bearer ')) {
82
82
  return `bearer:${auth.slice(7).trim()}`;
83
83
  }
84
84
  return undefined;
@@ -1,5 +1,6 @@
1
1
  import type { Router } from '@adonisjs/core/http';
2
2
  import type { AuthSocialConfig, RateLimitConfigInput } from '../define_config.js';
3
+ import { type AccountPathsOptions } from './account_paths.js';
3
4
  import type { SudoMethod } from './sudo/types.js';
4
5
  /** Chave da sessão Adonis que registra o timestamp da última atividade (idle timeout). */
5
6
  export declare const ACCOUNT_LAST_SEEN_KEY = "authkit_last_seen";
@@ -154,6 +155,35 @@ export interface AuthHostOptions {
154
155
  * controllers de conta e a view `otp-unlock`. Ver `account_login_url.ts`.
155
156
  */
156
157
  accountLoginUrl?: string;
158
+ /**
159
+ * Prefixo e segmentos de tela CONFIGURÁVEIS/LOCALIZÁVEIS do console de conta
160
+ * (`/account/*`). Top-level (e não dentro de `account`) DE PROPÓSITO: mesmo
161
+ * com `account: false`, a lib continua montando as rotas de sudo
162
+ * (`/account/confirm*`) e a JSON API (`/account/api/*`) — o prefixo precisa
163
+ * valer para essa infraestrutura também, então ele não pode viver debaixo da
164
+ * flag que desmonta as telas navegáveis.
165
+ *
166
+ * - `prefix` troca o `/account` base (ex.: `'/conta'`).
167
+ * - `paths` traduz cada TELA navegável (ex.: `{ security: 'seguranca',
168
+ * confirm: 'confirmar' }`).
169
+ *
170
+ * Só o prefixo e o segmento de tela são renomeáveis — os action-subpaths dos
171
+ * POSTs internos (`/password`, `/enroll`, `/passkeys/verify`, ...) e o
172
+ * segmento `api` da JSON API são FIXOS (endpoints de máquina, invisíveis ao
173
+ * usuário). Ver `account_paths.ts`.
174
+ *
175
+ * Ausente → tudo idêntico a hoje (`/account/...` — back-compat total).
176
+ *
177
+ * @example
178
+ * // Console em português: /conta/seguranca, /conta/confirmar, ...
179
+ * registerAuthHost(router, {
180
+ * accountRoutes: {
181
+ * prefix: '/conta',
182
+ * paths: { security: 'seguranca', confirm: 'confirmar', mfa: 'mfa' },
183
+ * },
184
+ * })
185
+ */
186
+ accountRoutes?: AccountPathsOptions;
157
187
  }
158
188
  /**
159
189
  * Flags de montagem por tela do console de conta. Cada campo ausente é `true`
@@ -1,17 +1,18 @@
1
1
  import { resolveRateLimit } from '../define_config.js';
2
- import { createAuthThrottles } from './rate_limit.js';
3
- import { ACCOUNT_SESSION_KEY } from './middleware/account_auth.js';
4
2
  import { accountHome } from './account_home.js';
3
+ import { getAccountLoginUrl, setAccountLoginUrl } from './account_login_url.js';
4
+ import { accountPath, joinAccountPath, setAccountPaths, } from './account_paths.js';
5
5
  import { resolveAccountRoles } from './account_roles.js';
6
6
  import { adminApiGuard } from './admin_api/admin_api_guard.js';
7
- import { setAdminPrefix, normalizeAdminPrefix, setAdminApiPrefix, normalizeAdminApiPrefix, } from './admin_prefix.js';
7
+ import { normalizeAdminApiPrefix, normalizeAdminPrefix, setAdminApiPrefix, setAdminPrefix, } from './admin_prefix.js';
8
+ import { getAuthHostConfig } from './auth_host_config.js';
9
+ import { ACCOUNT_SESSION_KEY } from './middleware/account_auth.js';
10
+ import { createAuthThrottles } from './rate_limit.js';
8
11
  import { resolveRuntimeSettings } from './runtime_settings.js';
9
12
  import { resolveEffectiveSessionPolicy } from './runtime_toggles.js';
10
- import { getAuthHostConfig } from './auth_host_config.js';
11
- import { setAccountLoginUrl, getAccountLoginUrl } from './account_login_url.js';
12
- import { completeSudo, fail, guardSudoRoutes, sudoContextFrom, setMountedSudoMethods, } from './sudo/runtime.js';
13
- import { password as sudoPassword } from './sudo/methods/password.js';
14
13
  import { passkey as sudoPasskey } from './sudo/methods/passkey.js';
14
+ import { password as sudoPassword } from './sudo/methods/password.js';
15
+ import { completeSudo, fail, guardSudoRoutes, setMountedSudoMethods, sudoContextFrom, } from './sudo/runtime.js';
15
16
  /**
16
17
  * Métodos de sudo montados quando o host não passa `sudoMethods` —
17
18
  * comportamento histórico (senha + passkey).
@@ -201,10 +202,19 @@ export function registerAuthHost(router, opts = {}) {
201
202
  const social = opts.social ?? hostCfg?.social;
202
203
  const adminOpt = opts.admin ?? (hostCfg?.adminEnabled ? true : undefined);
203
204
  const adminApiOpt = opts.adminApi ?? (hostCfg?.adminApiEnabled ? true : undefined);
205
+ // Prefixo/segmentos configuráveis do console de conta — persiste no singleton
206
+ // de processo ANTES de qualquer construção de rota/closure, para que o registro
207
+ // de rotas, os guards, os controllers, os e-mails e as views leiam o mesmo
208
+ // valor. Top-level de propósito (vale mesmo com `account: false`): as rotas de
209
+ // sudo e a JSON API respeitam o prefixo. Ausente → default `/account/*`
210
+ // (back-compat). Ver `account_paths.ts`.
211
+ if (opts.accountRoutes !== undefined) {
212
+ setAccountPaths(opts.accountRoutes);
213
+ }
204
214
  // Destino do redirect de "faça login" — persiste no singleton de processo para
205
215
  // que os guards (closures de tempo de registro), o middleware, os controllers e
206
- // as views leiam o mesmo valor. Só quando a opção foi passada (senão fica o
207
- // default `/account/login` do singleton — back-compat).
216
+ // as views leiam o mesmo valor. Só quando a opção foi passada (senão deriva de
217
+ // `accountPath('login')` — back-compat).
208
218
  if (opts.accountLoginUrl !== undefined) {
209
219
  setAccountLoginUrl(opts.accountLoginUrl);
210
220
  }
@@ -266,7 +276,10 @@ export function registerAuthHost(router, opts = {}) {
266
276
  withLogin(router.post('/auth/interaction/:uid/login', [C.interaction, 'login']));
267
277
  withLogin(router.post('/auth/interaction/:uid/mfa', [C.interaction, 'mfaVerify']));
268
278
  // Troca de senha obrigatória quando a senha expirou (password expiration gate).
269
- withLogin(router.post('/auth/interaction/:uid/password-expired', [C.interaction, 'changeExpiredPassword']));
279
+ withLogin(router.post('/auth/interaction/:uid/password-expired', [
280
+ C.interaction,
281
+ 'changeExpiredPassword',
282
+ ]));
270
283
  // Passkey como 2º fator alternativo no login (begin/finish; challenge na sessão).
271
284
  router.post('/auth/interaction/:uid/passkey/options', [C.interaction, 'passkeyOptions']);
272
285
  withLogin(router.post('/auth/interaction/:uid/passkey/verify', [C.interaction, 'passkeyVerify']));
@@ -293,74 +306,90 @@ export function registerAuthHost(router, opts = {}) {
293
306
  }
294
307
  // PAT introspection (server-to-server).
295
308
  withIntrospection(router.post('/authkit/pat/introspect', [C.patIntrospection, 'handle']));
309
+ // Paths do console de conta (configuráveis/localizáveis via `accountRoutes`).
310
+ // As TELAS vêm de `accountPath(key)` (prefixo + segmento configurável); os
311
+ // action-subpaths concatenados (`/password`, `/enroll`, ...) são FIXOS —
312
+ // endpoints de formulário invisíveis ao usuário. A JSON API segue o prefixo
313
+ // com o segmento `api` FIXO. Capturados uma vez aqui: `setAccountPaths` já
314
+ // rodou no topo e o singleton não muda mais neste processo.
315
+ const loginPath = accountPath('login');
316
+ const logoutPath = accountPath('logout');
317
+ const securityPath = accountPath('security');
318
+ const tokensPath = accountPath('tokens');
319
+ const appsPath = accountPath('apps');
320
+ const mfaPath = accountPath('mfa');
321
+ const confirmPath = accountPath('confirm');
322
+ const orgsPath = accountPath('orgs');
323
+ const emailConfirmPath = accountPath('emailConfirm');
324
+ const apiBase = joinAccountPath('api');
296
325
  // Organizations — invitation accept (sem guard: controller lida com não-autenticado).
297
326
  // Parte da tela `orgs`: desmontada junto (sem multi-tenancy, não há convite a aceitar).
298
327
  if (mountOrgs) {
299
- router.get('/account/orgs/invitations/:token/accept', [C.accountOrgs, 'showAcceptInvitation']);
300
- router.post('/account/orgs/invitations/:token/accept', [C.accountOrgs, 'acceptInvitation']);
328
+ router.get(`${orgsPath}/invitations/:token/accept`, [C.accountOrgs, 'showAcceptInvitation']);
329
+ router.post(`${orgsPath}/invitations/:token/accept`, [C.accountOrgs, 'acceptInvitation']);
301
330
  }
302
331
  // Console de conta (login de sessão do IdP + gerência de PAT).
303
332
  // Tela `login` desmontável: hosts passwordless delegam ao OIDC próprio e
304
333
  // apontam `accountLoginUrl` para a rota de login deles.
305
334
  if (mountLogin) {
306
- router.get('/account/login', [C.accountSession, 'show']);
335
+ router.get(loginPath, [C.accountSession, 'show']);
307
336
  // L6: throttle por IP no login/logout do console de conta (anti-brute-force),
308
337
  // alinhado com as demais rotas de credencial (interaction, forgot, reset).
309
- withLogin(router.post('/account/login', [C.accountSession, 'login']));
310
- withLogin(router.post('/account/logout', [C.accountSession, 'logout']));
338
+ withLogin(router.post(loginPath, [C.accountSession, 'login']));
339
+ withLogin(router.post(logoutPath, [C.accountSession, 'logout']));
311
340
  }
312
341
  // Confirmação de troca de e-mail (standalone, GET-only — consome o token do link;
313
342
  // pode ser aberta em outro dispositivo, então NÃO exige sessão). Parte da tela
314
343
  // `security` (o terminal do fluxo de troca de e-mail).
315
344
  if (mountSecurity) {
316
- router.get('/account/email/confirm', [C.accountSecurity, 'confirmEmail']);
345
+ router.get(emailConfirmPath, [C.accountSecurity, 'confirmEmail']);
317
346
  }
318
- // Rotas de tokens protegidas por AccountAuthMiddleware (redireciona para /account/login se não autenticado).
347
+ // Rotas de tokens protegidas por AccountAuthMiddleware (redireciona para a tela de login se não autenticado).
319
348
  router
320
349
  .group(() => {
321
350
  // Personal Access Tokens (tela `tokens`).
322
351
  if (mountTokens) {
323
- router.get('/account/tokens', [C.accountTokens, 'index']);
324
- router.post('/account/tokens', [C.accountTokens, 'store']);
325
- router.post('/account/tokens/:id/revoke', [C.accountTokens, 'destroy']);
352
+ router.get(tokensPath, [C.accountTokens, 'index']);
353
+ router.post(tokensPath, [C.accountTokens, 'store']);
354
+ router.post(`${tokensPath}/:id/revoke`, [C.accountTokens, 'destroy']);
326
355
  }
327
356
  // Segurança da conta: trocar senha + solicitar troca de e-mail + perfil (tela `security`).
328
357
  if (mountSecurity) {
329
- router.get('/account/security', [C.accountSecurity, 'index']);
330
- router.post('/account/security/password', [C.accountSecurity, 'changePassword']);
331
- router.post('/account/security/email', [C.accountSecurity, 'changeEmail']);
332
- router.post('/account/security/email/cancel', [C.accountSecurity, 'cancelEmailChange']);
333
- router.post('/account/security/profile', [C.accountSecurity, 'updateProfile']);
358
+ router.get(securityPath, [C.accountSecurity, 'index']);
359
+ router.post(`${securityPath}/password`, [C.accountSecurity, 'changePassword']);
360
+ router.post(`${securityPath}/email`, [C.accountSecurity, 'changeEmail']);
361
+ router.post(`${securityPath}/email/cancel`, [C.accountSecurity, 'cancelEmailChange']);
362
+ router.post(`${securityPath}/profile`, [C.accountSecurity, 'updateProfile']);
334
363
  // LGPD/GDPR: export de dados (portabilidade) + deleção self-service (danger zone).
335
364
  // O export carrega o throttle de login (anti-abuso) quando o rate-limit existe.
336
- withLogin(router.get('/account/security/export', [C.accountSecurity, 'exportData']));
337
- router.post('/account/security/delete', [C.accountSecurity, 'deleteAccount']);
365
+ withLogin(router.get(`${securityPath}/export`, [C.accountSecurity, 'exportData']));
366
+ router.post(`${securityPath}/delete`, [C.accountSecurity, 'deleteAccount']);
338
367
  // Trusted devices: limpa o cookie de confiança DESTE navegador.
339
- router.post('/account/security/trusted-devices/revoke', [
368
+ router.post(`${securityPath}/trusted-devices/revoke`, [
340
369
  C.accountSecurity,
341
370
  'revokeTrustedDevices',
342
371
  ]);
343
372
  }
344
373
  // Apps com acesso (consentimento): lista os grants da conta + revogação por client (tela `apps`).
345
374
  if (mountApps) {
346
- router.get('/account/apps', [C.accountApps, 'index']);
347
- router.post('/account/apps/:clientId/revoke', [C.accountApps, 'revoke']);
375
+ router.get(appsPath, [C.accountApps, 'index']);
376
+ router.post(`${appsPath}/:clientId/revoke`, [C.accountApps, 'revoke']);
348
377
  }
349
378
  // MFA — TOTP + passkeys (tela `mfa`).
350
379
  if (mountMfa) {
351
380
  // MFA / TOTP (enrollment, confirmação, disable).
352
- router.get('/account/mfa', [C.accountMfa, 'index']);
353
- router.post('/account/mfa/enroll', [C.accountMfa, 'enroll']);
354
- router.post('/account/mfa/confirm', [C.accountMfa, 'confirm']);
355
- router.post('/account/mfa/disable', [C.accountMfa, 'disable']);
381
+ router.get(mfaPath, [C.accountMfa, 'index']);
382
+ router.post(`${mfaPath}/enroll`, [C.accountMfa, 'enroll']);
383
+ router.post(`${mfaPath}/confirm`, [C.accountMfa, 'confirm']);
384
+ router.post(`${mfaPath}/disable`, [C.accountMfa, 'disable']);
356
385
  // MFA / WebAuthn (passkeys): registro (begin/finish) + remoção.
357
- router.post('/account/mfa/passkeys/options', [C.accountMfa, 'passkeyRegisterOptions']);
358
- router.post('/account/mfa/passkeys/verify', [C.accountMfa, 'passkeyRegisterVerify']);
359
- router.post('/account/mfa/passkeys/:id/remove', [C.accountMfa, 'passkeyRemove']);
386
+ router.post(`${mfaPath}/passkeys/options`, [C.accountMfa, 'passkeyRegisterOptions']);
387
+ router.post(`${mfaPath}/passkeys/verify`, [C.accountMfa, 'passkeyRegisterVerify']);
388
+ router.post(`${mfaPath}/passkeys/:id/remove`, [C.accountMfa, 'passkeyRemove']);
360
389
  }
361
390
  // Sudo mode (confirm identity): o GET lista os métodos; cada método
362
391
  // registra suas próprias rotas de verificação (SPI `SudoMethod`).
363
- router.get('/account/confirm', [C.accountConfirm, 'show']);
392
+ router.get(confirmPath, [C.accountConfirm, 'show']);
364
393
  // Rotas próprias dos métodos de sudo — DENTRO do grupo com `accountGuard`.
365
394
  // O guard não é só "tem sessão": ele roda `checkAndRefreshIdle`, que apaga
366
395
  // a sessão vencida por idle e refresca `authkit_last_seen`. Fora do grupo,
@@ -401,50 +430,55 @@ export function registerAuthHost(router, opts = {}) {
401
430
  // Organizations (multi-tenancy) — tela `orgs`. Montadas por default;
402
431
  // controller retorna 404/403 sem tabelas (capability-probed).
403
432
  if (mountOrgs) {
404
- router.get('/account/orgs', [C.accountOrgs, 'index']);
405
- router.post('/account/orgs', [C.accountOrgs, 'store']);
406
- router.post('/account/orgs/deactivate', [C.accountOrgs, 'deactivate']);
407
- router.post('/account/orgs/:id/activate', [C.accountOrgs, 'activate']);
408
- router.post('/account/orgs/:id/leave', [C.accountOrgs, 'leave']);
409
- router.post('/account/orgs/:id/invite', [C.accountOrgs, 'invite']);
410
- router.post('/account/orgs/:id/members/:accountId/remove', [C.accountOrgs, 'removeMember']);
411
- router.post('/account/orgs/:id/invitations/:invId/revoke', [C.accountOrgs, 'revokeInvitation']);
433
+ router.get(orgsPath, [C.accountOrgs, 'index']);
434
+ router.post(orgsPath, [C.accountOrgs, 'store']);
435
+ router.post(`${orgsPath}/deactivate`, [C.accountOrgs, 'deactivate']);
436
+ router.post(`${orgsPath}/:id/activate`, [C.accountOrgs, 'activate']);
437
+ router.post(`${orgsPath}/:id/leave`, [C.accountOrgs, 'leave']);
438
+ router.post(`${orgsPath}/:id/invite`, [C.accountOrgs, 'invite']);
439
+ router.post(`${orgsPath}/:id/members/:accountId/remove`, [C.accountOrgs, 'removeMember']);
440
+ router.post(`${orgsPath}/:id/invitations/:invId/revoke`, [
441
+ C.accountOrgs,
442
+ 'revokeInvitation',
443
+ ]);
412
444
  // JSON endpoints for React hooks (authkit-react).
413
- router.get('/account/orgs/json', [C.accountOrgs, 'listJson']);
414
- router.get('/account/orgs/invitations/json', [C.accountOrgs, 'listInvitationsJson']);
415
- router.get('/account/orgs/:id/json', [C.accountOrgs, 'showJson']);
445
+ router.get(`${orgsPath}/json`, [C.accountOrgs, 'listJson']);
446
+ router.get(`${orgsPath}/invitations/json`, [C.accountOrgs, 'listInvitationsJson']);
447
+ router.get(`${orgsPath}/:id/json`, [C.accountOrgs, 'showJson']);
416
448
  }
417
449
  // ─── Account self-service JSON API (authkit-react TanStack hooks) ─────
418
450
  // ⚠️ ORDER MATTERS: fixed-segment routes before parameterised ones.
419
451
  // Registered INSIDE the accountGuard group → same session-auth protection.
420
452
  // Mutating routes use CSRF (shield middleware on the host app).
421
- router.get('/account/api/me', [C.accountApi, 'me']);
422
- router.get('/account/api/security', [C.accountApi, 'securityOverview']);
423
- router.patch('/account/api/profile', [C.accountApi, 'updateProfile']);
424
- router.post('/account/api/password', [C.accountApi, 'changePassword']);
453
+ // Segue o prefixo do console (`accountRoutes.prefix`); o segmento `api` é
454
+ // FIXO — é contrato de máquina (authkit-react), não path de humano.
455
+ router.get(`${apiBase}/me`, [C.accountApi, 'me']);
456
+ router.get(`${apiBase}/security`, [C.accountApi, 'securityOverview']);
457
+ router.patch(`${apiBase}/profile`, [C.accountApi, 'updateProfile']);
458
+ router.post(`${apiBase}/password`, [C.accountApi, 'changePassword']);
425
459
  // Email-change: cancel BEFORE the generic post to avoid pattern collision.
426
- router.post('/account/api/email-change/cancel', [C.accountApi, 'cancelEmailChange']);
427
- router.post('/account/api/email-change', [C.accountApi, 'requestEmailChange']);
460
+ router.post(`${apiBase}/email-change/cancel`, [C.accountApi, 'cancelEmailChange']);
461
+ router.post(`${apiBase}/email-change`, [C.accountApi, 'requestEmailChange']);
428
462
  // Sessions: fixed routes BEFORE :id.
429
- router.get('/account/api/sessions', [C.accountApi, 'listSessions']);
430
- router.post('/account/api/sessions/revoke-others', [C.accountApi, 'revokeOtherSessions']);
431
- router.post('/account/api/sessions/revoke-all', [C.accountApi, 'revokeAllSessions']);
432
- router.delete('/account/api/sessions/:id', [C.accountApi, 'revokeSession']);
463
+ router.get(`${apiBase}/sessions`, [C.accountApi, 'listSessions']);
464
+ router.post(`${apiBase}/sessions/revoke-others`, [C.accountApi, 'revokeOtherSessions']);
465
+ router.post(`${apiBase}/sessions/revoke-all`, [C.accountApi, 'revokeAllSessions']);
466
+ router.delete(`${apiBase}/sessions/:id`, [C.accountApi, 'revokeSession']);
433
467
  // Apps (grants).
434
- router.get('/account/api/apps', [C.accountApi, 'listApps']);
435
- router.delete('/account/api/apps/:clientId', [C.accountApi, 'revokeApp']);
468
+ router.get(`${apiBase}/apps`, [C.accountApi, 'listApps']);
469
+ router.delete(`${apiBase}/apps/:clientId`, [C.accountApi, 'revokeApp']);
436
470
  // MFA + passkeys.
437
- router.get('/account/api/mfa', [C.accountApi, 'mfaStatus']);
438
- router.get('/account/api/passkeys', [C.accountApi, 'listPasskeys']);
439
- router.delete('/account/api/passkeys/:id', [C.accountApi, 'removePasskey']);
471
+ router.get(`${apiBase}/mfa`, [C.accountApi, 'mfaStatus']);
472
+ router.get(`${apiBase}/passkeys`, [C.accountApi, 'listPasskeys']);
473
+ router.delete(`${apiBase}/passkeys/:id`, [C.accountApi, 'removePasskey']);
440
474
  // PATs.
441
- router.get('/account/api/tokens', [C.accountApi, 'listTokens']);
442
- router.post('/account/api/tokens', [C.accountApi, 'createToken']);
443
- router.delete('/account/api/tokens/:id', [C.accountApi, 'revokeToken']);
475
+ router.get(`${apiBase}/tokens`, [C.accountApi, 'listTokens']);
476
+ router.post(`${apiBase}/tokens`, [C.accountApi, 'createToken']);
477
+ router.delete(`${apiBase}/tokens/:id`, [C.accountApi, 'revokeToken']);
444
478
  // Orgs: invitations BEFORE :id (fixed segment before parameterised).
445
- router.get('/account/api/orgs', [C.accountApi, 'listOrgs']);
446
- router.get('/account/api/orgs/invitations', [C.accountApi, 'listOrgInvitations']);
447
- router.get('/account/api/orgs/:id', [C.accountApi, 'showOrg']);
479
+ router.get(`${apiBase}/orgs`, [C.accountApi, 'listOrgs']);
480
+ router.get(`${apiBase}/orgs/invitations`, [C.accountApi, 'listOrgInvitations']);
481
+ router.get(`${apiBase}/orgs/:id`, [C.accountApi, 'showOrg']);
448
482
  })
449
483
  .use([accountGuard]);
450
484
  // Console admin (do config.admin.enabled ou opts). Protegido pelo adminGuard (sessão + role global).
@@ -481,13 +515,19 @@ export function registerAuthHost(router, opts = {}) {
481
515
  router.post(`${ap}/api/sessions/revoke-all`, [C.consoleSessions, 'revokeAll']);
482
516
  // Sessões por usuário (drawer do user — ANTES do catch-all para não receber HTML).
483
517
  router.get(`${ap}/api/users/:id/sessions`, [C.consoleSessions, 'userSessions']);
484
- router.post(`${ap}/api/users/:id/revoke-sessions`, [C.consoleSessions, 'userRevokeSessions']);
518
+ router.post(`${ap}/api/users/:id/revoke-sessions`, [
519
+ C.consoleSessions,
520
+ 'userRevokeSessions',
521
+ ]);
485
522
  // Clients OIDC.
486
523
  router.get(`${ap}/api/clients`, [C.consoleClients, 'index']);
487
524
  router.post(`${ap}/api/clients`, [C.consoleClients, 'store']);
488
525
  router.patch(`${ap}/api/clients/:id`, [C.consoleClients, 'update']);
489
526
  router.delete(`${ap}/api/clients/:id`, [C.consoleClients, 'destroy']);
490
- router.post(`${ap}/api/clients/:id/regenerate-secret`, [C.consoleClients, 'regenerateSecret']);
527
+ router.post(`${ap}/api/clients/:id/regenerate-secret`, [
528
+ C.consoleClients,
529
+ 'regenerateSecret',
530
+ ]);
491
531
  // Roles.
492
532
  router.get(`${ap}/api/roles`, [C.consoleRoles, 'index']);
493
533
  router.post(`${ap}/api/roles`, [C.consoleRoles, 'store']);
@@ -506,7 +546,10 @@ export function registerAuthHost(router, opts = {}) {
506
546
  router.delete(`${ap}/api/orgs/:id/members/:accountId`, [C.consoleOrgs, 'removeMember']);
507
547
  // Convites.
508
548
  router.post(`${ap}/api/orgs/:id/invitations`, [C.consoleOrgs, 'createInvitation']);
509
- router.delete(`${ap}/api/orgs/:id/invitations/:invitationId`, [C.consoleOrgs, 'revokeInvitation']);
549
+ router.delete(`${ap}/api/orgs/:id/invitations/:invitationId`, [
550
+ C.consoleOrgs,
551
+ 'revokeInvitation',
552
+ ]);
510
553
  // Auditoria (capability-gated: 404 quando sink não suporta consulta).
511
554
  router.get(`${ap}/api/audit`, [C.consoleAudit, 'index']);
512
555
  // Settings.
@@ -574,7 +617,10 @@ export function registerAuthHost(router, opts = {}) {
574
617
  withApiThrottle(router.delete('/organizations/:id/members/:accountId', [C.apiOrgs, 'removeMember']));
575
618
  withApiThrottle(router.patch('/organizations/:id/members/:accountId', [C.apiOrgs, 'updateMemberRole']));
576
619
  withApiThrottle(router.post('/organizations/:id/invitations', [C.apiOrgs, 'createInvitation']));
577
- withApiThrottle(router.delete('/organizations/:id/invitations/:invitationId', [C.apiOrgs, 'revokeInvitation']));
620
+ withApiThrottle(router.delete('/organizations/:id/invitations/:invitationId', [
621
+ C.apiOrgs,
622
+ 'revokeInvitation',
623
+ ]));
578
624
  // Auditoria + métricas + verificação de token.
579
625
  withApiThrottle(router.get('/audit', [C.apiMisc, 'audit']));
580
626
  withApiThrottle(router.get('/stats', [C.apiMisc, 'stats']));
@@ -1,5 +1,6 @@
1
- import { DEFAULT_MESSAGES, translate } from '../i18n.js';
2
1
  import { getAccountLoginUrl } from '../account_login_url.js';
2
+ import { accountPathsMap } from '../account_paths.js';
3
+ import { DEFAULT_MESSAGES, translate } from '../i18n.js';
3
4
  /**
4
5
  * Resolve o catálogo de mensagens ativo a partir do `authkit.server` (config
5
6
  * resolvida com o locale do host). Defensivo: se o container/serviço não estiver
@@ -33,7 +34,19 @@ export async function renderEdgeView(ctx, view, props) {
33
34
  // usam o destino configurável (`accountLoginUrl`) em vez do `/account/login` fixo,
34
35
  // que pode estar desmontado. Props explícitas ainda têm precedência (spread depois).
35
36
  const loginUrl = getAccountLoginUrl();
36
- return ctx.view.render(`authkit::${view}`, { loginUrl, ...props, t, messages });
37
+ // `accountPaths` como prop global: os `<form action>`/`fetch()` das views do
38
+ // console de conta (security, mfa, tokens, apps, orgs, ...) derivam o path de
39
+ // cada tela daqui, respeitando o prefixo/segmentos configurados via
40
+ // `accountRoutes` — sem cada view conhecer o prefixo. Os action-subpaths
41
+ // fixos (`/password`, `/passkeys/verify`, ...) são concatenados na própria view.
42
+ const accountPaths = accountPathsMap();
43
+ return ctx.view.render(`authkit::${view}`, {
44
+ loginUrl,
45
+ accountPaths,
46
+ ...props,
47
+ t,
48
+ messages,
49
+ });
37
50
  }
38
51
  /** Renderer do seam para hosts Edge. As views são donas-da-lib (disco `authkit::`). */
39
52
  export function edgeRenderer() {
@@ -94,13 +94,21 @@ export type AuthkitScreen = 'login' | 'signup' | 'consent' | 'forgot' | 'reset'
94
94
  *
95
95
  * ### Props da tela `account/mfa`
96
96
  *
97
+ * As props variam por action: `index` manda o estado base; `enroll` acrescenta
98
+ * `enrolling`/`secret`/`qrDataUrl` (passo do QR); `confirm` com código inválido
99
+ * reenvia `enrolling: true` + `error` (sem regenerar o segredo).
100
+ *
97
101
  * | Prop | Tipo | Descrição |
98
102
  * |---------------------|---------------------|-----------|
99
103
  * | `csrfToken` | `string` | Token CSRF para os formulários de enroll/confirm/disable e passkeys. |
100
104
  * | `enabled` | `boolean` | `true` quando o TOTP já está confirmado (habilitado) para a conta. |
101
105
  * | `recoveryCodes` | `string[] \| null` | Códigos de recuperação recém-gerados (exibidos UMA vez após enroll/confirm) ou `null`. |
102
106
  * | `passkeysSupported` | `boolean` | `true` quando o store persiste credenciais WebAuthn (passkeys). |
103
- * | `passkeys` | `Array<{ id: string; label?: string; createdAt: string }>` | Passkeys cadastradas (vazio quando não suportado). `id` é base64url; `createdAt` é ISO. |
107
+ * | `passkeys` | `Array<{ id: string; label?: string; createdAt: string }>` | Passkeys cadastradas (vazio quando não suportado). `id` é base64url; `createdAt` é ISO. Presente em `index`. |
108
+ * | `enrolling` | `boolean \| undefined` | `true` no passo de enrollment (após `POST /mfa/enroll` e na reexibição do `confirm` com código inválido) — a tela mostra o QR/segredo e o campo de código. Ausente em `index`. |
109
+ * | `secret` | `string \| null \| undefined` | Segredo TOTP em texto (base32) para entrada manual, exibido no passo de enroll. `null` na reexibição do `confirm` (o segredo pendente NÃO é regenerado). Ausente em `index`. |
110
+ * | `qrDataUrl` | `string \| null \| undefined` | QR code do `otpauth://` como data-URL (`<img src>`), no passo de enroll. `null` na reexibição do `confirm`. Ausente em `index`. |
111
+ * | `error` | `string \| undefined` | Mensagem de erro localizada (ex.: código TOTP inválido no `confirm`). Ausente quando não há erro. |
104
112
  * | `messages` | `AuthMessages` | Catálogo de mensagens i18n. |
105
113
  *
106
114
  * ### Props da tela `account/confirm` (sudo — confirmar identidade)
@@ -65,13 +65,21 @@ async function resolveMessagesFromCtx(ctx) {
65
65
  *
66
66
  * ### Props da tela `account/mfa`
67
67
  *
68
+ * As props variam por action: `index` manda o estado base; `enroll` acrescenta
69
+ * `enrolling`/`secret`/`qrDataUrl` (passo do QR); `confirm` com código inválido
70
+ * reenvia `enrolling: true` + `error` (sem regenerar o segredo).
71
+ *
68
72
  * | Prop | Tipo | Descrição |
69
73
  * |---------------------|---------------------|-----------|
70
74
  * | `csrfToken` | `string` | Token CSRF para os formulários de enroll/confirm/disable e passkeys. |
71
75
  * | `enabled` | `boolean` | `true` quando o TOTP já está confirmado (habilitado) para a conta. |
72
76
  * | `recoveryCodes` | `string[] \| null` | Códigos de recuperação recém-gerados (exibidos UMA vez após enroll/confirm) ou `null`. |
73
77
  * | `passkeysSupported` | `boolean` | `true` quando o store persiste credenciais WebAuthn (passkeys). |
74
- * | `passkeys` | `Array<{ id: string; label?: string; createdAt: string }>` | Passkeys cadastradas (vazio quando não suportado). `id` é base64url; `createdAt` é ISO. |
78
+ * | `passkeys` | `Array<{ id: string; label?: string; createdAt: string }>` | Passkeys cadastradas (vazio quando não suportado). `id` é base64url; `createdAt` é ISO. Presente em `index`. |
79
+ * | `enrolling` | `boolean \| undefined` | `true` no passo de enrollment (após `POST /mfa/enroll` e na reexibição do `confirm` com código inválido) — a tela mostra o QR/segredo e o campo de código. Ausente em `index`. |
80
+ * | `secret` | `string \| null \| undefined` | Segredo TOTP em texto (base32) para entrada manual, exibido no passo de enroll. `null` na reexibição do `confirm` (o segredo pendente NÃO é regenerado). Ausente em `index`. |
81
+ * | `qrDataUrl` | `string \| null \| undefined` | QR code do `otpauth://` como data-URL (`<img src>`), no passo de enroll. `null` na reexibição do `confirm`. Ausente em `index`. |
82
+ * | `error` | `string \| undefined` | Mensagem de erro localizada (ex.: código TOTP inválido no `confirm`). Ausente quando não há erro. |
75
83
  * | `messages` | `AuthMessages` | Catálogo de mensagens i18n. |
76
84
  *
77
85
  * ### Props da tela `account/confirm` (sudo — confirmar identidade)
@@ -50,7 +50,7 @@
50
50
  * );
51
51
  * ```
52
52
  */
53
- import { isSettingLocked, SettingLockedError } from './config_locks.js';
53
+ import { SettingLockedError, isSettingLocked } from './config_locks.js';
54
54
  /**
55
55
  * Type guard: o objeto (store ou serviço) expõe SettingsCapability?
56
56
  */
@@ -65,7 +65,7 @@ export interface RegistrationSetting {
65
65
  * FAIL-SAFE: `getSetting` nunca lança (contrato do RuntimeSettings). Se
66
66
  * retornar null, usamos configDefault.
67
67
  */
68
- export declare function resolveEffectiveRegistration(configDefault: boolean | undefined, settings: SettingsCapability): Promise<boolean>;
68
+ export declare function resolveEffectiveRegistration(configDefault: boolean, settings: SettingsCapability): Promise<boolean>;
69
69
  /**
70
70
  * Shape da setting `require_verified_email` em `auth_settings`.
71
71
  *
@@ -96,14 +96,14 @@ export interface ResolvedRequireVerifiedEmail {
96
96
  * - setting presente → sobrescreve.
97
97
  * - setting ausente/inválido/erro → usa configDefault.
98
98
  */
99
- export declare function resolveEffectiveRequireVerifiedEmail(configDefault: boolean | undefined, settings: SettingsCapability): Promise<boolean>;
99
+ export declare function resolveEffectiveRequireVerifiedEmail(configDefault: boolean, settings: SettingsCapability): Promise<boolean>;
100
100
  /**
101
101
  * Resolve o valor efetivo COMPLETO de `require_verified_email` incluindo
102
102
  * `graceDays`. Usado pelo gate de login para implementar a janela de graça.
103
103
  *
104
104
  * FAIL-SAFE: qualquer erro → `{ enabled: configDefault, graceDays: 0 }`.
105
105
  */
106
- export declare function resolveEffectiveRequireVerifiedEmailFull(configDefault: boolean | undefined, settings: SettingsCapability): Promise<ResolvedRequireVerifiedEmail>;
106
+ export declare function resolveEffectiveRequireVerifiedEmailFull(configDefault: boolean, settings: SettingsCapability): Promise<ResolvedRequireVerifiedEmail>;
107
107
  /**
108
108
  * Shape da setting `maintenance_mode` em `auth_settings`.
109
109
  *