@adonis-agora/authkit-server 0.47.0 → 0.49.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.
- package/build/commands/clients_create.js +4 -4
- package/build/commands/configure.js +3 -2
- package/build/commands/doctor.js +8 -5
- package/build/commands/eject.js +1 -1
- package/build/commands/expire_scan.js +7 -5
- package/build/commands/import_users.js +1 -1
- package/build/commands/keys_rotate.js +7 -4
- package/build/host/views/account/apps.edge +5 -5
- package/build/host/views/account/login.edge +1 -1
- package/build/host/views/account/mfa.edge +7 -7
- package/build/host/views/account/orgs.edge +5 -5
- package/build/host/views/account/security.edge +10 -10
- package/build/host/views/account/tokens.edge +5 -5
- package/build/index.d.ts +22 -7
- package/build/index.js +18 -5
- package/build/providers/authkit_server_provider.d.ts +2 -2
- package/build/providers/authkit_server_provider.js +20 -11
- package/build/services/main.d.ts +1 -1
- package/build/services/main.js +2 -2
- package/build/src/accounts/lucid_account_store.d.ts +3 -3
- package/build/src/accounts/lucid_account_store.js +6 -8
- package/build/src/accounts/lucid_store/core.js +1 -1
- package/build/src/accounts/lucid_store/mfa.js +1 -1
- package/build/src/accounts/lucid_store/organizations.js +2 -4
- package/build/src/accounts/lucid_store/password_hygiene.js +1 -4
- package/build/src/accounts/lucid_store/shared.d.ts +1 -1
- package/build/src/accounts/lucid_stores.d.ts +3 -3
- package/build/src/accounts/lucid_stores.js +2 -2
- package/build/src/adapters/database_adapter.js +3 -1
- package/build/src/adapters/factory.js +1 -1
- package/build/src/adapters/redis_adapter.js +2 -1
- package/build/src/commands/expire_scan_command.js +19 -5
- package/build/src/commands/settings_commands.js +22 -5
- package/build/src/define_config.d.ts +20 -20
- package/build/src/define_config.js +62 -67
- package/build/src/doctor/checks.js +45 -20
- package/build/src/events/dispatcher.d.ts +1 -1
- package/build/src/events/dispatcher.js +10 -10
- package/build/src/host/account_api/account_api_controller.js +95 -39
- package/build/src/host/account_deletion_ops.d.ts +4 -4
- package/build/src/host/account_deletion_ops.js +5 -5
- package/build/src/host/account_deletion_service.d.ts +2 -2
- package/build/src/host/account_deletion_service.js +2 -2
- package/build/src/host/account_export_service.d.ts +1 -1
- package/build/src/host/account_export_service.js +4 -6
- package/build/src/host/account_home.d.ts +0 -8
- package/build/src/host/account_home.js +6 -4
- package/build/src/host/account_login_url.d.ts +6 -3
- package/build/src/host/account_login_url.js +16 -8
- package/build/src/host/account_paths.d.ts +120 -0
- package/build/src/host/account_paths.js +161 -0
- package/build/src/host/account_screen_props.d.ts +163 -0
- package/build/src/host/account_screen_props.js +23 -0
- package/build/src/host/admin_api/admin_orgs_service.d.ts +2 -2
- package/build/src/host/admin_api/admin_orgs_service.js +3 -2
- package/build/src/host/admin_api/admin_users_service.d.ts +12 -12
- package/build/src/host/admin_api/admin_users_service.js +20 -20
- package/build/src/host/admin_api/api_clients_controller.js +2 -2
- package/build/src/host/admin_api/api_misc_controller.js +2 -2
- package/build/src/host/admin_api/api_orgs_controller.js +3 -3
- package/build/src/host/admin_api/api_settings_controller.js +11 -5
- package/build/src/host/admin_api/api_users_controller.d.ts +2 -2
- package/build/src/host/admin_api/api_users_controller.js +37 -38
- package/build/src/host/admin_api/dto.d.ts +4 -4
- package/build/src/host/admin_clients_service.js +5 -4
- package/build/src/host/admin_console/admin_shell_controller.js +4 -2
- package/build/src/host/admin_console/console_audit_controller.js +1 -1
- package/build/src/host/admin_console/console_clients_controller.js +5 -3
- package/build/src/host/admin_console/console_impersonation_controller.js +1 -1
- package/build/src/host/admin_console/console_keys_controller.js +2 -4
- package/build/src/host/admin_console/console_orgs_controller.js +3 -3
- package/build/src/host/admin_console/console_roles_controller.js +30 -12
- package/build/src/host/admin_console/console_sessions_controller.js +3 -3
- package/build/src/host/admin_console/console_settings_controller.js +11 -5
- package/build/src/host/admin_console/console_users_controller.d.ts +2 -2
- package/build/src/host/admin_console/console_users_controller.js +46 -47
- package/build/src/host/admin_prefix.js +2 -2
- package/build/src/host/admin_sessions_service.js +1 -3
- package/build/src/host/adonis_auth_sync.d.ts +1 -1
- package/build/src/host/adonis_auth_user_provider.d.ts +1 -1
- package/build/src/host/app_key.js +1 -1
- package/build/src/host/avatar_storage.js +1 -3
- package/build/src/host/config_locks.js +1 -3
- package/build/src/host/console_session.js +1 -1
- package/build/src/host/controllers/account_apps_controller.js +3 -2
- package/build/src/host/controllers/account_confirm_controller.js +5 -8
- package/build/src/host/controllers/account_mfa_controller.js +72 -18
- package/build/src/host/controllers/account_orgs_controller.js +47 -27
- package/build/src/host/controllers/account_security_controller.d.ts +2 -2
- package/build/src/host/controllers/account_security_controller.js +116 -116
- package/build/src/host/controllers/account_session_controller.js +13 -8
- package/build/src/host/controllers/account_tokens_controller.js +3 -2
- package/build/src/host/controllers/interaction_controller.js +59 -22
- package/build/src/host/controllers/registration_controller.js +28 -13
- package/build/src/host/default_mailer.js +11 -7
- package/build/src/host/durable/account_deletion_workflow.d.ts +2 -2
- package/build/src/host/durable/account_deletion_workflow.js +14 -14
- package/build/src/host/durable/account_export_workflow.d.ts +2 -2
- package/build/src/host/durable/account_export_workflow.js +12 -12
- package/build/src/host/durable/index.d.ts +5 -5
- package/build/src/host/durable/index.js +6 -6
- package/build/src/host/i18n.d.ts +1376 -1376
- package/build/src/host/i18n.js +1380 -1380
- package/build/src/host/impersonation.js +1 -7
- package/build/src/host/key_rotation_actions.d.ts +1 -1
- package/build/src/host/login_attempt.js +35 -6
- package/build/src/host/login_notify.js +1 -1
- package/build/src/host/otp_lockout.js +1 -3
- package/build/src/host/rate_limit.js +1 -1
- package/build/src/host/register_auth_host.d.ts +30 -0
- package/build/src/host/register_auth_host.js +120 -74
- package/build/src/host/renderers/edge_renderer.js +15 -2
- package/build/src/host/renderers/inertia_renderer.d.ts +23 -58
- package/build/src/host/renderers/inertia_renderer.js +23 -58
- package/build/src/host/runtime_settings.js +1 -1
- package/build/src/host/runtime_toggles.d.ts +3 -3
- package/build/src/host/runtime_toggles.js +69 -24
- package/build/src/host/security_notice_service.d.ts +1 -1
- package/build/src/host/security_notice_service.js +1 -1
- package/build/src/host/session_context.js +1 -1
- package/build/src/host/sudo/index.d.ts +3 -3
- package/build/src/host/sudo/index.js +3 -3
- package/build/src/host/sudo/methods/magic_link.js +6 -5
- package/build/src/host/sudo/methods/passkey.js +7 -4
- package/build/src/host/sudo/methods/password.js +8 -3
- package/build/src/host/sudo/runtime.js +6 -13
- package/build/src/host/sudo_mode.js +3 -2
- package/build/src/host/ui-dist/assets/index-BQoTDiLV.js +137 -0
- package/build/src/host/ui-dist/index.html +1 -1
- package/build/src/keys/jwks_manager.js +1 -1
- package/build/src/keys/keystore.js +1 -1
- package/build/src/keys/keystore_manager.d.ts +2 -2
- package/build/src/keys/keystore_manager.js +2 -2
- package/build/src/keys/keystore_vault.js +8 -4
- package/build/src/mixins/with_audit_log.d.ts +1 -1
- package/build/src/mixins/with_auth_user.d.ts +1 -1
- package/build/src/mixins/with_auth_user.js +1 -1
- package/build/src/mixins/with_credentials.d.ts +1 -1
- package/build/src/mixins/with_mfa.d.ts +1 -1
- package/build/src/mixins/with_personal_access_token.d.ts +1 -1
- package/build/src/mixins/with_provider_identity.d.ts +1 -1
- package/build/src/mixins/with_webauthn_credential.d.ts +1 -1
- package/build/src/observability/diagnostics_bridge.js +2 -2
- package/build/src/observability/metrics_service.js +1 -1
- package/build/src/observability/telescope/data_providers.d.ts +1 -1
- package/build/src/observability/telescope/data_providers.js +29 -43
- package/build/src/observability/telescope/extension.d.ts +1 -1
- package/build/src/observability/telescope/extension.js +58 -58
- package/build/src/observability/telescope/index.d.ts +2 -2
- package/build/src/observability/telescope/index.js +2 -2
- package/build/src/observability/wire_provider_events.d.ts +1 -1
- package/build/src/observability/wire_provider_events.js +11 -11
- package/build/src/password/common_passwords.js +1 -1
- package/build/src/password/password_manager.d.ts +1 -1
- package/build/src/password/password_manager.js +4 -4
- package/build/src/pat/lucid_pat_store.js +2 -6
- package/build/src/pat/pat_tokens.js +1 -1
- package/build/src/provider/interaction_actions.js +1 -1
- package/build/src/provider/key_rotation_scheduler.js +3 -1
- package/build/src/provider/logout_sources.js +2 -8
- package/build/src/provider/oidc_service.d.ts +2 -2
- package/build/src/provider/oidc_service.js +22 -14
- package/build/src/provider/single_flight_lock.js +1 -3
- package/build/src/provider/token_exchange.d.ts +1 -1
- package/build/src/schema/ensure.js +3 -1
- package/build/stubs/ui/react/components/auth_shell.tsx +19 -19
- package/build/stubs/ui/react/pages/account/login.tsx +6 -7
- package/build/stubs/ui/react/pages/account/mfa.tsx +16 -15
- package/build/stubs/ui/react/pages/account/tokens.tsx +27 -17
- package/build/stubs/ui/react/pages/consent.tsx +12 -12
- package/build/stubs/ui/react/pages/forgot.tsx +7 -7
- package/build/stubs/ui/react/pages/login.tsx +22 -24
- package/build/stubs/ui/react/pages/mfa-challenge.tsx +9 -10
- package/build/stubs/ui/react/pages/reset.tsx +10 -10
- package/build/stubs/ui/react/pages/signup.tsx +30 -16
- package/build/stubs/ui/react/pages/verify-email.tsx +6 -6
- package/package.json +2 -2
- package/stubs/main.ts +2 -2
- package/stubs/ui/react/components/auth_shell.tsx +19 -19
- package/stubs/ui/react/pages/account/login.tsx +6 -7
- package/stubs/ui/react/pages/account/mfa.tsx +16 -15
- package/stubs/ui/react/pages/account/tokens.tsx +27 -17
- package/stubs/ui/react/pages/consent.tsx +12 -12
- package/stubs/ui/react/pages/forgot.tsx +7 -7
- package/stubs/ui/react/pages/login.tsx +22 -24
- package/stubs/ui/react/pages/mfa-challenge.tsx +9 -10
- package/stubs/ui/react/pages/reset.tsx +10 -10
- package/stubs/ui/react/pages/signup.tsx +30 -16
- package/stubs/ui/react/pages/verify-email.tsx +6 -6
- 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 {
|
|
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({
|
|
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
|
-
? {
|
|
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
|
-
? {
|
|
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
|
-
? {
|
|
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
|
-
? {
|
|
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 {
|
|
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
|
|
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 {
|
|
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
|
|
207
|
-
//
|
|
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', [
|
|
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(
|
|
300
|
-
router.post(
|
|
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(
|
|
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(
|
|
310
|
-
withLogin(router.post(
|
|
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(
|
|
345
|
+
router.get(emailConfirmPath, [C.accountSecurity, 'confirmEmail']);
|
|
317
346
|
}
|
|
318
|
-
// Rotas de tokens protegidas por AccountAuthMiddleware (redireciona para
|
|
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(
|
|
324
|
-
router.post(
|
|
325
|
-
router.post(
|
|
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(
|
|
330
|
-
router.post(
|
|
331
|
-
router.post(
|
|
332
|
-
router.post(
|
|
333
|
-
router.post(
|
|
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(
|
|
337
|
-
router.post(
|
|
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(
|
|
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(
|
|
347
|
-
router.post(
|
|
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(
|
|
353
|
-
router.post(
|
|
354
|
-
router.post(
|
|
355
|
-
router.post(
|
|
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(
|
|
358
|
-
router.post(
|
|
359
|
-
router.post(
|
|
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(
|
|
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(
|
|
405
|
-
router.post(
|
|
406
|
-
router.post(
|
|
407
|
-
router.post(
|
|
408
|
-
router.post(
|
|
409
|
-
router.post(
|
|
410
|
-
router.post(
|
|
411
|
-
router.post(
|
|
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(
|
|
414
|
-
router.get(
|
|
415
|
-
router.get(
|
|
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
|
-
|
|
422
|
-
|
|
423
|
-
router.
|
|
424
|
-
router.
|
|
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(
|
|
427
|
-
router.post(
|
|
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(
|
|
430
|
-
router.post(
|
|
431
|
-
router.post(
|
|
432
|
-
router.delete(
|
|
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(
|
|
435
|
-
router.delete(
|
|
468
|
+
router.get(`${apiBase}/apps`, [C.accountApi, 'listApps']);
|
|
469
|
+
router.delete(`${apiBase}/apps/:clientId`, [C.accountApi, 'revokeApp']);
|
|
436
470
|
// MFA + passkeys.
|
|
437
|
-
router.get(
|
|
438
|
-
router.get(
|
|
439
|
-
router.delete(
|
|
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(
|
|
442
|
-
router.post(
|
|
443
|
-
router.delete(
|
|
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(
|
|
446
|
-
router.get(
|
|
447
|
-
router.get(
|
|
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`, [
|
|
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`, [
|
|
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`, [
|
|
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', [
|
|
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
|
-
|
|
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() {
|