@adonis-agora/authkit-server 0.46.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 (195) 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/host/views/otp-unlock.edge +2 -2
  15. package/build/index.d.ts +7 -7
  16. package/build/index.js +5 -5
  17. package/build/providers/authkit_server_provider.d.ts +2 -2
  18. package/build/providers/authkit_server_provider.js +26 -10
  19. package/build/services/booted_app.d.ts +8 -0
  20. package/build/services/booted_app.js +27 -0
  21. package/build/services/main.d.ts +8 -1
  22. package/build/services/main.js +10 -2
  23. package/build/src/accounts/lucid_account_store.d.ts +3 -3
  24. package/build/src/accounts/lucid_account_store.js +6 -8
  25. package/build/src/accounts/lucid_store/core.js +1 -1
  26. package/build/src/accounts/lucid_store/mfa.js +1 -1
  27. package/build/src/accounts/lucid_store/organizations.js +2 -4
  28. package/build/src/accounts/lucid_store/password_hygiene.js +1 -4
  29. package/build/src/accounts/lucid_store/shared.d.ts +1 -1
  30. package/build/src/accounts/lucid_stores.d.ts +3 -3
  31. package/build/src/accounts/lucid_stores.js +2 -2
  32. package/build/src/adapters/database_adapter.js +3 -1
  33. package/build/src/adapters/factory.js +1 -1
  34. package/build/src/adapters/redis_adapter.js +2 -1
  35. package/build/src/commands/expire_scan_command.js +19 -5
  36. package/build/src/commands/settings_commands.js +22 -5
  37. package/build/src/define_config.d.ts +20 -20
  38. package/build/src/define_config.js +62 -67
  39. package/build/src/doctor/checks.js +45 -20
  40. package/build/src/events/dispatcher.d.ts +1 -1
  41. package/build/src/events/dispatcher.js +10 -10
  42. package/build/src/host/account_api/account_api_controller.js +95 -39
  43. package/build/src/host/account_deletion_ops.d.ts +4 -4
  44. package/build/src/host/account_deletion_ops.js +5 -5
  45. package/build/src/host/account_deletion_service.d.ts +2 -2
  46. package/build/src/host/account_deletion_service.js +2 -2
  47. package/build/src/host/account_export_service.d.ts +1 -1
  48. package/build/src/host/account_export_service.js +4 -6
  49. package/build/src/host/account_home.d.ts +0 -8
  50. package/build/src/host/account_home.js +6 -4
  51. package/build/src/host/account_login_url.d.ts +44 -0
  52. package/build/src/host/account_login_url.js +58 -0
  53. package/build/src/host/account_paths.d.ts +120 -0
  54. package/build/src/host/account_paths.js +161 -0
  55. package/build/src/host/admin_api/admin_orgs_service.d.ts +2 -2
  56. package/build/src/host/admin_api/admin_orgs_service.js +3 -2
  57. package/build/src/host/admin_api/admin_users_service.d.ts +12 -12
  58. package/build/src/host/admin_api/admin_users_service.js +20 -20
  59. package/build/src/host/admin_api/api_clients_controller.js +2 -2
  60. package/build/src/host/admin_api/api_misc_controller.js +2 -2
  61. package/build/src/host/admin_api/api_orgs_controller.js +3 -3
  62. package/build/src/host/admin_api/api_settings_controller.js +11 -5
  63. package/build/src/host/admin_api/api_users_controller.d.ts +2 -2
  64. package/build/src/host/admin_api/api_users_controller.js +37 -38
  65. package/build/src/host/admin_api/dto.d.ts +4 -4
  66. package/build/src/host/admin_clients_service.js +5 -4
  67. package/build/src/host/admin_console/admin_shell_controller.js +4 -2
  68. package/build/src/host/admin_console/console_audit_controller.js +1 -1
  69. package/build/src/host/admin_console/console_clients_controller.js +5 -3
  70. package/build/src/host/admin_console/console_impersonation_controller.js +1 -1
  71. package/build/src/host/admin_console/console_keys_controller.js +2 -4
  72. package/build/src/host/admin_console/console_orgs_controller.js +3 -3
  73. package/build/src/host/admin_console/console_roles_controller.js +30 -12
  74. package/build/src/host/admin_console/console_sessions_controller.js +3 -3
  75. package/build/src/host/admin_console/console_settings_controller.js +11 -5
  76. package/build/src/host/admin_console/console_users_controller.d.ts +2 -2
  77. package/build/src/host/admin_console/console_users_controller.js +46 -47
  78. package/build/src/host/admin_prefix.js +2 -2
  79. package/build/src/host/admin_sessions_service.d.ts +3 -1
  80. package/build/src/host/admin_sessions_service.js +36 -6
  81. package/build/src/host/adonis_auth_sync.d.ts +1 -1
  82. package/build/src/host/adonis_auth_user_provider.d.ts +1 -1
  83. package/build/src/host/app_key.js +1 -1
  84. package/build/src/host/avatar_storage.js +1 -3
  85. package/build/src/host/config_locks.js +1 -3
  86. package/build/src/host/console_session.d.ts +4 -0
  87. package/build/src/host/console_session.js +9 -2
  88. package/build/src/host/controllers/account_apps_controller.js +3 -2
  89. package/build/src/host/controllers/account_confirm_controller.js +2 -6
  90. package/build/src/host/controllers/account_mfa_controller.js +63 -12
  91. package/build/src/host/controllers/account_orgs_controller.js +53 -30
  92. package/build/src/host/controllers/account_security_controller.d.ts +2 -2
  93. package/build/src/host/controllers/account_security_controller.js +116 -116
  94. package/build/src/host/controllers/account_session_controller.js +7 -5
  95. package/build/src/host/controllers/account_tokens_controller.js +14 -2
  96. package/build/src/host/controllers/interaction_controller.js +59 -22
  97. package/build/src/host/controllers/pat_introspection_controller.js +10 -0
  98. package/build/src/host/controllers/registration_controller.js +28 -13
  99. package/build/src/host/default_mailer.js +11 -7
  100. package/build/src/host/durable/account_deletion_workflow.d.ts +2 -2
  101. package/build/src/host/durable/account_deletion_workflow.js +14 -14
  102. package/build/src/host/durable/account_export_workflow.d.ts +2 -2
  103. package/build/src/host/durable/account_export_workflow.js +12 -12
  104. package/build/src/host/durable/index.d.ts +5 -5
  105. package/build/src/host/durable/index.js +6 -6
  106. package/build/src/host/i18n.d.ts +1376 -1376
  107. package/build/src/host/i18n.js +1380 -1380
  108. package/build/src/host/impersonation.js +1 -7
  109. package/build/src/host/key_rotation_actions.d.ts +1 -1
  110. package/build/src/host/login_attempt.js +35 -6
  111. package/build/src/host/login_notify.js +1 -1
  112. package/build/src/host/middleware/account_auth.js +3 -1
  113. package/build/src/host/otp_lockout.js +1 -3
  114. package/build/src/host/rate_limit.js +1 -1
  115. package/build/src/host/register_auth_host.d.ts +96 -2
  116. package/build/src/host/register_auth_host.js +196 -91
  117. package/build/src/host/renderers/edge_renderer.js +19 -1
  118. package/build/src/host/renderers/inertia_renderer.d.ts +63 -0
  119. package/build/src/host/renderers/inertia_renderer.js +63 -0
  120. package/build/src/host/runtime_settings.js +1 -1
  121. package/build/src/host/runtime_toggles.d.ts +3 -3
  122. package/build/src/host/runtime_toggles.js +69 -24
  123. package/build/src/host/security_notice_service.d.ts +1 -1
  124. package/build/src/host/security_notice_service.js +1 -1
  125. package/build/src/host/session_context.js +1 -1
  126. package/build/src/host/sudo/index.d.ts +3 -3
  127. package/build/src/host/sudo/index.js +3 -3
  128. package/build/src/host/sudo/methods/magic_link.js +6 -5
  129. package/build/src/host/sudo/methods/passkey.js +7 -4
  130. package/build/src/host/sudo/methods/password.js +8 -3
  131. package/build/src/host/sudo/runtime.js +6 -13
  132. package/build/src/host/sudo_mode.js +3 -2
  133. package/build/src/host/ui-dist/assets/index-BQoTDiLV.js +137 -0
  134. package/build/src/host/ui-dist/index.html +1 -1
  135. package/build/src/keys/jwks_manager.js +1 -1
  136. package/build/src/keys/keystore.js +1 -1
  137. package/build/src/keys/keystore_manager.d.ts +2 -2
  138. package/build/src/keys/keystore_manager.js +2 -2
  139. package/build/src/keys/keystore_vault.js +8 -4
  140. package/build/src/mixins/with_audit_log.d.ts +1 -1
  141. package/build/src/mixins/with_auth_user.d.ts +1 -1
  142. package/build/src/mixins/with_auth_user.js +1 -1
  143. package/build/src/mixins/with_credentials.d.ts +1 -1
  144. package/build/src/mixins/with_mfa.d.ts +1 -1
  145. package/build/src/mixins/with_personal_access_token.d.ts +1 -1
  146. package/build/src/mixins/with_provider_identity.d.ts +1 -1
  147. package/build/src/mixins/with_webauthn_credential.d.ts +1 -1
  148. package/build/src/observability/diagnostics_bridge.js +2 -2
  149. package/build/src/observability/metrics_service.js +1 -1
  150. package/build/src/observability/telescope/data_providers.d.ts +1 -1
  151. package/build/src/observability/telescope/data_providers.js +29 -43
  152. package/build/src/observability/telescope/extension.d.ts +1 -1
  153. package/build/src/observability/telescope/extension.js +58 -58
  154. package/build/src/observability/telescope/index.d.ts +2 -2
  155. package/build/src/observability/telescope/index.js +2 -2
  156. package/build/src/observability/wire_provider_events.d.ts +1 -1
  157. package/build/src/observability/wire_provider_events.js +11 -11
  158. package/build/src/password/common_passwords.js +1 -1
  159. package/build/src/password/password_manager.d.ts +1 -1
  160. package/build/src/password/password_manager.js +4 -4
  161. package/build/src/pat/lucid_pat_store.js +2 -6
  162. package/build/src/pat/pat_tokens.js +1 -1
  163. package/build/src/provider/interaction_actions.js +1 -1
  164. package/build/src/provider/key_rotation_scheduler.js +3 -1
  165. package/build/src/provider/logout_sources.js +2 -8
  166. package/build/src/provider/oidc_service.d.ts +2 -2
  167. package/build/src/provider/oidc_service.js +22 -14
  168. package/build/src/provider/single_flight_lock.js +1 -3
  169. package/build/src/provider/token_exchange.d.ts +1 -1
  170. package/build/src/schema/ensure.js +3 -1
  171. package/build/stubs/ui/react/components/auth_shell.tsx +19 -19
  172. package/build/stubs/ui/react/pages/account/login.tsx +6 -7
  173. package/build/stubs/ui/react/pages/account/mfa.tsx +16 -15
  174. package/build/stubs/ui/react/pages/account/tokens.tsx +27 -17
  175. package/build/stubs/ui/react/pages/consent.tsx +12 -12
  176. package/build/stubs/ui/react/pages/forgot.tsx +7 -7
  177. package/build/stubs/ui/react/pages/login.tsx +22 -24
  178. package/build/stubs/ui/react/pages/mfa-challenge.tsx +9 -10
  179. package/build/stubs/ui/react/pages/reset.tsx +10 -10
  180. package/build/stubs/ui/react/pages/signup.tsx +30 -16
  181. package/build/stubs/ui/react/pages/verify-email.tsx +6 -6
  182. package/package.json +1 -1
  183. package/stubs/main.ts +2 -2
  184. package/stubs/ui/react/components/auth_shell.tsx +19 -19
  185. package/stubs/ui/react/pages/account/login.tsx +6 -7
  186. package/stubs/ui/react/pages/account/mfa.tsx +16 -15
  187. package/stubs/ui/react/pages/account/tokens.tsx +27 -17
  188. package/stubs/ui/react/pages/consent.tsx +12 -12
  189. package/stubs/ui/react/pages/forgot.tsx +7 -7
  190. package/stubs/ui/react/pages/login.tsx +22 -24
  191. package/stubs/ui/react/pages/mfa-challenge.tsx +9 -10
  192. package/stubs/ui/react/pages/reset.tsx +10 -10
  193. package/stubs/ui/react/pages/signup.tsx +30 -16
  194. package/stubs/ui/react/pages/verify-email.tsx +6 -6
  195. package/build/src/host/ui-dist/assets/index-BmI_J87g.js +0 -137
@@ -1,16 +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 { completeSudo, fail, guardSudoRoutes, sudoContextFrom, setMountedSudoMethods, } from './sudo/runtime.js';
12
- import { password as sudoPassword } from './sudo/methods/password.js';
13
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';
14
16
  /**
15
17
  * Métodos de sudo montados quando o host não passa `sudoMethods` —
16
18
  * comportamento histórico (senha + passkey).
@@ -68,16 +70,26 @@ async function checkAndRefreshIdle(ctx) {
68
70
  * @internal
69
71
  */
70
72
  function buildLoginRedirect(ctx, extra) {
73
+ // Destino configurável (`accountLoginUrl`): default `/account/login`, mas um
74
+ // host que desmontou a tela de login (`account: { login: false }`) aponta para
75
+ // a própria rota de login dele (ex.: `/login`). Ver `account_login_url.ts`.
76
+ const loginUrl = getAccountLoginUrl();
71
77
  const url = ctx.request?.url?.() ?? '';
72
78
  const qs = ctx.request?.parsedUrl?.search ?? '';
73
79
  const dest = qs ? `${url}${qs}` : url;
74
80
  // Só inclui return_to quando há um caminho real (não vazio, não é o próprio login).
75
- if (dest && dest !== '/' && !dest.startsWith('/account/login')) {
81
+ if (dest && dest !== '/' && !dest.startsWith(loginUrl)) {
76
82
  const encoded = encodeURIComponent(dest);
77
- const base = extra ? `/account/login?${extra}&return_to=${encoded}` : `/account/login?return_to=${encoded}`;
83
+ const sep = loginUrl.includes('?') ? '&' : '?';
84
+ const base = extra
85
+ ? `${loginUrl}${sep}${extra}&return_to=${encoded}`
86
+ : `${loginUrl}${sep}return_to=${encoded}`;
78
87
  return base;
79
88
  }
80
- return extra ? `/account/login?${extra}` : '/account/login';
89
+ if (!extra)
90
+ return loginUrl;
91
+ const sep = loginUrl.includes('?') ? '&' : '?';
92
+ return `${loginUrl}${sep}${extra}`;
81
93
  }
82
94
  /**
83
95
  * Guard inline do console de conta. Usamos uma closure (forma confiável do
@@ -100,9 +112,11 @@ const accountGuard = async (ctx, next) => {
100
112
  * Guard do console admin (B6). Como o `accountGuard`, é uma closure inline (forma
101
113
  * confiável do `.use()` num grupo). Exige:
102
114
  * 0. `config.admin.enabled` ligado (senão → 404; ver nota de flag-drift abaixo);
103
- * 1. sessão de conta ativa (senão → /account/login);
115
+ * 1. sessão de conta ativa (senão → `accountLoginUrl`, default /account/login);
104
116
  * 2. a conta logada com pelo menos UMA das `config.admin.roles` nas roles globais
105
- * (senão → /account/tokens, evitando vazar a existência do /admin).
117
+ * (senão → `accountHome(cfg)`, default /account/security NÃO revela a
118
+ * existência do /admin, e cai numa tela que o host controla via `accountHome`;
119
+ * se a tela default estiver desmontada, aponte `config.accountHome` para uma montada).
106
120
  * As roles permitidas são resolvidas em runtime do `authkit.server` (config lazy).
107
121
  */
108
122
  export const adminGuard = async (ctx, next) => {
@@ -188,6 +202,38 @@ export function registerAuthHost(router, opts = {}) {
188
202
  const social = opts.social ?? hostCfg?.social;
189
203
  const adminOpt = opts.admin ?? (hostCfg?.adminEnabled ? true : undefined);
190
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
+ }
214
+ // Destino do redirect de "faça login" — persiste no singleton de processo para
215
+ // que os guards (closures de tempo de registro), o middleware, os controllers e
216
+ // as views leiam o mesmo valor. Só quando a opção foi passada (senão deriva de
217
+ // `accountPath('login')` — back-compat).
218
+ if (opts.accountLoginUrl !== undefined) {
219
+ setAccountLoginUrl(opts.accountLoginUrl);
220
+ }
221
+ // Montagem por tela do console de conta. `undefined` → tudo montado;
222
+ // `false` → nada; objeto → cada flag ausente default `true`.
223
+ const accountOpt = opts.account;
224
+ const mountScreen = (key) => {
225
+ if (accountOpt === false)
226
+ return false;
227
+ if (accountOpt && typeof accountOpt === 'object')
228
+ return accountOpt[key] !== false;
229
+ return true;
230
+ };
231
+ const mountLogin = mountScreen('login');
232
+ const mountTokens = mountScreen('tokens');
233
+ const mountOrgs = mountScreen('orgs');
234
+ const mountSecurity = mountScreen('security');
235
+ const mountMfa = mountScreen('mfa');
236
+ const mountApps = mountScreen('apps');
191
237
  // Throttles opt-in (anti-brute-force). `undefined` quando rate-limit desligado.
192
238
  const resolvedRateLimit = opts.rateLimit !== undefined
193
239
  ? resolveRateLimit(opts.rateLimit)
@@ -230,7 +276,10 @@ export function registerAuthHost(router, opts = {}) {
230
276
  withLogin(router.post('/auth/interaction/:uid/login', [C.interaction, 'login']));
231
277
  withLogin(router.post('/auth/interaction/:uid/mfa', [C.interaction, 'mfaVerify']));
232
278
  // Troca de senha obrigatória quando a senha expirou (password expiration gate).
233
- 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
+ ]));
234
283
  // Passkey como 2º fator alternativo no login (begin/finish; challenge na sessão).
235
284
  router.post('/auth/interaction/:uid/passkey/options', [C.interaction, 'passkeyOptions']);
236
285
  withLogin(router.post('/auth/interaction/:uid/passkey/verify', [C.interaction, 'passkeyVerify']));
@@ -257,54 +306,90 @@ export function registerAuthHost(router, opts = {}) {
257
306
  }
258
307
  // PAT introspection (server-to-server).
259
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');
260
325
  // Organizations — invitation accept (sem guard: controller lida com não-autenticado).
261
- router.get('/account/orgs/invitations/:token/accept', [C.accountOrgs, 'showAcceptInvitation']);
262
- router.post('/account/orgs/invitations/:token/accept', [C.accountOrgs, 'acceptInvitation']);
326
+ // Parte da tela `orgs`: desmontada junto (sem multi-tenancy, não há convite a aceitar).
327
+ if (mountOrgs) {
328
+ router.get(`${orgsPath}/invitations/:token/accept`, [C.accountOrgs, 'showAcceptInvitation']);
329
+ router.post(`${orgsPath}/invitations/:token/accept`, [C.accountOrgs, 'acceptInvitation']);
330
+ }
263
331
  // Console de conta (login de sessão do IdP + gerência de PAT).
264
- router.get('/account/login', [C.accountSession, 'show']);
265
- // L6: throttle por IP no login/logout do console de conta (anti-brute-force),
266
- // alinhado com as demais rotas de credencial (interaction, forgot, reset).
267
- withLogin(router.post('/account/login', [C.accountSession, 'login']));
268
- withLogin(router.post('/account/logout', [C.accountSession, 'logout']));
332
+ // Tela `login` desmontável: hosts passwordless delegam ao OIDC próprio e
333
+ // apontam `accountLoginUrl` para a rota de login deles.
334
+ if (mountLogin) {
335
+ router.get(loginPath, [C.accountSession, 'show']);
336
+ // L6: throttle por IP no login/logout do console de conta (anti-brute-force),
337
+ // alinhado com as demais rotas de credencial (interaction, forgot, reset).
338
+ withLogin(router.post(loginPath, [C.accountSession, 'login']));
339
+ withLogin(router.post(logoutPath, [C.accountSession, 'logout']));
340
+ }
269
341
  // Confirmação de troca de e-mail (standalone, GET-only — consome o token do link;
270
- // pode ser aberta em outro dispositivo, então NÃO exige sessão).
271
- router.get('/account/email/confirm', [C.accountSecurity, 'confirmEmail']);
272
- // Rotas de tokens protegidas por AccountAuthMiddleware (redireciona para /account/login se não autenticado).
342
+ // pode ser aberta em outro dispositivo, então NÃO exige sessão). Parte da tela
343
+ // `security` (o terminal do fluxo de troca de e-mail).
344
+ if (mountSecurity) {
345
+ router.get(emailConfirmPath, [C.accountSecurity, 'confirmEmail']);
346
+ }
347
+ // Rotas de tokens protegidas por AccountAuthMiddleware (redireciona para a tela de login se não autenticado).
273
348
  router
274
349
  .group(() => {
275
- router.get('/account/tokens', [C.accountTokens, 'index']);
276
- router.post('/account/tokens', [C.accountTokens, 'store']);
277
- router.post('/account/tokens/:id/revoke', [C.accountTokens, 'destroy']);
278
- // Segurança da conta: trocar senha + solicitar troca de e-mail + perfil.
279
- router.get('/account/security', [C.accountSecurity, 'index']);
280
- router.post('/account/security/password', [C.accountSecurity, 'changePassword']);
281
- router.post('/account/security/email', [C.accountSecurity, 'changeEmail']);
282
- router.post('/account/security/email/cancel', [C.accountSecurity, 'cancelEmailChange']);
283
- router.post('/account/security/profile', [C.accountSecurity, 'updateProfile']);
284
- // LGPD/GDPR: export de dados (portabilidade) + deleção self-service (danger zone).
285
- // O export carrega o throttle de login (anti-abuso) quando o rate-limit existe.
286
- withLogin(router.get('/account/security/export', [C.accountSecurity, 'exportData']));
287
- router.post('/account/security/delete', [C.accountSecurity, 'deleteAccount']);
288
- // Trusted devices: limpa o cookie de confiança DESTE navegador.
289
- router.post('/account/security/trusted-devices/revoke', [
290
- C.accountSecurity,
291
- 'revokeTrustedDevices',
292
- ]);
293
- // Apps com acesso (consentimento): lista os grants da conta + revogação por client.
294
- router.get('/account/apps', [C.accountApps, 'index']);
295
- router.post('/account/apps/:clientId/revoke', [C.accountApps, 'revoke']);
296
- // MFA / TOTP (enrollment, confirmação, disable).
297
- router.get('/account/mfa', [C.accountMfa, 'index']);
298
- router.post('/account/mfa/enroll', [C.accountMfa, 'enroll']);
299
- router.post('/account/mfa/confirm', [C.accountMfa, 'confirm']);
300
- router.post('/account/mfa/disable', [C.accountMfa, 'disable']);
301
- // MFA / WebAuthn (passkeys): registro (begin/finish) + remoção.
302
- router.post('/account/mfa/passkeys/options', [C.accountMfa, 'passkeyRegisterOptions']);
303
- router.post('/account/mfa/passkeys/verify', [C.accountMfa, 'passkeyRegisterVerify']);
304
- router.post('/account/mfa/passkeys/:id/remove', [C.accountMfa, 'passkeyRemove']);
350
+ // Personal Access Tokens (tela `tokens`).
351
+ if (mountTokens) {
352
+ router.get(tokensPath, [C.accountTokens, 'index']);
353
+ router.post(tokensPath, [C.accountTokens, 'store']);
354
+ router.post(`${tokensPath}/:id/revoke`, [C.accountTokens, 'destroy']);
355
+ }
356
+ // Segurança da conta: trocar senha + solicitar troca de e-mail + perfil (tela `security`).
357
+ if (mountSecurity) {
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']);
363
+ // LGPD/GDPR: export de dados (portabilidade) + deleção self-service (danger zone).
364
+ // O export carrega o throttle de login (anti-abuso) quando o rate-limit existe.
365
+ withLogin(router.get(`${securityPath}/export`, [C.accountSecurity, 'exportData']));
366
+ router.post(`${securityPath}/delete`, [C.accountSecurity, 'deleteAccount']);
367
+ // Trusted devices: limpa o cookie de confiança DESTE navegador.
368
+ router.post(`${securityPath}/trusted-devices/revoke`, [
369
+ C.accountSecurity,
370
+ 'revokeTrustedDevices',
371
+ ]);
372
+ }
373
+ // Apps com acesso (consentimento): lista os grants da conta + revogação por client (tela `apps`).
374
+ if (mountApps) {
375
+ router.get(appsPath, [C.accountApps, 'index']);
376
+ router.post(`${appsPath}/:clientId/revoke`, [C.accountApps, 'revoke']);
377
+ }
378
+ // MFA — TOTP + passkeys (tela `mfa`).
379
+ if (mountMfa) {
380
+ // MFA / TOTP (enrollment, confirmação, 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']);
385
+ // MFA / WebAuthn (passkeys): registro (begin/finish) + remoção.
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']);
389
+ }
305
390
  // Sudo mode (confirm identity): o GET lista os métodos; cada método
306
391
  // registra suas próprias rotas de verificação (SPI `SudoMethod`).
307
- router.get('/account/confirm', [C.accountConfirm, 'show']);
392
+ router.get(confirmPath, [C.accountConfirm, 'show']);
308
393
  // Rotas próprias dos métodos de sudo — DENTRO do grupo com `accountGuard`.
309
394
  // O guard não é só "tem sessão": ele roda `checkAndRefreshIdle`, que apaga
310
395
  // a sessão vencida por idle e refresca `authkit_last_seen`. Fora do grupo,
@@ -342,50 +427,58 @@ export function registerAuthHost(router, opts = {}) {
342
427
  // configura `config.sudo.methods`: a tela oferece exatamente isto, e os
343
428
  // handlers aceitam exatamente isto.
344
429
  setMountedSudoMethods(sudoMethodsToMount);
345
- // Organizations (multi-tenancy) — sempre montadas; controller retorna 404/403 sem tabelas.
346
- router.get('/account/orgs', [C.accountOrgs, 'index']);
347
- router.post('/account/orgs', [C.accountOrgs, 'store']);
348
- router.post('/account/orgs/deactivate', [C.accountOrgs, 'deactivate']);
349
- router.post('/account/orgs/:id/activate', [C.accountOrgs, 'activate']);
350
- router.post('/account/orgs/:id/leave', [C.accountOrgs, 'leave']);
351
- router.post('/account/orgs/:id/invite', [C.accountOrgs, 'invite']);
352
- router.post('/account/orgs/:id/members/:accountId/remove', [C.accountOrgs, 'removeMember']);
353
- router.post('/account/orgs/:id/invitations/:invId/revoke', [C.accountOrgs, 'revokeInvitation']);
354
- // JSON endpoints for React hooks (authkit-react).
355
- router.get('/account/orgs/json', [C.accountOrgs, 'listJson']);
356
- router.get('/account/orgs/invitations/json', [C.accountOrgs, 'listInvitationsJson']);
357
- router.get('/account/orgs/:id/json', [C.accountOrgs, 'showJson']);
430
+ // Organizations (multi-tenancy) — tela `orgs`. Montadas por default;
431
+ // controller retorna 404/403 sem tabelas (capability-probed).
432
+ if (mountOrgs) {
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
+ ]);
444
+ // JSON endpoints for React hooks (authkit-react).
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']);
448
+ }
358
449
  // ─── Account self-service JSON API (authkit-react TanStack hooks) ─────
359
450
  // ⚠️ ORDER MATTERS: fixed-segment routes before parameterised ones.
360
451
  // Registered INSIDE the accountGuard group → same session-auth protection.
361
452
  // Mutating routes use CSRF (shield middleware on the host app).
362
- router.get('/account/api/me', [C.accountApi, 'me']);
363
- router.get('/account/api/security', [C.accountApi, 'securityOverview']);
364
- router.patch('/account/api/profile', [C.accountApi, 'updateProfile']);
365
- 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']);
366
459
  // Email-change: cancel BEFORE the generic post to avoid pattern collision.
367
- router.post('/account/api/email-change/cancel', [C.accountApi, 'cancelEmailChange']);
368
- 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']);
369
462
  // Sessions: fixed routes BEFORE :id.
370
- router.get('/account/api/sessions', [C.accountApi, 'listSessions']);
371
- router.post('/account/api/sessions/revoke-others', [C.accountApi, 'revokeOtherSessions']);
372
- router.post('/account/api/sessions/revoke-all', [C.accountApi, 'revokeAllSessions']);
373
- 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']);
374
467
  // Apps (grants).
375
- router.get('/account/api/apps', [C.accountApi, 'listApps']);
376
- 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']);
377
470
  // MFA + passkeys.
378
- router.get('/account/api/mfa', [C.accountApi, 'mfaStatus']);
379
- router.get('/account/api/passkeys', [C.accountApi, 'listPasskeys']);
380
- 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']);
381
474
  // PATs.
382
- router.get('/account/api/tokens', [C.accountApi, 'listTokens']);
383
- router.post('/account/api/tokens', [C.accountApi, 'createToken']);
384
- 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']);
385
478
  // Orgs: invitations BEFORE :id (fixed segment before parameterised).
386
- router.get('/account/api/orgs', [C.accountApi, 'listOrgs']);
387
- router.get('/account/api/orgs/invitations', [C.accountApi, 'listOrgInvitations']);
388
- 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']);
389
482
  })
390
483
  .use([accountGuard]);
391
484
  // Console admin (do config.admin.enabled ou opts). Protegido pelo adminGuard (sessão + role global).
@@ -422,13 +515,19 @@ export function registerAuthHost(router, opts = {}) {
422
515
  router.post(`${ap}/api/sessions/revoke-all`, [C.consoleSessions, 'revokeAll']);
423
516
  // Sessões por usuário (drawer do user — ANTES do catch-all para não receber HTML).
424
517
  router.get(`${ap}/api/users/:id/sessions`, [C.consoleSessions, 'userSessions']);
425
- 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
+ ]);
426
522
  // Clients OIDC.
427
523
  router.get(`${ap}/api/clients`, [C.consoleClients, 'index']);
428
524
  router.post(`${ap}/api/clients`, [C.consoleClients, 'store']);
429
525
  router.patch(`${ap}/api/clients/:id`, [C.consoleClients, 'update']);
430
526
  router.delete(`${ap}/api/clients/:id`, [C.consoleClients, 'destroy']);
431
- 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
+ ]);
432
531
  // Roles.
433
532
  router.get(`${ap}/api/roles`, [C.consoleRoles, 'index']);
434
533
  router.post(`${ap}/api/roles`, [C.consoleRoles, 'store']);
@@ -447,7 +546,10 @@ export function registerAuthHost(router, opts = {}) {
447
546
  router.delete(`${ap}/api/orgs/:id/members/:accountId`, [C.consoleOrgs, 'removeMember']);
448
547
  // Convites.
449
548
  router.post(`${ap}/api/orgs/:id/invitations`, [C.consoleOrgs, 'createInvitation']);
450
- 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
+ ]);
451
553
  // Auditoria (capability-gated: 404 quando sink não suporta consulta).
452
554
  router.get(`${ap}/api/audit`, [C.consoleAudit, 'index']);
453
555
  // Settings.
@@ -515,7 +617,10 @@ export function registerAuthHost(router, opts = {}) {
515
617
  withApiThrottle(router.delete('/organizations/:id/members/:accountId', [C.apiOrgs, 'removeMember']));
516
618
  withApiThrottle(router.patch('/organizations/:id/members/:accountId', [C.apiOrgs, 'updateMemberRole']));
517
619
  withApiThrottle(router.post('/organizations/:id/invitations', [C.apiOrgs, 'createInvitation']));
518
- withApiThrottle(router.delete('/organizations/:id/invitations/:invitationId', [C.apiOrgs, 'revokeInvitation']));
620
+ withApiThrottle(router.delete('/organizations/:id/invitations/:invitationId', [
621
+ C.apiOrgs,
622
+ 'revokeInvitation',
623
+ ]));
519
624
  // Auditoria + métricas + verificação de token.
520
625
  withApiThrottle(router.get('/audit', [C.apiMisc, 'audit']));
521
626
  withApiThrottle(router.get('/stats', [C.apiMisc, 'stats']));
@@ -1,3 +1,5 @@
1
+ import { getAccountLoginUrl } from '../account_login_url.js';
2
+ import { accountPathsMap } from '../account_paths.js';
1
3
  import { DEFAULT_MESSAGES, translate } from '../i18n.js';
2
4
  /**
3
5
  * Resolve o catálogo de mensagens ativo a partir do `authkit.server` (config
@@ -28,7 +30,23 @@ async function resolveMessagesFromCtx(ctx) {
28
30
  export async function renderEdgeView(ctx, view, props) {
29
31
  const messages = await resolveMessagesFromCtx(ctx);
30
32
  const t = (key, params) => translate(messages, key, params);
31
- return ctx.view.render(`authkit::${view}`, { ...props, t, messages });
33
+ // `loginUrl` como prop global: as views que linkam "faça login" (ex.: `otp-unlock`)
34
+ // usam o destino configurável (`accountLoginUrl`) em vez do `/account/login` fixo,
35
+ // que pode estar desmontado. Props explícitas ainda têm precedência (spread depois).
36
+ const loginUrl = getAccountLoginUrl();
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
+ });
32
50
  }
33
51
  /** Renderer do seam para hosts Edge. As views são donas-da-lib (disco `authkit::`). */
34
52
  export function edgeRenderer() {
@@ -66,5 +66,68 @@ export type AuthkitScreen = 'login' | 'signup' | 'consent' | 'forgot' | 'reset'
66
66
  * | `returnTo` | `string \| null` | Caminho interno de destino pós-login (já validado pelo servidor — só caminhos internos). Quando presente, o formulário deve incluir `<input type="hidden" name="return_to" value={returnTo} />`. O servidor revalida o valor no POST; hosts com tela custom precisam propagar esse hidden input. |
67
67
  * | `error` | `string \| undefined` | Mensagem de erro de autenticação localizada (credenciais inválidas, conta bloqueada, etc.). |
68
68
  * | `messages` | `AuthMessages` | Catálogo de mensagens i18n. |
69
+ *
70
+ * ### Props da tela `account/security`
71
+ *
72
+ * | Prop | Tipo | Descrição |
73
+ * |-------------------------|---------------------|-----------|
74
+ * | `csrfToken` | `string` | Token CSRF para os formulários da tela. |
75
+ * | `supported` | `boolean` | `false` quando o store não suporta o self-service de segurança (troca de senha/e-mail) — a tela deve degradar. |
76
+ * | `profileSupported` | `boolean` | `true` quando o store suporta editar nome/avatar (`updateProfile`). |
77
+ * | `avatarUploadSupported` | `boolean` | `true` quando algum backend (drive OU media) pode armazenar o upload de avatar. |
78
+ * | `email` | `string` | E-mail atual da conta (`''` se ausente). |
79
+ * | `name` | `string` | Nome atual da conta (`''` se ausente). |
80
+ * | `avatarUrl` | `string` | URL do avatar atual (`''` se ausente). |
81
+ * | `passwordChanged` | `string \| null` | Flash de sucesso da troca de senha (mensagem localizada) ou `null`. |
82
+ * | `emailChangeRequested` | `string \| null` | Flash: link de confirmação de troca de e-mail enviado (ou cancelamento) ou `null`. |
83
+ * | `emailChanged` | `string \| null` | Flash: troca de e-mail concluída ou `null`. |
84
+ * | `profileUpdated` | `string \| null` | Flash: perfil atualizado ou `null`. |
85
+ * | `error` | `string \| null` | Flash de erro de segurança (senha inválida, e-mail em uso, política violada) ou `null`. |
86
+ * | `trustedDevicesEnabled` | `boolean` | `true` quando o recurso de dispositivos confiáveis está ligado. |
87
+ * | `trustedDevicesRevoked` | `string \| null` | Flash: confiança deste navegador revogada ou `null`. |
88
+ * | `sessionsSupported` | `boolean` | `true` quando o adapter OIDC enumera as sessões ativas da conta. |
89
+ * | `sessions` | `Array<{ loginTs: string; browser: string; os: string; ip: string; location: string }>` | Sessões ativas da própria conta (vazio quando não suportado). `loginTs` é ISO ou `''`. |
90
+ * | `exportSupported` | `boolean` | Sempre `true` — export de dados (portabilidade/LGPD) disponível para a conta logada. |
91
+ * | `deletionSupported` | `boolean` | `true` quando o store suporta hard delete (danger zone). |
92
+ * | `deleteError` | `string \| null` | Flash de erro da confirmação de deleção ou `null`. |
93
+ * | `messages` | `AuthMessages` | Catálogo de mensagens i18n. |
94
+ *
95
+ * ### Props da tela `account/mfa`
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
+ *
101
+ * | Prop | Tipo | Descrição |
102
+ * |---------------------|---------------------|-----------|
103
+ * | `csrfToken` | `string` | Token CSRF para os formulários de enroll/confirm/disable e passkeys. |
104
+ * | `enabled` | `boolean` | `true` quando o TOTP já está confirmado (habilitado) para a conta. |
105
+ * | `recoveryCodes` | `string[] \| null` | Códigos de recuperação recém-gerados (exibidos UMA vez após enroll/confirm) ou `null`. |
106
+ * | `passkeysSupported` | `boolean` | `true` quando o store persiste credenciais WebAuthn (passkeys). |
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. |
112
+ * | `messages` | `AuthMessages` | Catálogo de mensagens i18n. |
113
+ *
114
+ * ### Props da tela `account/confirm` (sudo — confirmar identidade)
115
+ *
116
+ * | Prop | Tipo | Descrição |
117
+ * |---------------|---------------------|-----------|
118
+ * | `csrfToken` | `string` | Token CSRF para o POST de cada método. |
119
+ * | `returnTo` | `string \| null` | Caminho interno de destino após confirmar (validado pelo servidor) ou `null`. |
120
+ * | `error` | `string \| null` | Flash de erro da última tentativa de confirmação ou `null`. |
121
+ * | `notice` | `string \| null` | Flash informativo (ex.: "link de confirmação enviado") ou `null`. |
122
+ * | `methods` | `Array<{ id: string; labelKey: string; kind: 'form' \| 'action' \| 'redirect' \| 'webauthn'; endpoint: string; fields?: Array<{ name: string; type: 'password' \| 'text'; labelKey: string }> }>` | Métodos de sudo disponíveis para a conta. A tela renderiza por `kind`; `endpoint` é o POST de verificação (`webauthn` pede options em `${endpoint}/options`). |
123
+ * | `preferredId` | `string \| null` | `id` do último método usado (destaque na UI) ou `null`. |
124
+ * | `messages` | `AuthMessages` | Catálogo de mensagens i18n. |
125
+ *
126
+ * ### Props da tela `account/email-confirmed` (terminal do link de troca de e-mail)
127
+ *
128
+ * | Prop | Tipo | Descrição |
129
+ * |------------|----------------|-----------|
130
+ * | `ok` | `boolean` | `true` quando o token era válido e o novo e-mail foi aplicado; `false` para token inválido/expirado ou store sem suporte. A tela mostra sucesso ou falha conforme o valor. |
131
+ * | `messages` | `AuthMessages` | Catálogo de mensagens i18n. |
69
132
  */
70
133
  export declare function inertiaRenderer(opts: InertiaRendererOptions): (ctx: HttpContext, view: string, props: Record<string, unknown>) => Promise<any>;
@@ -37,6 +37,69 @@ async function resolveMessagesFromCtx(ctx) {
37
37
  * | `returnTo` | `string \| null` | Caminho interno de destino pós-login (já validado pelo servidor — só caminhos internos). Quando presente, o formulário deve incluir `<input type="hidden" name="return_to" value={returnTo} />`. O servidor revalida o valor no POST; hosts com tela custom precisam propagar esse hidden input. |
38
38
  * | `error` | `string \| undefined` | Mensagem de erro de autenticação localizada (credenciais inválidas, conta bloqueada, etc.). |
39
39
  * | `messages` | `AuthMessages` | Catálogo de mensagens i18n. |
40
+ *
41
+ * ### Props da tela `account/security`
42
+ *
43
+ * | Prop | Tipo | Descrição |
44
+ * |-------------------------|---------------------|-----------|
45
+ * | `csrfToken` | `string` | Token CSRF para os formulários da tela. |
46
+ * | `supported` | `boolean` | `false` quando o store não suporta o self-service de segurança (troca de senha/e-mail) — a tela deve degradar. |
47
+ * | `profileSupported` | `boolean` | `true` quando o store suporta editar nome/avatar (`updateProfile`). |
48
+ * | `avatarUploadSupported` | `boolean` | `true` quando algum backend (drive OU media) pode armazenar o upload de avatar. |
49
+ * | `email` | `string` | E-mail atual da conta (`''` se ausente). |
50
+ * | `name` | `string` | Nome atual da conta (`''` se ausente). |
51
+ * | `avatarUrl` | `string` | URL do avatar atual (`''` se ausente). |
52
+ * | `passwordChanged` | `string \| null` | Flash de sucesso da troca de senha (mensagem localizada) ou `null`. |
53
+ * | `emailChangeRequested` | `string \| null` | Flash: link de confirmação de troca de e-mail enviado (ou cancelamento) ou `null`. |
54
+ * | `emailChanged` | `string \| null` | Flash: troca de e-mail concluída ou `null`. |
55
+ * | `profileUpdated` | `string \| null` | Flash: perfil atualizado ou `null`. |
56
+ * | `error` | `string \| null` | Flash de erro de segurança (senha inválida, e-mail em uso, política violada) ou `null`. |
57
+ * | `trustedDevicesEnabled` | `boolean` | `true` quando o recurso de dispositivos confiáveis está ligado. |
58
+ * | `trustedDevicesRevoked` | `string \| null` | Flash: confiança deste navegador revogada ou `null`. |
59
+ * | `sessionsSupported` | `boolean` | `true` quando o adapter OIDC enumera as sessões ativas da conta. |
60
+ * | `sessions` | `Array<{ loginTs: string; browser: string; os: string; ip: string; location: string }>` | Sessões ativas da própria conta (vazio quando não suportado). `loginTs` é ISO ou `''`. |
61
+ * | `exportSupported` | `boolean` | Sempre `true` — export de dados (portabilidade/LGPD) disponível para a conta logada. |
62
+ * | `deletionSupported` | `boolean` | `true` quando o store suporta hard delete (danger zone). |
63
+ * | `deleteError` | `string \| null` | Flash de erro da confirmação de deleção ou `null`. |
64
+ * | `messages` | `AuthMessages` | Catálogo de mensagens i18n. |
65
+ *
66
+ * ### Props da tela `account/mfa`
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
+ *
72
+ * | Prop | Tipo | Descrição |
73
+ * |---------------------|---------------------|-----------|
74
+ * | `csrfToken` | `string` | Token CSRF para os formulários de enroll/confirm/disable e passkeys. |
75
+ * | `enabled` | `boolean` | `true` quando o TOTP já está confirmado (habilitado) para a conta. |
76
+ * | `recoveryCodes` | `string[] \| null` | Códigos de recuperação recém-gerados (exibidos UMA vez após enroll/confirm) ou `null`. |
77
+ * | `passkeysSupported` | `boolean` | `true` quando o store persiste credenciais WebAuthn (passkeys). |
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. |
83
+ * | `messages` | `AuthMessages` | Catálogo de mensagens i18n. |
84
+ *
85
+ * ### Props da tela `account/confirm` (sudo — confirmar identidade)
86
+ *
87
+ * | Prop | Tipo | Descrição |
88
+ * |---------------|---------------------|-----------|
89
+ * | `csrfToken` | `string` | Token CSRF para o POST de cada método. |
90
+ * | `returnTo` | `string \| null` | Caminho interno de destino após confirmar (validado pelo servidor) ou `null`. |
91
+ * | `error` | `string \| null` | Flash de erro da última tentativa de confirmação ou `null`. |
92
+ * | `notice` | `string \| null` | Flash informativo (ex.: "link de confirmação enviado") ou `null`. |
93
+ * | `methods` | `Array<{ id: string; labelKey: string; kind: 'form' \| 'action' \| 'redirect' \| 'webauthn'; endpoint: string; fields?: Array<{ name: string; type: 'password' \| 'text'; labelKey: string }> }>` | Métodos de sudo disponíveis para a conta. A tela renderiza por `kind`; `endpoint` é o POST de verificação (`webauthn` pede options em `${endpoint}/options`). |
94
+ * | `preferredId` | `string \| null` | `id` do último método usado (destaque na UI) ou `null`. |
95
+ * | `messages` | `AuthMessages` | Catálogo de mensagens i18n. |
96
+ *
97
+ * ### Props da tela `account/email-confirmed` (terminal do link de troca de e-mail)
98
+ *
99
+ * | Prop | Tipo | Descrição |
100
+ * |------------|----------------|-----------|
101
+ * | `ok` | `boolean` | `true` quando o token era válido e o novo e-mail foi aplicado; `false` para token inválido/expirado ou store sem suporte. A tela mostra sucesso ou falha conforme o valor. |
102
+ * | `messages` | `AuthMessages` | Catálogo de mensagens i18n. |
40
103
  */
41
104
  export function inertiaRenderer(opts) {
42
105
  const allowed = opts.views ? new Set(opts.views) : null;
@@ -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
  *