@adonis-agora/authkit-server 0.54.0 → 0.55.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/index.d.ts +4 -2
- package/build/index.js +2 -1
- package/build/providers/authkit_server_provider.js +18 -0
- package/build/src/accounts/lucid_account_store.d.ts +35 -0
- package/build/src/accounts/lucid_account_store.js +9 -0
- package/build/src/accounts/lucid_store/core.js +88 -19
- package/build/src/accounts/lucid_store/shared.d.ts +14 -0
- package/build/src/accounts/lucid_store/token_hash.d.ts +79 -0
- package/build/src/accounts/lucid_store/token_hash.js +145 -0
- package/build/src/define_config.d.ts +93 -8
- package/build/src/define_config.js +28 -2
- package/build/src/host/account_api/account_api_controller.js +5 -4
- package/build/src/host/admin_api/admin_users_service.js +2 -1
- package/build/src/host/admin_api/api_orgs_controller.js +2 -1
- package/build/src/host/admin_console/console_impersonation_controller.d.ts +15 -1
- package/build/src/host/admin_console/console_impersonation_controller.js +26 -2
- package/build/src/host/admin_console/console_orgs_controller.js +3 -1
- package/build/src/host/admin_validators.d.ts +3 -3
- package/build/src/host/auth_host_config.d.ts +30 -0
- package/build/src/host/auth_host_config.js +15 -0
- package/build/src/host/config_locks.d.ts +29 -0
- package/build/src/host/config_locks.js +46 -0
- package/build/src/host/console_session.d.ts +38 -2
- package/build/src/host/console_session.js +46 -2
- package/build/src/host/controllers/account_mfa_controller.js +5 -5
- package/build/src/host/controllers/account_orgs_controller.js +2 -1
- package/build/src/host/controllers/account_security_controller.js +6 -5
- package/build/src/host/controllers/interaction_controller.js +122 -26
- package/build/src/host/controllers/registration_controller.js +5 -4
- package/build/src/host/controllers/social_controller.js +37 -0
- package/build/src/host/default_mailer.d.ts +36 -5
- package/build/src/host/default_mailer.js +63 -10
- package/build/src/host/i18n.d.ts +10 -0
- package/build/src/host/i18n.js +16 -0
- package/build/src/host/login_attempt.d.ts +88 -2
- package/build/src/host/login_attempt.js +101 -48
- package/build/src/host/login_notify.js +2 -2
- package/build/src/host/oidc_rp_guard.d.ts +25 -2
- package/build/src/host/oidc_rp_guard.js +55 -9
- package/build/src/host/origin.d.ts +21 -0
- package/build/src/host/origin.js +22 -0
- package/build/src/host/register_auth_host.d.ts +104 -3
- package/build/src/host/register_auth_host.js +222 -26
- package/build/src/host/runtime_settings.d.ts +16 -0
- package/build/src/host/runtime_settings.js +24 -0
- package/build/src/host/runtime_toggles.d.ts +10 -0
- package/build/src/host/runtime_toggles.js +4 -0
- package/build/src/host/security_notice_service.d.ts +4 -2
- package/build/src/host/security_notice_service.js +4 -2
- package/build/src/host/sudo/index.d.ts +8 -0
- package/build/src/host/sudo/index.js +8 -0
- package/build/src/host/sudo/methods/magic_link.d.ts +17 -3
- package/build/src/host/sudo/methods/magic_link.js +37 -13
- package/build/src/host/sudo/runtime.d.ts +44 -4
- package/build/src/host/sudo/runtime.js +90 -6
- package/build/src/host/sudo/satisfiability.d.ts +62 -0
- package/build/src/host/sudo/satisfiability.js +89 -0
- package/build/src/password/common_passwords.js +27 -7
- package/build/src/provider/oidc_service.js +25 -13
- package/build/src/provider/token_exchange.d.ts +12 -1
- package/build/src/provider/token_exchange.js +12 -0
- package/package.json +6 -3
- /package/build/{password → src/password}/common_passwords.txt +0 -0
|
@@ -6,9 +6,11 @@ import type { AuditSink } from './audit/audit_sink.js';
|
|
|
6
6
|
import { type EventsConfigInput } from './events/dispatcher.js';
|
|
7
7
|
import { type BotProtectionConfigInput, type ResolvedBotProtectionConfig } from './host/bot_protection.js';
|
|
8
8
|
import type { BrandingConfig } from './host/branding.js';
|
|
9
|
+
import { type PolicyRouteOption } from './host/config_locks.js';
|
|
9
10
|
import type { ResolveGeo } from './host/geo.js';
|
|
10
11
|
import { type AuthMessages, type I18nConfig } from './host/i18n.js';
|
|
11
12
|
import { type OtpLoginConfigInput, type ResolvedOtpLoginConfig } from './host/otp_login.js';
|
|
13
|
+
import type { AuthHostOptions } from './host/register_auth_host.js';
|
|
12
14
|
import type { SudoMethod } from './host/sudo/types.js';
|
|
13
15
|
import { type ResolvedTrustedDevicesConfig, type TrustedDevicesConfigInput } from './host/trusted_device.js';
|
|
14
16
|
import type { PatStore } from './pat/pat_store.js';
|
|
@@ -58,7 +60,13 @@ export interface MailHooks {
|
|
|
58
60
|
/**
|
|
59
61
|
* Envia o link de CONFIRMAÇÃO DE IDENTIDADE (sudo). Distinto de
|
|
60
62
|
* `onMagicLink`: aquele autentica, este só concede sudo a quem já está
|
|
61
|
-
* logado.
|
|
63
|
+
* logado.
|
|
64
|
+
*
|
|
65
|
+
* OPCIONAL, como todo hook de e-mail daqui: sem ele o próprio host-kit envia
|
|
66
|
+
* pelo mailer default (`@adonisjs/mail`), com branding e i18n. Antes a
|
|
67
|
+
* ausência deste hook tornava `sudoMethods.magicLink()` INDISPONÍVEL, e num
|
|
68
|
+
* host passwordless isso fechava o deadlock de sudo — o único método sem
|
|
69
|
+
* credencial prévia dependia de alguém ter escrito um hook.
|
|
62
70
|
*/
|
|
63
71
|
onSudoLink?: (data: {
|
|
64
72
|
email: string;
|
|
@@ -154,6 +162,20 @@ export interface MailHooks {
|
|
|
154
162
|
address: string;
|
|
155
163
|
name?: string;
|
|
156
164
|
};
|
|
165
|
+
/**
|
|
166
|
+
* Escape hatch: origem (`scheme://host[:port]`) usada para montar os links
|
|
167
|
+
* EMAILADOS (reset de senha, magic link, desbloqueio OTP, convites de
|
|
168
|
+
* organização, verificação/troca de e-mail, avisos de segurança), no lugar
|
|
169
|
+
* do `issuer` resolvido.
|
|
170
|
+
*
|
|
171
|
+
* Por default (campo ausente) os links usam `new URL(issuer).origin` — NUNCA
|
|
172
|
+
* `request.host()` / `X-Forwarded-Host`, que são client-supplied e abrem
|
|
173
|
+
* "password-reset poisoning" (ver `host/origin.ts`). Use este campo apenas
|
|
174
|
+
* quando o mesmo issuer é servido legitimamente sob MÚLTIPLOS hostnames
|
|
175
|
+
* públicos e os links devem seguir um hostname fixo diferente do issuer —
|
|
176
|
+
* o comportamento normal (issuer único) não precisa disto.
|
|
177
|
+
*/
|
|
178
|
+
origin?: string;
|
|
157
179
|
}
|
|
158
180
|
/** Bucket de rate-limit: pontos (requests) permitidos por janela de duração. */
|
|
159
181
|
export interface RateLimitBucket {
|
|
@@ -529,6 +551,30 @@ export interface AdminConfigInput {
|
|
|
529
551
|
enabled: boolean;
|
|
530
552
|
/** Roles globais que dão acesso ao /admin. Default: ['ADMIN']. */
|
|
531
553
|
roles?: string[];
|
|
554
|
+
/**
|
|
555
|
+
* Interruptor de impersonation. Governa as DUAS superfícies:
|
|
556
|
+
*
|
|
557
|
+
* 1. o grant RFC 8693 `urn:ietf:params:oauth:grant-type:token-exchange`
|
|
558
|
+
* registrado no provider OIDC (`src/provider/oidc_service.ts`) — é por
|
|
559
|
+
* ele que um admin troca o próprio access token pelo de outra conta;
|
|
560
|
+
* 2. o painel de impersonation do console admin
|
|
561
|
+
* (`GET {prefix}/api/impersonation/:userId`).
|
|
562
|
+
*
|
|
563
|
+
* `false` desliga as duas: o grant deixa de existir no provider (o token
|
|
564
|
+
* endpoint passa a responder `unsupported_grant_type`) e o painel responde
|
|
565
|
+
* 404. É um kill switch de verdade, não só um esconde-UI.
|
|
566
|
+
*
|
|
567
|
+
* Default: **`true`** — back-compat. O grant sempre foi registrado
|
|
568
|
+
* incondicionalmente e há hosts que o consomem a partir do PRÓPRIO /admin,
|
|
569
|
+
* sem montar o console desta lib; um default `false` os quebraria em runtime,
|
|
570
|
+
* em silêncio. Virar o default para `false` (secure-by-default) é decisão de
|
|
571
|
+
* major.
|
|
572
|
+
*
|
|
573
|
+
* Declarar esta chave também TRAVA a setting de runtime `admin_impersonation`
|
|
574
|
+
* (ver `src/host/config_locks.ts`): o arquivo passa a mandar e a UI/Admin API
|
|
575
|
+
* não altera mais o valor.
|
|
576
|
+
*/
|
|
577
|
+
impersonation?: boolean;
|
|
532
578
|
}
|
|
533
579
|
export interface ResolvedAdminConfig {
|
|
534
580
|
enabled: boolean;
|
|
@@ -681,6 +727,33 @@ export interface AuthServerConfigInput {
|
|
|
681
727
|
patStore?: PatStore;
|
|
682
728
|
/** Caminho base onde o host-kit monta as rotas OIDC. Default: '/oidc'. */
|
|
683
729
|
mountPath?: string;
|
|
730
|
+
/**
|
|
731
|
+
* Montagem AUTOMÁTICA das rotas do host-kit, sem `registerAuthHost` no
|
|
732
|
+
* `start/routes.ts`.
|
|
733
|
+
*
|
|
734
|
+
* - ausente (default) → NÃO auto-monta. O app chama `registerAuthHost(router)`
|
|
735
|
+
* no `start/routes.ts`, como sempre (back-compat).
|
|
736
|
+
* - `true` → o provider monta as rotas no boot chamando `registerAuthHost` —
|
|
737
|
+
* literalmente a mesma função, não uma segunda implementação.
|
|
738
|
+
* - objeto → auto-monta usando estes valores como DEFAULTS estruturais.
|
|
739
|
+
* - `false` → não auto-monta, explicitamente (o kill switch documentado).
|
|
740
|
+
*
|
|
741
|
+
* ⚠️ Auto-montar E chamar `registerAuthHost` no `start/routes.ts` é duplo
|
|
742
|
+
* registro: a chamada manual LANÇA com a instrução de qual dos dois remover
|
|
743
|
+
* (duas rotas com o mesmo nome derrubam o boot do AdonisJS).
|
|
744
|
+
*
|
|
745
|
+
* ⚠️ A auto-montagem roda no `boot()` do provider, ANTES do `start/routes.ts`
|
|
746
|
+
* — as rotas do authkit ficam registradas primeiro, e os wildcards
|
|
747
|
+
* (`${mountPath}/*`, `${adminPrefix}/*`) casam antes de rotas do app com
|
|
748
|
+
* padrão sobreposto. Quem precisa da ordem inversa continua chamando
|
|
749
|
+
* `registerAuthHost` na posição que quiser.
|
|
750
|
+
*
|
|
751
|
+
* As chaves de POLÍTICA aqui dentro (`social`, `rateLimit`, `sudoMethods`,
|
|
752
|
+
* `admin`, `adminApi`) são redundantes: elas já vêm dos campos de topo do
|
|
753
|
+
* config e são travadas por eles. Use este objeto para o ESTRUTURAL —
|
|
754
|
+
* `accountRoutes`, `account`, `accountLoginUrl`, `mountPath`.
|
|
755
|
+
*/
|
|
756
|
+
routes?: boolean | AuthHostOptions;
|
|
684
757
|
/**
|
|
685
758
|
* Destino default da área da conta: pós-login do console (sem `return_to`),
|
|
686
759
|
* confirmações de e-mail e fallback de redirects. Default: '/account/security'.
|
|
@@ -721,16 +794,21 @@ export interface AuthServerConfigInput {
|
|
|
721
794
|
* Métodos de confirmação de identidade (sudo mode). A ordem do array é a
|
|
722
795
|
* ordem de exibição; o último método usado com sucesso é promovido ao topo.
|
|
723
796
|
*
|
|
724
|
-
* Ausente → a lista que `registerAuthHost` MONTOU
|
|
725
|
-
* `[password
|
|
726
|
-
*
|
|
727
|
-
*
|
|
728
|
-
* método
|
|
797
|
+
* Ausente → a lista que `registerAuthHost` MONTOU, DERIVADA contra este
|
|
798
|
+
* config: `[password, passkey]` num host com senha (o comportamento
|
|
799
|
+
* histórico) e `[passkey, magicLink]` num host que declarou
|
|
800
|
+
* `authMethods: { password: false }` — que de outra forma não teria um único
|
|
801
|
+
* método satisfazível. É a mesma resposta que os handlers dão nesse caso —
|
|
802
|
+
* "vale o que tem rota, menos o que este host não consegue satisfazer" —, e é
|
|
803
|
+
* o que impede a tela de oferecer um método sem endpoint ou de esconder um que
|
|
804
|
+
* funciona. Ver `derivedSudoMethods` em `host/sudo/runtime.ts`.
|
|
729
805
|
*
|
|
730
|
-
*
|
|
806
|
+
* DECLARAR esta lista DESLIGA a derivação: ela SUBSTITUI os defaults e vale ao
|
|
807
|
+
* pé da letra. Um host passwordless que a declare PRECISA incluir ao menos um
|
|
731
808
|
* método que não exija credencial previamente cadastrada — `oidcStepUp()` ou
|
|
732
809
|
* `magicLink()` — senão o usuário fica sem caminho para exportar/excluir os
|
|
733
|
-
* próprios dados
|
|
810
|
+
* próprios dados, e é isso que o aviso de boot de
|
|
811
|
+
* `host/sudo/satisfiability.ts` denuncia.
|
|
734
812
|
*/
|
|
735
813
|
sudo?: {
|
|
736
814
|
methods?: SudoMethod[];
|
|
@@ -1000,6 +1078,13 @@ export interface ResolvedServerConfig {
|
|
|
1000
1078
|
};
|
|
1001
1079
|
/** Keys de `auth_settings` travadas por terem sido definidas no defineConfig. */
|
|
1002
1080
|
lockedSettingKeys: string[];
|
|
1081
|
+
/** Auto-montagem das rotas do host-kit. Ver {@link AuthServerConfigInput.routes}. */
|
|
1082
|
+
routes?: boolean | AuthHostOptions;
|
|
1083
|
+
/**
|
|
1084
|
+
* Opções de POLÍTICA de `registerAuthHost` travadas por terem sido definidas
|
|
1085
|
+
* no defineConfig (config vence sobre o argumento). Ver `deriveLockedRouteOptions`.
|
|
1086
|
+
*/
|
|
1087
|
+
lockedRouteOptions: PolicyRouteOption[];
|
|
1003
1088
|
/** Integração opt-in com `@adonisjs/auth` (ausente = não integrado; comportamento de sempre). */
|
|
1004
1089
|
adonisAuth?: {
|
|
1005
1090
|
guard: string;
|
|
@@ -2,10 +2,11 @@ import { configProvider } from '@adonisjs/core';
|
|
|
2
2
|
import { adapters } from './adapters/factory.js';
|
|
3
3
|
import { composeAuditSink, resolveEvents } from './events/dispatcher.js';
|
|
4
4
|
import { resolveBotProtection, } from './host/bot_protection.js';
|
|
5
|
-
import { deriveLockedSettingKeys } from './host/config_locks.js';
|
|
5
|
+
import { deriveLockedRouteOptions, deriveLockedSettingKeys, } from './host/config_locks.js';
|
|
6
6
|
import { resolveMessages } from './host/i18n.js';
|
|
7
7
|
import { resolveOtpLoginConfig, } from './host/otp_login.js';
|
|
8
8
|
import { edgeRenderer } from './host/renderers/edge_renderer.js';
|
|
9
|
+
import { warnUnsatisfiableSudoConfig } from './host/sudo/satisfiability.js';
|
|
9
10
|
import { resolveTrustedDevices, } from './host/trusted_device.js';
|
|
10
11
|
import { generateJwks } from './keys/jwks_manager.js';
|
|
11
12
|
import { KeystoreCodec } from './keys/keystore_codec.js';
|
|
@@ -158,7 +159,9 @@ export function resolveAdmin(input) {
|
|
|
158
159
|
return {
|
|
159
160
|
enabled: input?.enabled ?? false,
|
|
160
161
|
roles: input?.roles && input.roles.length > 0 ? input.roles : ['ADMIN'],
|
|
161
|
-
|
|
162
|
+
// Default `true`: preserva o comportamento histórico (grant sempre
|
|
163
|
+
// registrado). Ver o docblock de `AdminConfigInput.impersonation`.
|
|
164
|
+
impersonation: input?.impersonation !== false,
|
|
162
165
|
};
|
|
163
166
|
}
|
|
164
167
|
/** Lê API keys de `AUTHKIT_ADMIN_API_KEY` (uma ou várias, separadas por vírgula). */
|
|
@@ -294,6 +297,25 @@ export function defineConfig(config) {
|
|
|
294
297
|
else {
|
|
295
298
|
jwks = { keys: jwksConfig.keys ?? [] };
|
|
296
299
|
}
|
|
300
|
+
// BACKSTOP DE SUDO. Um host cujo `sudo.methods` não tem um único método
|
|
301
|
+
// satisfazível por conta sem senha fica bricado para TODA operação sob
|
|
302
|
+
// `requireSudo` — e hoje isso só se descobre quando um usuário não consegue
|
|
303
|
+
// excluir a própria conta. Aqui é o único ponto do pacote onde `sudo.methods`,
|
|
304
|
+
// `authMethods` e `passwordless` estão os três resolvidos e juntos, e este
|
|
305
|
+
// callback roda no boot (`configProvider.resolve` no `boot()` do provider),
|
|
306
|
+
// antes de qualquer request. Ver `host/sudo/satisfiability.ts` para as quatro
|
|
307
|
+
// condições e para o porquê de ser WARN e não THROW.
|
|
308
|
+
//
|
|
309
|
+
// LIMITE CONHECIDO: só vê a lista que passou pelo CONFIG. Um host que declare
|
|
310
|
+
// a lista APENAS no argumento `registerAuthHost(router, { sudoMethods })` não
|
|
311
|
+
// é coberto — o registro de rotas acontece fora daqui e o `authMethods` não
|
|
312
|
+
// chega lá. É o caso do ator informado (quem escreveu a lista à mão), não o
|
|
313
|
+
// do host que herdou os defaults, que é quem o aviso existe para pegar.
|
|
314
|
+
warnUnsatisfiableSudoConfig({
|
|
315
|
+
methods: config.sudo?.methods,
|
|
316
|
+
passwordPinnedOff: config.authMethods?.password === false,
|
|
317
|
+
passwordlessSignup: config.passwordless?.signup === true,
|
|
318
|
+
});
|
|
297
319
|
// #9: mfaIssuer efetivo — top-level do defineConfig vence; senão o do lucidAccountStore; senão default.
|
|
298
320
|
const effectiveMfaIssuer = config.mfaIssuer ??
|
|
299
321
|
config.accountStore?.__mfaIssuer ??
|
|
@@ -389,6 +411,10 @@ export function defineConfig(config) {
|
|
|
389
411
|
// Keys de auth_settings travadas porque foram definidas no defineConfig:
|
|
390
412
|
// config vence e a UI/Admin API não pode alterá-las (ver host/config_locks.ts).
|
|
391
413
|
lockedSettingKeys: deriveLockedSettingKeys(config),
|
|
414
|
+
routes: config.routes,
|
|
415
|
+
// Mesma regra do `lockedSettingKeys`, outro eixo: config × argumento de
|
|
416
|
+
// `registerAuthHost` (ver host/config_locks.ts).
|
|
417
|
+
lockedRouteOptions: deriveLockedRouteOptions(config),
|
|
392
418
|
// Opt-in: ausente = authkit nunca toca `ctx.auth` (comportamento de sempre).
|
|
393
419
|
adonisAuth: config.adonisAuth,
|
|
394
420
|
};
|
|
@@ -38,6 +38,7 @@ import { AvatarUploadError, isAvatarUploadSupported, storeAvatar } from '../avat
|
|
|
38
38
|
import { sendEmailChangeConfirmationEmail, sendEmailChangeNoticeEmail } from '../default_mailer.js';
|
|
39
39
|
import { translate } from '../i18n.js';
|
|
40
40
|
import { ACCOUNT_SESSION_KEY } from '../middleware/account_auth.js';
|
|
41
|
+
import { authkitOrigin } from '../origin.js';
|
|
41
42
|
import { resolveRuntimeSettings } from '../runtime_settings.js';
|
|
42
43
|
import { resolveEffectiveEmailChange, resolveEffectivePasswordHistory, } from '../runtime_toggles.js';
|
|
43
44
|
import { dispatchSecurityNotice } from '../security_notice_service.js';
|
|
@@ -331,7 +332,7 @@ export default class AccountApiController {
|
|
|
331
332
|
kind: 'password_changed',
|
|
332
333
|
ip: ctx.request.ip?.() ?? null,
|
|
333
334
|
timestamp: new Date().toISOString(),
|
|
334
|
-
}, cfg.mail, cfg.audit);
|
|
335
|
+
}, cfg.mail, cfg.audit, cfg);
|
|
335
336
|
}
|
|
336
337
|
return { ok: true };
|
|
337
338
|
}
|
|
@@ -389,7 +390,7 @@ export default class AccountApiController {
|
|
|
389
390
|
email: newEmail,
|
|
390
391
|
ip: ctx.request.ip?.() ?? null,
|
|
391
392
|
});
|
|
392
|
-
const origin =
|
|
393
|
+
const origin = authkitOrigin(cfg);
|
|
393
394
|
const confirmUrl = `${origin}${accountPath('emailConfirm')}?token=${encodeURIComponent(issued.token)}`;
|
|
394
395
|
if (cfg.mail?.onEmailChangeConfirm) {
|
|
395
396
|
await cfg.mail.onEmailChangeConfirm({
|
|
@@ -414,7 +415,7 @@ export default class AccountApiController {
|
|
|
414
415
|
await cfg.mail.onEmailChangeNotice({ email: account.email, newEmail });
|
|
415
416
|
}
|
|
416
417
|
else {
|
|
417
|
-
await sendEmailChangeNoticeEmail(ctx, { email: account.email, newEmail });
|
|
418
|
+
await sendEmailChangeNoticeEmail(ctx, cfg, { email: account.email, newEmail });
|
|
418
419
|
}
|
|
419
420
|
}
|
|
420
421
|
return { ok: true, email: newEmail };
|
|
@@ -692,7 +693,7 @@ export default class AccountApiController {
|
|
|
692
693
|
kind: 'passkey_removed',
|
|
693
694
|
ip: ctx.request.ip?.() ?? null,
|
|
694
695
|
timestamp: new Date().toISOString(),
|
|
695
|
-
}, cfg.mail, cfg.audit);
|
|
696
|
+
}, cfg.mail, cfg.audit, cfg);
|
|
696
697
|
}
|
|
697
698
|
return { ok: true, removed: credentialId };
|
|
698
699
|
}
|
|
@@ -3,6 +3,7 @@ import { supportsAccountDeletion, supportsAccountStatus, supportsCountByGlobalRo
|
|
|
3
3
|
import { PasswordPolicyError } from '../../password/password_manager.js';
|
|
4
4
|
import { AccountDeletionService } from '../account_deletion_service.js';
|
|
5
5
|
import { sendPasswordResetEmail } from '../default_mailer.js';
|
|
6
|
+
import { authkitOrigin } from '../origin.js';
|
|
6
7
|
import { resolveEffectiveRolesCatalog } from '../runtime_toggles.js';
|
|
7
8
|
/**
|
|
8
9
|
* Lógica de gestão de usuários compartilhada entre o console admin (B6, HTML) e a
|
|
@@ -292,7 +293,7 @@ export class AdminUsersService {
|
|
|
292
293
|
const issued = await this.cfg.accountStore.issuePasswordResetToken(email);
|
|
293
294
|
if (!issued)
|
|
294
295
|
return;
|
|
295
|
-
const origin =
|
|
296
|
+
const origin = authkitOrigin(this.cfg);
|
|
296
297
|
const resetUrl = `${origin}/auth/reset-password?token=${encodeURIComponent(issued.token)}`;
|
|
297
298
|
if (this.cfg.mail?.onPasswordReset) {
|
|
298
299
|
await this.cfg.mail.onPasswordReset({
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import '../augmentations.js';
|
|
2
2
|
import { orgAddMemberValidator, orgCreateValidator, orgInvitationValidator, orgMemberRoleValidator, orgUpdateValidator, } from '../admin_validators.js';
|
|
3
|
+
import { authkitOrigin } from '../origin.js';
|
|
3
4
|
import { resolveRuntimeSettings } from '../runtime_settings.js';
|
|
4
5
|
import { AdminOrgsService } from './admin_orgs_service.js';
|
|
5
6
|
import { apiError, orgDetailDto, orgDto, orgInvitationDto } from './dto.js';
|
|
@@ -158,7 +159,7 @@ export default class ApiOrgsController {
|
|
|
158
159
|
const svc = new AdminOrgsService(cfg);
|
|
159
160
|
const orgId = ctx.request.param('id');
|
|
160
161
|
const { email, role } = await ctx.request.validateUsing(orgInvitationValidator);
|
|
161
|
-
const origin =
|
|
162
|
+
const origin = authkitOrigin(cfg);
|
|
162
163
|
const result = await svc.createInvitation(orgId, { email, role: role ?? 'member' }, actor, origin, await resolveRuntimeSettings(ctx));
|
|
163
164
|
if (!result.ok) {
|
|
164
165
|
if (result.reason === 'not_supported')
|
|
@@ -7,7 +7,21 @@ import type { HttpContext } from '@adonisjs/core/http';
|
|
|
7
7
|
*
|
|
8
8
|
* Retorna os parâmetros RFC 8693 (token exchange) para o admin assumir a
|
|
9
9
|
* identidade de um usuário-alvo. 404 quando impersonation está desabilitado na
|
|
10
|
-
* config
|
|
10
|
+
* config, quando a setting de runtime `admin_impersonation` desliga o painel, ou
|
|
11
|
+
* quando nenhum client tem o grant token-exchange habilitado.
|
|
12
|
+
*
|
|
13
|
+
* DUAS CAMADAS, PROPOSITALMENTE ASSIMÉTRICAS:
|
|
14
|
+
*
|
|
15
|
+
* 1. `config.admin.impersonation` decide se a CAPACIDADE existe. É decisão de
|
|
16
|
+
* boot: é ela que registra (ou não) o grant RFC 8693 no provider OIDC. Uma
|
|
17
|
+
* setting de runtime não desregistra rota que nunca foi registrada.
|
|
18
|
+
* 2. a setting `admin_impersonation` decide se o CONSOLE OFERECE o painel.
|
|
19
|
+
* É decisão de runtime, mudável sem redeploy.
|
|
20
|
+
*
|
|
21
|
+
* A setting só APERTA, nunca AFROUXA — o gate de config é checado ANTES dela, e
|
|
22
|
+
* declarar `admin.impersonation` no `defineConfig` TRAVA a key (ver
|
|
23
|
+
* `host/config_locks.ts`), de forma que `getSetting` devolve null e o resolver
|
|
24
|
+
* cai no valor do config. Config > runtime, a mesma precedência do resto da lib.
|
|
11
25
|
*/
|
|
12
26
|
export default class ConsoleImpersonationController {
|
|
13
27
|
handle(ctx: HttpContext): Promise<void | {
|
|
@@ -2,6 +2,8 @@ import '../augmentations.js';
|
|
|
2
2
|
import { apiError } from '../admin_api/dto.js';
|
|
3
3
|
import { buildImpersonationPanel } from '../impersonation.js';
|
|
4
4
|
import { ACCOUNT_SESSION_KEY } from '../middleware/account_auth.js';
|
|
5
|
+
import { resolveRuntimeSettingsOrNoop } from '../runtime_settings.js';
|
|
6
|
+
import { resolveEffectiveAdminImpersonation } from '../runtime_toggles.js';
|
|
5
7
|
/**
|
|
6
8
|
* Endpoint JSON do painel de impersonation do console admin React.
|
|
7
9
|
*
|
|
@@ -9,16 +11,38 @@ import { ACCOUNT_SESSION_KEY } from '../middleware/account_auth.js';
|
|
|
9
11
|
*
|
|
10
12
|
* Retorna os parâmetros RFC 8693 (token exchange) para o admin assumir a
|
|
11
13
|
* identidade de um usuário-alvo. 404 quando impersonation está desabilitado na
|
|
12
|
-
* config
|
|
14
|
+
* config, quando a setting de runtime `admin_impersonation` desliga o painel, ou
|
|
15
|
+
* quando nenhum client tem o grant token-exchange habilitado.
|
|
16
|
+
*
|
|
17
|
+
* DUAS CAMADAS, PROPOSITALMENTE ASSIMÉTRICAS:
|
|
18
|
+
*
|
|
19
|
+
* 1. `config.admin.impersonation` decide se a CAPACIDADE existe. É decisão de
|
|
20
|
+
* boot: é ela que registra (ou não) o grant RFC 8693 no provider OIDC. Uma
|
|
21
|
+
* setting de runtime não desregistra rota que nunca foi registrada.
|
|
22
|
+
* 2. a setting `admin_impersonation` decide se o CONSOLE OFERECE o painel.
|
|
23
|
+
* É decisão de runtime, mudável sem redeploy.
|
|
24
|
+
*
|
|
25
|
+
* A setting só APERTA, nunca AFROUXA — o gate de config é checado ANTES dela, e
|
|
26
|
+
* declarar `admin.impersonation` no `defineConfig` TRAVA a key (ver
|
|
27
|
+
* `host/config_locks.ts`), de forma que `getSetting` devolve null e o resolver
|
|
28
|
+
* cai no valor do config. Config > runtime, a mesma precedência do resto da lib.
|
|
13
29
|
*/
|
|
14
30
|
export default class ConsoleImpersonationController {
|
|
15
31
|
async handle(ctx) {
|
|
16
32
|
const service = await ctx.containerResolver.make('authkit.server');
|
|
17
33
|
const cfg = service.config;
|
|
18
|
-
//
|
|
34
|
+
// Camada 1 — o kill switch de config. Sem ele o grant token-exchange nem
|
|
35
|
+
// existe no provider; devolver os parâmetros do painel seria mentir.
|
|
19
36
|
if (!cfg.admin.impersonation) {
|
|
20
37
|
return ctx.response.notFound(apiError('capability_unsupported', 'Impersonation não está habilitado nesta instalação.'));
|
|
21
38
|
}
|
|
39
|
+
// Camada 2 — a política de runtime. Fail-safe: sem tabela `auth_settings`
|
|
40
|
+
// (ou com a key travada por config) o resolver cai no valor do config.
|
|
41
|
+
const runtimeSettings = await resolveRuntimeSettingsOrNoop(ctx);
|
|
42
|
+
const effective = await resolveEffectiveAdminImpersonation(runtimeSettings, cfg.admin.impersonation);
|
|
43
|
+
if (!effective.enabled) {
|
|
44
|
+
return ctx.response.notFound(apiError('capability_unsupported', 'O painel de impersonation está desligado pela setting de runtime `admin_impersonation`.'));
|
|
45
|
+
}
|
|
22
46
|
const targetId = ctx.request.param('userId');
|
|
23
47
|
const account = await cfg.accountStore.findById(targetId);
|
|
24
48
|
if (!account) {
|
|
@@ -3,6 +3,7 @@ import { AdminOrgsService } from '../admin_api/admin_orgs_service.js';
|
|
|
3
3
|
import { apiError, orgDetailDto, orgDto } from '../admin_api/dto.js';
|
|
4
4
|
import { orgAddMemberValidator, orgCreateValidator, orgInvitationValidator, orgMemberRoleValidator, orgUpdateValidator, } from '../admin_validators.js';
|
|
5
5
|
import { ACCOUNT_SESSION_KEY } from '../middleware/account_auth.js';
|
|
6
|
+
import { authkitOrigin } from '../origin.js';
|
|
6
7
|
import { resolveRuntimeSettings } from '../runtime_settings.js';
|
|
7
8
|
/**
|
|
8
9
|
* Endpoints JSON de organizações do console admin React.
|
|
@@ -201,7 +202,8 @@ export default class ConsoleOrgsController {
|
|
|
201
202
|
}
|
|
202
203
|
const orgId = ctx.request.param('id');
|
|
203
204
|
const { email, role } = await ctx.request.validateUsing(orgInvitationValidator);
|
|
204
|
-
const
|
|
205
|
+
const service = await ctx.containerResolver.make('authkit.server');
|
|
206
|
+
const origin = authkitOrigin(service.config);
|
|
205
207
|
const result = await svc.createInvitation(orgId, { email, role: role ?? 'member' }, this.actor(ctx), origin, await this.settings(ctx));
|
|
206
208
|
if ('ok' in result && result.ok === false) {
|
|
207
209
|
if (result.reason === 'not_found') {
|
|
@@ -96,16 +96,16 @@ export declare const adminUserUpdateValidator: import("@vinejs/vine").VineValida
|
|
|
96
96
|
avatarUrl: import("@vinejs/vine/schema/base/literal").OptionalModifier<import("@vinejs/vine/schema/base/literal").NullableModifier<import("@vinejs/vine").VineString>>;
|
|
97
97
|
}, {
|
|
98
98
|
name?: string | null | undefined;
|
|
99
|
-
globalRoles?: string[] | null | undefined;
|
|
100
99
|
avatarUrl?: string | null | undefined;
|
|
100
|
+
globalRoles?: string[] | null | undefined;
|
|
101
101
|
}, {
|
|
102
102
|
name?: string | null | undefined;
|
|
103
|
-
globalRoles?: string[] | undefined;
|
|
104
103
|
avatarUrl?: string | null | undefined;
|
|
104
|
+
globalRoles?: string[] | undefined;
|
|
105
105
|
}, {
|
|
106
106
|
name?: string | null | undefined;
|
|
107
|
-
globalRoles?: string[] | undefined;
|
|
108
107
|
avatarUrl?: string | null | undefined;
|
|
108
|
+
globalRoles?: string[] | undefined;
|
|
109
109
|
}>, Record<string, any> | undefined>;
|
|
110
110
|
/** Substituição de roles globais no console (PATCH /users/:id/roles). */
|
|
111
111
|
export declare const adminUserRolesValidator: import("@vinejs/vine").VineValidator<import("@vinejs/vine").VineObject<{
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { AuthSocialConfig, ResolvedRateLimitConfig } from '../define_config.js';
|
|
2
|
+
import type { PolicyRouteOption } from './config_locks.js';
|
|
3
|
+
import type { AuthHostOptions } from './register_auth_host.js';
|
|
4
|
+
import type { SudoMethod } from './sudo/types.js';
|
|
2
5
|
/**
|
|
3
6
|
* Bits de routing do config resolvido que o `registerAuthHost` precisa em tempo de
|
|
4
7
|
* REGISTRO de rota (síncrono). Stash feito no `boot()` do provider — que roda ANTES
|
|
@@ -13,10 +16,37 @@ export interface AuthHostRuntimeConfig {
|
|
|
13
16
|
rateLimit: ResolvedRateLimitConfig;
|
|
14
17
|
adminEnabled: boolean;
|
|
15
18
|
adminApiEnabled: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* `config.sudo.methods` — a lista que a TELA oferece e que os handlers
|
|
21
|
+
* ACEITAM. Presente aqui para que `registerAuthHost` MONTE exatamente ela,
|
|
22
|
+
* em vez de exigir que o host repita a lista no `start/routes.ts`.
|
|
23
|
+
*/
|
|
24
|
+
sudoMethods?: SudoMethod[];
|
|
25
|
+
/**
|
|
26
|
+
* Defaults ESTRUTURAIS declarados em `config.routes` (prefixo do console de
|
|
27
|
+
* conta, telas montadas, destino de login). Argumentos de `registerAuthHost`
|
|
28
|
+
* continuam vencendo sobre estes — ver a regra de precedência no docblock de
|
|
29
|
+
* `registerAuthHost`.
|
|
30
|
+
*/
|
|
31
|
+
routes?: AuthHostOptions;
|
|
32
|
+
/**
|
|
33
|
+
* Opções de POLÍTICA travadas por terem sido declaradas no `defineConfig`.
|
|
34
|
+
* Ver `deriveLockedRouteOptions`.
|
|
35
|
+
*/
|
|
36
|
+
lockedRouteOptions?: PolicyRouteOption[];
|
|
16
37
|
}
|
|
17
38
|
/** Stash dos bits de routing (chamado no boot do provider). */
|
|
18
39
|
export declare function setAuthHostConfig(config: AuthHostRuntimeConfig): void;
|
|
19
40
|
/** Lê os bits de routing stashados; undefined se o boot ainda não rodou (fallback p/ opts/defaults). */
|
|
20
41
|
export declare function getAuthHostConfig(): AuthHostRuntimeConfig | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* Registra que o provider já montou as rotas automaticamente (`config.routes`).
|
|
44
|
+
* A partir daí uma chamada manual a `registerAuthHost` é um DUPLO REGISTRO —
|
|
45
|
+
* duas rotas com o mesmo nome derrubam o boot do AdonisJS —, então ela lança
|
|
46
|
+
* com a instrução de qual dos dois caminhos remover.
|
|
47
|
+
*/
|
|
48
|
+
export declare function markAuthHostAutoMounted(): void;
|
|
49
|
+
/** O provider já auto-montou as rotas neste processo? */
|
|
50
|
+
export declare function wasAuthHostAutoMounted(): boolean;
|
|
21
51
|
/** Limpa o stash — uso em testes. */
|
|
22
52
|
export declare function resetAuthHostConfig(): void;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
let stashed;
|
|
2
|
+
let autoMounted = false;
|
|
2
3
|
/** Stash dos bits de routing (chamado no boot do provider). */
|
|
3
4
|
export function setAuthHostConfig(config) {
|
|
4
5
|
stashed = config;
|
|
@@ -7,7 +8,21 @@ export function setAuthHostConfig(config) {
|
|
|
7
8
|
export function getAuthHostConfig() {
|
|
8
9
|
return stashed;
|
|
9
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Registra que o provider já montou as rotas automaticamente (`config.routes`).
|
|
13
|
+
* A partir daí uma chamada manual a `registerAuthHost` é um DUPLO REGISTRO —
|
|
14
|
+
* duas rotas com o mesmo nome derrubam o boot do AdonisJS —, então ela lança
|
|
15
|
+
* com a instrução de qual dos dois caminhos remover.
|
|
16
|
+
*/
|
|
17
|
+
export function markAuthHostAutoMounted() {
|
|
18
|
+
autoMounted = true;
|
|
19
|
+
}
|
|
20
|
+
/** O provider já auto-montou as rotas neste processo? */
|
|
21
|
+
export function wasAuthHostAutoMounted() {
|
|
22
|
+
return autoMounted;
|
|
23
|
+
}
|
|
10
24
|
/** Limpa o stash — uso em testes. */
|
|
11
25
|
export function resetAuthHostConfig() {
|
|
12
26
|
stashed = undefined;
|
|
27
|
+
autoMounted = false;
|
|
13
28
|
}
|
|
@@ -22,6 +22,35 @@
|
|
|
22
22
|
* travam (UI sempre controla).
|
|
23
23
|
*/
|
|
24
24
|
export declare function deriveLockedSettingKeys(config: Record<string, any>): string[];
|
|
25
|
+
/**
|
|
26
|
+
* Opções de rota que são POLÍTICA (decidem o que é PERMITIDO), e não estrutura
|
|
27
|
+
* (onde as rotas moram). Ver `AuthHostOptions` e o docblock de
|
|
28
|
+
* `registerAuthHost`.
|
|
29
|
+
*/
|
|
30
|
+
export declare const POLICY_ROUTE_OPTIONS: readonly ["social", "rateLimit", "sudoMethods", "admin", "adminApi"];
|
|
31
|
+
/** Uma opção de política de `AuthHostOptions`. */
|
|
32
|
+
export type PolicyRouteOption = (typeof POLICY_ROUTE_OPTIONS)[number];
|
|
33
|
+
/**
|
|
34
|
+
* Deriva as opções de rota TRAVADAS a partir dos campos EXPLICITAMENTE presentes
|
|
35
|
+
* no input do `defineConfig` — a MESMA regra de {@link deriveLockedSettingKeys}
|
|
36
|
+
* ("declarou no arquivo → o arquivo manda"), aplicada ao outro eixo: config vs.
|
|
37
|
+
* argumento de `registerAuthHost` (em vez de config vs. runtime setting).
|
|
38
|
+
*
|
|
39
|
+
* Travada significa: `registerAuthHost(router, { <key>: ... })` NÃO altera o
|
|
40
|
+
* comportamento; o valor do config vence e a divergência é reportada
|
|
41
|
+
* (`AuthHostRouteMap.overriddenByConfig` + `console.warn` no boot).
|
|
42
|
+
*
|
|
43
|
+
* O motivo de existir: sem isso, `start/routes.ts` pode AFROUXAR o que o config
|
|
44
|
+
* declarou — desligar o rate-limit, montar login social que o config não
|
|
45
|
+
* declara, trocar a lista de métodos de sudo — e o `config/authkit.ts` deixa de
|
|
46
|
+
* ser auditável (seria preciso ler o routes.ts de cada app para saber o que
|
|
47
|
+
* está valendo).
|
|
48
|
+
*
|
|
49
|
+
* Só trava o que o config DECLARA. Um campo ausente do config nunca trava: o
|
|
50
|
+
* argumento continua livre (back-compat com quem só configura pelo routes.ts) —
|
|
51
|
+
* exatamente como `authMethods` só trava os métodos que ele lista.
|
|
52
|
+
*/
|
|
53
|
+
export declare function deriveLockedRouteOptions(config: Record<string, any>): PolicyRouteOption[];
|
|
25
54
|
/** Erro lançado ao tentar gravar/remover uma setting travada via `defineConfig`. */
|
|
26
55
|
export declare class SettingLockedError extends Error {
|
|
27
56
|
readonly code = "E_SETTING_LOCKED";
|
|
@@ -43,6 +43,52 @@ export function deriveLockedSettingKeys(config) {
|
|
|
43
43
|
add(config.ttl !== undefined, SETTING_KEYS.TOKEN_TTL);
|
|
44
44
|
return locked;
|
|
45
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Opções de rota que são POLÍTICA (decidem o que é PERMITIDO), e não estrutura
|
|
48
|
+
* (onde as rotas moram). Ver `AuthHostOptions` e o docblock de
|
|
49
|
+
* `registerAuthHost`.
|
|
50
|
+
*/
|
|
51
|
+
export const POLICY_ROUTE_OPTIONS = [
|
|
52
|
+
'social',
|
|
53
|
+
'rateLimit',
|
|
54
|
+
'sudoMethods',
|
|
55
|
+
'admin',
|
|
56
|
+
'adminApi',
|
|
57
|
+
];
|
|
58
|
+
/**
|
|
59
|
+
* Deriva as opções de rota TRAVADAS a partir dos campos EXPLICITAMENTE presentes
|
|
60
|
+
* no input do `defineConfig` — a MESMA regra de {@link deriveLockedSettingKeys}
|
|
61
|
+
* ("declarou no arquivo → o arquivo manda"), aplicada ao outro eixo: config vs.
|
|
62
|
+
* argumento de `registerAuthHost` (em vez de config vs. runtime setting).
|
|
63
|
+
*
|
|
64
|
+
* Travada significa: `registerAuthHost(router, { <key>: ... })` NÃO altera o
|
|
65
|
+
* comportamento; o valor do config vence e a divergência é reportada
|
|
66
|
+
* (`AuthHostRouteMap.overriddenByConfig` + `console.warn` no boot).
|
|
67
|
+
*
|
|
68
|
+
* O motivo de existir: sem isso, `start/routes.ts` pode AFROUXAR o que o config
|
|
69
|
+
* declarou — desligar o rate-limit, montar login social que o config não
|
|
70
|
+
* declara, trocar a lista de métodos de sudo — e o `config/authkit.ts` deixa de
|
|
71
|
+
* ser auditável (seria preciso ler o routes.ts de cada app para saber o que
|
|
72
|
+
* está valendo).
|
|
73
|
+
*
|
|
74
|
+
* Só trava o que o config DECLARA. Um campo ausente do config nunca trava: o
|
|
75
|
+
* argumento continua livre (back-compat com quem só configura pelo routes.ts) —
|
|
76
|
+
* exatamente como `authMethods` só trava os métodos que ele lista.
|
|
77
|
+
*/
|
|
78
|
+
export function deriveLockedRouteOptions(config) {
|
|
79
|
+
const locked = [];
|
|
80
|
+
if (config.social !== undefined)
|
|
81
|
+
locked.push('social');
|
|
82
|
+
if (config.rateLimit !== undefined)
|
|
83
|
+
locked.push('rateLimit');
|
|
84
|
+
if (config.sudo?.methods !== undefined)
|
|
85
|
+
locked.push('sudoMethods');
|
|
86
|
+
if (config.admin !== undefined)
|
|
87
|
+
locked.push('admin');
|
|
88
|
+
if (config.adminApi !== undefined)
|
|
89
|
+
locked.push('adminApi');
|
|
90
|
+
return locked;
|
|
91
|
+
}
|
|
46
92
|
/** Erro lançado ao tentar gravar/remover uma setting travada via `defineConfig`. */
|
|
47
93
|
export class SettingLockedError extends Error {
|
|
48
94
|
code = 'E_SETTING_LOCKED';
|
|
@@ -9,10 +9,46 @@ import type { HttpContext } from '@adonisjs/core/http';
|
|
|
9
9
|
* sessão mudar.
|
|
10
10
|
*/
|
|
11
11
|
/**
|
|
12
|
-
* Retorna o id da conta
|
|
13
|
-
* sessão).
|
|
12
|
+
* Retorna o id da conta que o request está representando no console (ou `null`
|
|
13
|
+
* quando não há sessão).
|
|
14
|
+
*
|
|
15
|
+
* ATENÇÃO: com impersonation ativa isto é a conta PERSONIFICADA, não o admin
|
|
16
|
+
* que a personificou. É o comportamento certo para "como qual conta este
|
|
17
|
+
* request está agindo?" e é a entrada ERRADA para uma checagem de
|
|
18
|
+
* permissão/role — para isso use `realAccountId`.
|
|
14
19
|
*/
|
|
15
20
|
export declare function getAccountId(ctx: HttpContext): string | null;
|
|
21
|
+
/**
|
|
22
|
+
* Retorna o id do HUMANO real por trás do request — o id que uma decisão de
|
|
23
|
+
* autorização deve usar.
|
|
24
|
+
*
|
|
25
|
+
* ```
|
|
26
|
+
* impersonation ativa → o id do impersonator (o admin real)
|
|
27
|
+
* sem impersonation → o id da conta logada
|
|
28
|
+
* sem sessão → null
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* USE ESTE em qualquer checagem de permissão/role ("esta pessoa é admin?",
|
|
32
|
+
* "esta pessoa pode aprovar isto?"). Use `getAccountId` só para responder
|
|
33
|
+
* "como qual conta este request está agindo?" (queries com escopo no alvo,
|
|
34
|
+
* UI, banner de impersonation).
|
|
35
|
+
*
|
|
36
|
+
* POR QUE ISSO EXISTE. Durante uma impersonation, `getAccountId` devolve a
|
|
37
|
+
* conta PERSONIFICADA — é o comportamento correto dele e o resto do app
|
|
38
|
+
* depende disso. Mas ele é, por isso mesmo, a entrada ERRADA para um role
|
|
39
|
+
* check: perguntar "o `getAccountId` é admin?" com impersonation ativa
|
|
40
|
+
* pergunta sobre o usuário personificado e, quando ele por acaso for admin,
|
|
41
|
+
* entrega os privilégios do admin a quem está sendo personificado — e, no
|
|
42
|
+
* sentido inverso, faz o admin real perder o próprio acesso. `realAccountId`
|
|
43
|
+
* ignora a troca de conta e responde sempre sobre quem está de fato dirigindo
|
|
44
|
+
* a sessão.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* // gate de /admin: a pessoa por trás do request é admin?
|
|
48
|
+
* const id = realAccountId(ctx)
|
|
49
|
+
* if (!id || !(await authz.hasRole(id, 'admin'))) throw new Error('forbidden')
|
|
50
|
+
*/
|
|
51
|
+
export declare function realAccountId(ctx: HttpContext): string | null;
|
|
16
52
|
/**
|
|
17
53
|
* `true` quando o request carrega uma sessão de conta do console.
|
|
18
54
|
*/
|