@adonis-agora/authkit-server 1.0.0-rc.1 → 1.0.0-rc.3
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/CHANGELOG.md +34 -0
- package/README.md +4 -5
- package/build/commands/clients_create.d.ts +4 -0
- package/build/commands/clients_create.js +97 -0
- package/build/commands/commands.json +14 -0
- package/build/index.d.ts +8 -11
- package/build/index.js +5 -9
- package/build/providers/authkit_server_provider.js +6 -8
- package/build/src/agents/config.d.ts +9 -13
- package/build/src/agents/config.js +17 -11
- package/build/src/agents/delegation_service.js +3 -3
- package/build/src/agents/delegation_store.d.ts +3 -2
- package/build/src/agents/delegation_store.js +6 -5
- package/build/src/agents/extension.d.ts +261 -0
- package/build/src/agents/extension.js +40 -0
- package/build/src/agents/middleware.js +1 -1
- package/build/src/agents/protocol.d.ts +4 -4
- package/build/src/agents/registry_store.d.ts +3 -2
- package/build/src/agents/registry_store.js +1 -1
- package/build/src/agents/runtime.d.ts +26 -7
- package/build/src/agents/runtime.js +110 -75
- package/build/src/audit/audit_sink.d.ts +1 -1
- package/build/src/audit/audit_sink.js +6 -1
- package/build/src/define_config.d.ts +16 -12
- package/build/src/define_config.js +11 -7
- package/build/src/host/access_token_verifier.js +7 -0
- package/build/src/host/account_api/account_api_controller.js +8 -6
- package/build/src/host/account_session_sign_in.d.ts +12 -0
- package/build/src/host/account_session_sign_in.js +19 -0
- package/build/src/host/admin_api/api_clients_controller.d.ts +37 -0
- package/build/src/host/admin_api/api_clients_controller.js +31 -1
- package/build/src/host/admin_api/dto.d.ts +3 -1
- package/build/src/host/admin_api/dto.js +2 -0
- package/build/src/host/admin_clients_service.d.ts +20 -0
- package/build/src/host/admin_clients_service.js +55 -0
- package/build/src/host/admin_console/console_clients_controller.d.ts +41 -0
- package/build/src/host/admin_console/console_clients_controller.js +34 -1
- package/build/src/host/admin_console/console_personal_agents_controller.d.ts +1 -6
- package/build/src/host/admin_console/console_personal_agents_controller.js +50 -30
- package/build/src/host/admin_sessions_service.js +8 -5
- package/build/src/host/admin_validators.d.ts +29 -0
- package/build/src/host/admin_validators.js +30 -0
- package/build/src/host/auth_host_config.d.ts +6 -2
- package/build/src/host/controllers/account_apps_controller.js +15 -11
- package/build/src/host/csrf.d.ts +3 -3
- package/build/src/host/csrf.js +3 -13
- package/build/src/host/default_mailer.d.ts +11 -6
- package/build/src/host/default_mailer.js +16 -12
- package/build/src/host/i18n.d.ts +3 -14
- package/build/src/host/i18n.js +15 -13
- package/build/src/host/register_auth_host.js +30 -61
- package/build/src/host/ui-dist/assets/index-u0bMxkLg.js +67 -0
- package/build/src/host/ui-dist/index.html +1 -1
- package/build/src/provider/build_provider.js +37 -6
- package/build/src/provider/oidc_service.d.ts +1 -1
- package/build/src/provider/oidc_service.js +1 -1
- package/build/src/schema/ensure.d.ts +14 -0
- package/build/src/schema/ensure.js +43 -150
- package/build/src/service_tenants/client_metadata.d.ts +39 -0
- package/build/src/service_tenants/client_metadata.js +80 -0
- package/build/src/service_tenants/config.d.ts +44 -0
- package/build/src/service_tenants/config.js +44 -0
- package/build/src/service_tenants/provider_hooks.d.ts +52 -0
- package/build/src/service_tenants/provider_hooks.js +124 -0
- package/build/src/service_tenants/tenant_verifier.d.ts +73 -0
- package/build/src/service_tenants/tenant_verifier.js +104 -0
- package/package.json +2 -2
- package/build/src/agents/poppy/client_registry.d.ts +0 -50
- package/build/src/agents/poppy/client_registry.js +0 -260
- package/build/src/agents/poppy/config.d.ts +0 -396
- package/build/src/agents/poppy/config.js +0 -228
- package/build/src/agents/poppy/dpop.d.ts +0 -61
- package/build/src/agents/poppy/dpop.js +0 -158
- package/build/src/agents/poppy/email_code_sign_in.d.ts +0 -27
- package/build/src/agents/poppy/email_code_sign_in.js +0 -140
- package/build/src/agents/poppy/errors.d.ts +0 -25
- package/build/src/agents/poppy/errors.js +0 -25
- package/build/src/agents/poppy/replay.d.ts +0 -25
- package/build/src/agents/poppy/replay.js +0 -106
- package/build/src/agents/poppy/resource.d.ts +0 -100
- package/build/src/agents/poppy/resource.js +0 -129
- package/build/src/agents/poppy/safe_fetch.d.ts +0 -48
- package/build/src/agents/poppy/safe_fetch.js +0 -262
- package/build/src/agents/poppy/service.d.ts +0 -293
- package/build/src/agents/poppy/service.js +0 -1458
- package/build/src/agents/poppy/session_jwt.d.ts +0 -30
- package/build/src/agents/poppy/session_jwt.js +0 -133
- package/build/src/agents/poppy/store.d.ts +0 -106
- package/build/src/agents/poppy/store.js +0 -261
- package/build/src/agents/poppy/web_session.d.ts +0 -39
- package/build/src/agents/poppy/web_session.js +0 -122
- package/build/src/host/controllers/poppy_consent_controller.d.ts +0 -19
- package/build/src/host/controllers/poppy_consent_controller.js +0 -199
- package/build/src/host/controllers/poppy_controller.d.ts +0 -33
- package/build/src/host/controllers/poppy_controller.js +0 -219
- package/build/src/host/poppy_session_key.d.ts +0 -9
- package/build/src/host/poppy_session_key.js +0 -9
- package/build/src/host/ui-dist/assets/index-D0EIeyKK.js +0 -67
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { HttpContext } from '@adonisjs/core/http';
|
|
2
|
+
import type { ResolvedPersonalAgentExtension } from '../agents/extension.js';
|
|
2
3
|
import type { AuthSocialConfig, ResolvedRateLimitConfig } from '../define_config.js';
|
|
3
4
|
import type { PolicyRouteOption } from './config_locks.js';
|
|
4
5
|
import type { AuthHostOptions } from './register_auth_host.js';
|
|
@@ -35,10 +36,13 @@ export interface AuthHostRuntimeConfig {
|
|
|
35
36
|
* Ver `deriveLockedRouteOptions`.
|
|
36
37
|
*/
|
|
37
38
|
lockedRouteOptions?: PolicyRouteOption[];
|
|
38
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* Personal agents ligados → prefixo das rotas do PACT (`/agents`) e as
|
|
41
|
+
* `personalAgents.extensions` resolvidas (rotas, isenções de CSRF, navegador do agente).
|
|
42
|
+
*/
|
|
39
43
|
personalAgents?: {
|
|
40
44
|
prefix: string;
|
|
41
|
-
|
|
45
|
+
extensions?: readonly ResolvedPersonalAgentExtension[];
|
|
42
46
|
};
|
|
43
47
|
/**
|
|
44
48
|
* `accountSession.acceptIdpSession: false` com `authMethods: { password: false }`:
|
|
@@ -38,16 +38,19 @@ export default class AccountAppsController {
|
|
|
38
38
|
scopes: g.scopes,
|
|
39
39
|
})))
|
|
40
40
|
: null;
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
41
|
+
// Conexões das `personalAgents.extensions`: desconectadas pela mesma lista.
|
|
42
|
+
let extensionAgents = null;
|
|
43
|
+
for (const { runtime } of agentsRuntime?.extensions.values() ?? []) {
|
|
44
|
+
extensionAgents ??= [];
|
|
45
|
+
for (const g of await runtime.listAccountGrants(accountId)) {
|
|
46
|
+
extensionAgents.push({
|
|
47
|
+
id: g.id,
|
|
48
|
+
name: await runtime.displayName(g.clientId),
|
|
49
|
+
scopes: g.scopes,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const agents = pactAgents || extensionAgents ? [...(pactAgents ?? []), ...(extensionAgents ?? [])] : null;
|
|
51
54
|
return render(ctx, 'account/apps', {
|
|
52
55
|
csrfToken: ctx.request.csrfToken,
|
|
53
56
|
supported,
|
|
@@ -89,8 +92,9 @@ export default class AccountAppsController {
|
|
|
89
92
|
/** POST /account/apps/agents/:grantId/revoke — revoga a delegação de um personal agent. */
|
|
90
93
|
async revokeAgent(ctx) {
|
|
91
94
|
const runtime = await personalAgentsFor(ctx);
|
|
92
|
-
if (!runtime
|
|
95
|
+
if (!runtime || (!runtime.delegation && runtime.extensions.size === 0)) {
|
|
93
96
|
return ctx.response.notFound();
|
|
97
|
+
}
|
|
94
98
|
const service = await ctx.containerResolver.make('authkit.server');
|
|
95
99
|
const cfg = service.config;
|
|
96
100
|
const accountId = ctx.session.get(ACCOUNT_SESSION_KEY);
|
package/build/src/host/csrf.d.ts
CHANGED
|
@@ -11,10 +11,10 @@ export interface AuthkitCsrfOptions {
|
|
|
11
11
|
*/
|
|
12
12
|
personalAgentsPrefix?: string | false;
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* Isenta os `csrfExemptPaths` das `personalAgents.extensions` do config. Default: true.
|
|
15
|
+
* `false` desliga.
|
|
16
16
|
*/
|
|
17
|
-
|
|
17
|
+
personalAgentExtensions?: boolean;
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
20
|
* Retorna `true` quando `url` é uma rota AuthKit que deve ser ISENTA de CSRF
|
package/build/src/host/csrf.js
CHANGED
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
* ```
|
|
17
17
|
*/
|
|
18
18
|
import { normalizePersonalAgentsPrefix } from '../agents/config.js';
|
|
19
|
-
import { normalizePoppyPrefix } from '../agents/poppy/config.js';
|
|
20
19
|
import { getAuthHostConfig } from './auth_host_config.js';
|
|
21
20
|
/**
|
|
22
21
|
* Retorna `true` quando `url` é uma rota AuthKit que deve ser ISENTA de CSRF
|
|
@@ -34,21 +33,12 @@ export function authkitCsrfExceptions(url, options = {}) {
|
|
|
34
33
|
? normalizePersonalAgentsPrefix(options.personalAgentsPrefix)
|
|
35
34
|
: getAuthHostConfig()?.personalAgents?.prefix;
|
|
36
35
|
const agentsOAuth = agentsPrefix ? `${agentsPrefix}/oauth/` : null;
|
|
37
|
-
const poppyPrefix = options.poppyPrefix === false
|
|
38
|
-
? undefined
|
|
39
|
-
: options.poppyPrefix !== undefined
|
|
40
|
-
? normalizePoppyPrefix(options.poppyPrefix)
|
|
41
|
-
: getAuthHostConfig()?.personalAgents?.poppyPrefix;
|
|
42
36
|
const path = url.split('?')[0];
|
|
43
|
-
const
|
|
44
|
-
(
|
|
45
|
-
path === `${poppyPrefix}/oauth/revoke` ||
|
|
46
|
-
path === `${poppyPrefix}/oauth/device` ||
|
|
47
|
-
path === `${poppyPrefix}/browser-session` ||
|
|
48
|
-
path === `${poppyPrefix}/browser-session/authorize`);
|
|
37
|
+
const extensionExempt = options.personalAgentExtensions !== false &&
|
|
38
|
+
(getAuthHostConfig()?.personalAgents?.extensions ?? []).some((e) => e.csrfExemptPaths?.includes(path));
|
|
49
39
|
return (url.includes(mountPath) ||
|
|
50
40
|
url.includes('/authkit/pat') ||
|
|
51
41
|
(backchannel !== null && url === backchannel) ||
|
|
52
42
|
(agentsOAuth !== null && url.startsWith(agentsOAuth)) ||
|
|
53
|
-
|
|
43
|
+
extensionExempt);
|
|
54
44
|
}
|
|
@@ -67,15 +67,20 @@ export declare function sendMagicLinkEmail(ctx: HttpContext, data: {
|
|
|
67
67
|
channel?: 'code' | 'link';
|
|
68
68
|
}): Promise<void>;
|
|
69
69
|
/**
|
|
70
|
-
* Envia
|
|
71
|
-
*
|
|
72
|
-
*
|
|
70
|
+
* Envia um e-mail só com um código (sem link: quem digita é a pessoa, em outro lugar) pelo mailer
|
|
71
|
+
* default do host, com o branding e o i18n do authkit. Os textos saem do catálogo pelas chaves
|
|
72
|
+
* `{messages}.subject`, `.heading`, `.intro`, `.code_label` e `.fallback`, com `params`
|
|
73
|
+
* interpolados — uma extension registra as dela em `messages`. Best-effort: sem `@adonisjs/mail`
|
|
74
|
+
* loga o código (dev); nunca lança.
|
|
75
|
+
*
|
|
76
|
+
* Estável no 1.x (é o mailer das `personalAgents.extensions`).
|
|
73
77
|
*/
|
|
74
|
-
export declare function
|
|
78
|
+
export declare function sendAuthkitCodeEmail(ctx: HttpContext, data: {
|
|
75
79
|
email: string;
|
|
76
80
|
code: string;
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
/** Prefixo das chaves no catálogo (`'mail.poppy_sign_in'`). */
|
|
82
|
+
messages: string;
|
|
83
|
+
params?: Record<string, string | number>;
|
|
79
84
|
}): Promise<void>;
|
|
80
85
|
/**
|
|
81
86
|
* Envia o link de CONFIRMAÇÃO DE IDENTIDADE (sudo) pelo mailer default do host.
|
|
@@ -295,32 +295,36 @@ export async function sendMagicLinkEmail(ctx, data) {
|
|
|
295
295
|
}
|
|
296
296
|
}
|
|
297
297
|
/**
|
|
298
|
-
* Envia
|
|
299
|
-
*
|
|
300
|
-
*
|
|
298
|
+
* Envia um e-mail só com um código (sem link: quem digita é a pessoa, em outro lugar) pelo mailer
|
|
299
|
+
* default do host, com o branding e o i18n do authkit. Os textos saem do catálogo pelas chaves
|
|
300
|
+
* `{messages}.subject`, `.heading`, `.intro`, `.code_label` e `.fallback`, com `params`
|
|
301
|
+
* interpolados — uma extension registra as dela em `messages`. Best-effort: sem `@adonisjs/mail`
|
|
302
|
+
* loga o código (dev); nunca lança.
|
|
303
|
+
*
|
|
304
|
+
* Estável no 1.x (é o mailer das `personalAgents.extensions`).
|
|
301
305
|
*/
|
|
302
|
-
export async function
|
|
306
|
+
export async function sendAuthkitCodeEmail(ctx, data) {
|
|
303
307
|
try {
|
|
304
308
|
const brand = resolveBrand(ctx);
|
|
305
309
|
const { messages: t, locale } = resolveMailMessages(ctx);
|
|
306
|
-
const
|
|
310
|
+
const key = (name) => `${data.messages}.${name}`;
|
|
307
311
|
const content = renderTransactionalEmail({
|
|
308
312
|
brand,
|
|
309
313
|
locale,
|
|
310
|
-
subject: translate(t, '
|
|
311
|
-
heading: translate(t, '
|
|
312
|
-
intro: translate(t, '
|
|
313
|
-
footnote: translate(t, '
|
|
314
|
+
subject: translate(t, key('subject'), data.params),
|
|
315
|
+
heading: translate(t, key('heading'), data.params),
|
|
316
|
+
intro: translate(t, key('intro'), data.params),
|
|
317
|
+
footnote: translate(t, key('fallback'), data.params),
|
|
314
318
|
code: data.code,
|
|
315
|
-
codeLabel: translate(t, '
|
|
319
|
+
codeLabel: translate(t, key('code_label'), data.params),
|
|
316
320
|
});
|
|
317
321
|
const sent = await sendEmail(ctx, data.email, content);
|
|
318
322
|
if (!sent) {
|
|
319
|
-
ctx.logger.info({ email: data.email,
|
|
323
|
+
ctx.logger.info({ email: data.email, code: data.code, messages: data.messages, ...(data.params ?? {}) }, 'authkit: código por e-mail (dev — @adonisjs/mail ausente)');
|
|
320
324
|
}
|
|
321
325
|
}
|
|
322
326
|
catch (error) {
|
|
323
|
-
ctx.logger.error({ err: error, email: data.email }, 'authkit: falha ao enviar o
|
|
327
|
+
ctx.logger.error({ err: error, email: data.email, messages: data.messages }, 'authkit: falha ao enviar o e-mail com código');
|
|
324
328
|
}
|
|
325
329
|
}
|
|
326
330
|
/**
|
package/build/src/host/i18n.d.ts
CHANGED
|
@@ -560,11 +560,6 @@ export declare const DEFAULT_MESSAGES: {
|
|
|
560
560
|
'mail.magic_link.code_subject': string;
|
|
561
561
|
'mail.magic_link.code_intro': string;
|
|
562
562
|
'mail.magic_link.code_only_label': string;
|
|
563
|
-
'mail.poppy_sign_in.subject': string;
|
|
564
|
-
'mail.poppy_sign_in.heading': string;
|
|
565
|
-
'mail.poppy_sign_in.intro': string;
|
|
566
|
-
'mail.poppy_sign_in.code_label': string;
|
|
567
|
-
'mail.poppy_sign_in.fallback': string;
|
|
568
563
|
'mail.sudo_link.subject': string;
|
|
569
564
|
'mail.sudo_link.heading': string;
|
|
570
565
|
'mail.sudo_link.intro': string;
|
|
@@ -1318,11 +1313,6 @@ export declare const PT_BR_MESSAGES: {
|
|
|
1318
1313
|
'mail.magic_link.code_subject': string;
|
|
1319
1314
|
'mail.magic_link.code_intro': string;
|
|
1320
1315
|
'mail.magic_link.code_only_label': string;
|
|
1321
|
-
'mail.poppy_sign_in.subject': string;
|
|
1322
|
-
'mail.poppy_sign_in.heading': string;
|
|
1323
|
-
'mail.poppy_sign_in.intro': string;
|
|
1324
|
-
'mail.poppy_sign_in.code_label': string;
|
|
1325
|
-
'mail.poppy_sign_in.fallback': string;
|
|
1326
1316
|
'mail.sudo_link.subject': string;
|
|
1327
1317
|
'mail.sudo_link.heading': string;
|
|
1328
1318
|
'mail.sudo_link.intro': string;
|
|
@@ -1547,11 +1537,10 @@ export declare const PT_BR_MESSAGES: {
|
|
|
1547
1537
|
*/
|
|
1548
1538
|
export declare const BUILTIN_MESSAGES: Record<string, AuthMessages>;
|
|
1549
1539
|
/**
|
|
1550
|
-
*
|
|
1551
|
-
*
|
|
1552
|
-
* overrides do host por cima. Sem config, retorna o default `en` intacto.
|
|
1553
|
-
* Chaves omitidas caem no default `en` (fallback de cobertura).
|
|
1540
|
+
* Soma os catálogos de uma extension (`messages`) aos embutidos. Chamado quando o config resolve;
|
|
1541
|
+
* os overrides do host (`i18n.messages`) continuam vencendo.
|
|
1554
1542
|
*/
|
|
1543
|
+
export declare function registerExtensionMessages(catalogs: Readonly<Record<string, Readonly<Record<string, string>>>>): void;
|
|
1555
1544
|
export declare function resolveMessages(i18n?: I18nConfig): AuthMessages;
|
|
1556
1545
|
/**
|
|
1557
1546
|
* Retorna a string para `key` (cai na própria `key` quando ausente) com
|
package/build/src/host/i18n.js
CHANGED
|
@@ -603,12 +603,6 @@ export const DEFAULT_MESSAGES = {
|
|
|
603
603
|
'mail.magic_link.code_subject': 'Your login code',
|
|
604
604
|
'mail.magic_link.code_intro': 'Use the code below to sign in. It expires shortly and can be used once.',
|
|
605
605
|
'mail.magic_link.code_only_label': 'Enter this code to sign in:',
|
|
606
|
-
// Código do Mediated Sign-In do Poppy: um personal agent pede para entrar na conta.
|
|
607
|
-
'mail.poppy_sign_in.subject': 'Your code to connect {agent}',
|
|
608
|
-
'mail.poppy_sign_in.heading': 'Connect {agent} to your account',
|
|
609
|
-
'mail.poppy_sign_in.intro': '{agent} asked to sign in to your account. If that was you, give it the code below. It expires in {minutes} minutes and works once.',
|
|
610
|
-
'mail.poppy_sign_in.code_label': 'Your code:',
|
|
611
|
-
'mail.poppy_sign_in.fallback': 'If you did not ask for this, ignore this email — nobody gets in without the code. Never share it with anyone else.',
|
|
612
606
|
// Link de CONFIRMAÇÃO DE IDENTIDADE (sudo). Copy DISTINTA da do magic link de
|
|
613
607
|
// login de propósito: este link não autentica ninguém — quem o abre já está
|
|
614
608
|
// logado e só reprova que é quem diz ser. Prometer "entre na sua conta" num
|
|
@@ -1447,12 +1441,6 @@ export const PT_BR_MESSAGES = {
|
|
|
1447
1441
|
'mail.magic_link.code_subject': 'Seu código de login',
|
|
1448
1442
|
'mail.magic_link.code_intro': 'Use o código abaixo para entrar. Ele expira em instantes e serve para um único acesso.',
|
|
1449
1443
|
'mail.magic_link.code_only_label': 'Digite este código para entrar:',
|
|
1450
|
-
// Código do Mediated Sign-In do Poppy — ver o comentário no catálogo `en`.
|
|
1451
|
-
'mail.poppy_sign_in.subject': 'Seu código para conectar {agent}',
|
|
1452
|
-
'mail.poppy_sign_in.heading': 'Conecte {agent} à sua conta',
|
|
1453
|
-
'mail.poppy_sign_in.intro': '{agent} pediu para entrar na sua conta. Se foi você, informe o código abaixo. Ele expira em {minutes} minutos e serve uma única vez.',
|
|
1454
|
-
'mail.poppy_sign_in.code_label': 'Seu código:',
|
|
1455
|
-
'mail.poppy_sign_in.fallback': 'Se não foi você, ignore este e-mail — ninguém entra sem o código. Nunca o compartilhe com outra pessoa.',
|
|
1456
1444
|
// Link de CONFIRMAÇÃO DE IDENTIDADE (sudo) — ver o comentário no catálogo `en`.
|
|
1457
1445
|
'mail.sudo_link.subject': 'Confirme que é você',
|
|
1458
1446
|
'mail.sudo_link.heading': 'Confirme sua identidade',
|
|
@@ -1713,13 +1701,27 @@ export const BUILTIN_MESSAGES = {
|
|
|
1713
1701
|
* overrides do host por cima. Sem config, retorna o default `en` intacto.
|
|
1714
1702
|
* Chaves omitidas caem no default `en` (fallback de cobertura).
|
|
1715
1703
|
*/
|
|
1704
|
+
/** Catálogos das `personalAgents.extensions`, por locale (ver {@link registerExtensionMessages}). */
|
|
1705
|
+
const extensionMessages = {};
|
|
1706
|
+
/**
|
|
1707
|
+
* Soma os catálogos de uma extension (`messages`) aos embutidos. Chamado quando o config resolve;
|
|
1708
|
+
* os overrides do host (`i18n.messages`) continuam vencendo.
|
|
1709
|
+
*/
|
|
1710
|
+
export function registerExtensionMessages(catalogs) {
|
|
1711
|
+
for (const [locale, messages] of Object.entries(catalogs)) {
|
|
1712
|
+
extensionMessages[locale] = { ...(extensionMessages[locale] ?? {}), ...messages };
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1716
1715
|
export function resolveMessages(i18n) {
|
|
1717
1716
|
const locale = i18n?.locale ?? DEFAULT_LOCALE;
|
|
1718
1717
|
// Base: sempre o default `en` para garantir cobertura total das chaves; o
|
|
1719
|
-
// catálogo embutido do locale (ex.: pt-BR) é mesclado por cima.
|
|
1718
|
+
// catálogo embutido do locale (ex.: pt-BR) é mesclado por cima. Os das extensions entram
|
|
1719
|
+
// em cada camada, abaixo dos overrides do host.
|
|
1720
1720
|
const base = {
|
|
1721
1721
|
...DEFAULT_MESSAGES,
|
|
1722
|
+
...(extensionMessages[DEFAULT_LOCALE] ?? {}),
|
|
1722
1723
|
...(BUILTIN_MESSAGES[locale] ?? {}),
|
|
1724
|
+
...(locale === DEFAULT_LOCALE ? {} : (extensionMessages[locale] ?? {})),
|
|
1723
1725
|
};
|
|
1724
1726
|
const overrides = i18n?.messages?.[locale];
|
|
1725
1727
|
if (!overrides)
|
|
@@ -12,7 +12,6 @@ import { getAuthHostConfig, markAuthHostAutoMounted, wasAuthHostAutoMounted, } f
|
|
|
12
12
|
import { consoleDeniedPage } from './console_denied_page.js';
|
|
13
13
|
import { CONSOLE_SSO_CALLBACK_PATH, CONSOLE_SSO_START_PATH } from './console_sso.js';
|
|
14
14
|
import { endBridgedIdpSession, ensureConsoleSession } from './idp_session_bridge.js';
|
|
15
|
-
import { POPPY_WEB_SESSION_KEY } from './poppy_session_key.js';
|
|
16
15
|
import { createAuthThrottles } from './rate_limit.js';
|
|
17
16
|
import { redirectExact } from './redirect_exact.js';
|
|
18
17
|
import { requestPathWithQuery } from './request_url.js';
|
|
@@ -121,10 +120,21 @@ function buildLoginRedirect(ctx, extra) {
|
|
|
121
120
|
* classe NÃO era aplicada em runtime num grupo, deixando /account/tokens e
|
|
122
121
|
* /account/mfa acessíveis sem sessão.
|
|
123
122
|
*/
|
|
124
|
-
/**
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
123
|
+
/**
|
|
124
|
+
* A sessão é do navegador de um personal agent (`isAgentBrowser` de uma extension)? Um hook que
|
|
125
|
+
* lança conta como "é": na dúvida, o console fica fechado.
|
|
126
|
+
*/
|
|
127
|
+
function isAgentBrowser(ctx) {
|
|
128
|
+
for (const extension of getAuthHostConfig()?.personalAgents?.extensions ?? []) {
|
|
129
|
+
try {
|
|
130
|
+
if (extension.isAgentBrowser?.(ctx))
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return false;
|
|
128
138
|
}
|
|
129
139
|
/**
|
|
130
140
|
* `true` when the caller wants JSON (an `Accept` that prefers `application/json`,
|
|
@@ -149,9 +159,9 @@ function unauthenticated(ctx, redirectTo) {
|
|
|
149
159
|
return redirectExact(ctx.response, redirectTo);
|
|
150
160
|
}
|
|
151
161
|
const accountGuard = async (ctx, next) => {
|
|
152
|
-
// O navegador de um personal agent
|
|
153
|
-
//
|
|
154
|
-
if (
|
|
162
|
+
// O navegador de um personal agent não entra no console de conta nem nas telas de
|
|
163
|
+
// consentimento: a sessão é do agente, não do usuário.
|
|
164
|
+
if (isAgentBrowser(ctx))
|
|
155
165
|
return ctx.response.status(403).send('');
|
|
156
166
|
// Sessão do console — ou, com `accountSession.acceptIdpSession`, a do IdP (SSO).
|
|
157
167
|
if (!(await ensureConsoleSession(ctx))) {
|
|
@@ -186,7 +196,7 @@ export const adminGuard = async (ctx, next) => {
|
|
|
186
196
|
if (!cfg.admin.enabled) {
|
|
187
197
|
return ctx.response.notFound();
|
|
188
198
|
}
|
|
189
|
-
if (
|
|
199
|
+
if (isAgentBrowser(ctx))
|
|
190
200
|
return ctx.response.status(403).send('');
|
|
191
201
|
// Com `accountSession.acceptIdpSession`, a sessão do IdP também abre o console.
|
|
192
202
|
await ensureConsoleSession(ctx);
|
|
@@ -289,8 +299,6 @@ const C = {
|
|
|
289
299
|
accountConfirm: () => import('./controllers/account_confirm_controller.js'),
|
|
290
300
|
agentOAuth: () => import('./controllers/agent_oauth_controller.js'),
|
|
291
301
|
agentConsent: () => import('./controllers/agent_consent_controller.js'),
|
|
292
|
-
poppy: () => import('./controllers/poppy_controller.js'),
|
|
293
|
-
poppyConsent: () => import('./controllers/poppy_consent_controller.js'),
|
|
294
302
|
webauthnAsset: () => import('./controllers/webauthn_asset_controller.js'),
|
|
295
303
|
logoutAsset: () => import('./controllers/logout_asset_controller.js'),
|
|
296
304
|
passkeyAutofillAsset: () => import('./controllers/passkey_autofill_asset_controller.js'),
|
|
@@ -671,56 +679,13 @@ export function registerAuthHost(router, opts = {}) {
|
|
|
671
679
|
})
|
|
672
680
|
.use([accountGuard]);
|
|
673
681
|
}
|
|
674
|
-
//
|
|
675
|
-
//
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
// Nomes explícitos: o Router nomeia pela dupla controller.método, e as duas URLs da metadata
|
|
682
|
-
// (RFC 8414 na raiz e debaixo do issuer) usam o mesmo — "poppy.metadata already exists" no boot.
|
|
683
|
-
router.get('/.well-known/poppy.json', [C.poppy, 'discovery']).as('authkit.poppy.discovery');
|
|
684
|
-
router
|
|
685
|
-
.get('/.well-known/oauth-protected-resource', [C.poppy, 'protectedResource'])
|
|
686
|
-
.as('authkit.poppy.protected_resource');
|
|
687
|
-
router
|
|
688
|
-
.get(`/.well-known/oauth-authorization-server${poppyPrefix}`, [C.poppy, 'metadata'])
|
|
689
|
-
.as('authkit.poppy.metadata');
|
|
690
|
-
router
|
|
691
|
-
.get(`${poppyPrefix}/.well-known/oauth-authorization-server`, [C.poppy, 'metadata'])
|
|
692
|
-
.as('authkit.poppy.metadata_under_issuer');
|
|
693
|
-
router.post(`${poppyPrefix}/oauth/token`, [C.poppy, 'token']).as('authkit.poppy.token');
|
|
694
|
-
router.post(`${poppyPrefix}/oauth/revoke`, [C.poppy, 'revoke']).as('authkit.poppy.revoke');
|
|
695
|
-
router
|
|
696
|
-
.post(`${poppyPrefix}/oauth/device`, [C.poppy, 'deviceAuthorization'])
|
|
697
|
-
.as('authkit.poppy.device_authorization');
|
|
698
|
-
// A página grava um pedido sem autenticação: throttle por IP.
|
|
699
|
-
withLogin(router
|
|
700
|
-
.get(`${poppyPrefix}/browser-session`, [C.poppy, 'browserSessionPage'])
|
|
701
|
-
.as('authkit.poppy.browser_session_page'));
|
|
702
|
-
router
|
|
703
|
-
.post(`${poppyPrefix}/browser-session`, [C.poppy, 'browserSession'])
|
|
704
|
-
.as('authkit.poppy.browser_session');
|
|
705
|
-
router
|
|
706
|
-
.post(`${poppyPrefix}/browser-session/authorize`, [C.poppy, 'browserSessionAuthorize'])
|
|
707
|
-
.as('authkit.poppy.browser_session_authorize');
|
|
708
|
-
router
|
|
709
|
-
.group(() => {
|
|
710
|
-
router
|
|
711
|
-
.get(`${poppyPrefix}/oauth/authorize`, [C.poppyConsent, 'authorize'])
|
|
712
|
-
.as('authkit.poppy.authorize');
|
|
713
|
-
router
|
|
714
|
-
.post(`${poppyPrefix}/oauth/authorize`, [C.poppyConsent, 'decideAuthorization'])
|
|
715
|
-
.as('authkit.poppy.authorize_decide');
|
|
716
|
-
withOtpLogin(router
|
|
717
|
-
.get(`${poppyPrefix}/device`, [C.poppyConsent, 'device'])
|
|
718
|
-
.as('authkit.poppy.device'));
|
|
719
|
-
withOtpLogin(router
|
|
720
|
-
.post(`${poppyPrefix}/device`, [C.poppyConsent, 'decideDevice'])
|
|
721
|
-
.as('authkit.poppy.device_decide'));
|
|
722
|
-
})
|
|
723
|
-
.use([accountGuard]);
|
|
682
|
+
// `personalAgents.extensions`: cada uma monta as rotas dela, com o guard de conta e os
|
|
683
|
+
// buckets de rate limit das telas embutidas.
|
|
684
|
+
for (const extension of hostCfg?.personalAgents?.extensions ?? []) {
|
|
685
|
+
extension.routes?.(router, {
|
|
686
|
+
accountGuard,
|
|
687
|
+
throttle: { login: (route) => withLogin(route), otp: (route) => withOtpLogin(route) },
|
|
688
|
+
});
|
|
724
689
|
}
|
|
725
690
|
// Paths do console de conta (configuráveis/localizáveis via `accountRoutes`).
|
|
726
691
|
// As TELAS vêm de `accountPath(key)` (prefixo + segmento configurável); os
|
|
@@ -1032,6 +997,8 @@ export function registerAuthHost(router, opts = {}) {
|
|
|
1032
997
|
router.get(`${ap}/api/clients/:id`, [C.consoleClients, 'show']);
|
|
1033
998
|
router.patch(`${ap}/api/clients/:id`, [C.consoleClients, 'update']);
|
|
1034
999
|
router.delete(`${ap}/api/clients/:id`, [C.consoleClients, 'destroy']);
|
|
1000
|
+
router.post(`${ap}/api/clients/:id/disable`, [C.consoleClients, 'disable']);
|
|
1001
|
+
router.post(`${ap}/api/clients/:id/enable`, [C.consoleClients, 'enable']);
|
|
1035
1002
|
router.post(`${ap}/api/clients/:id/regenerate-secret`, [
|
|
1036
1003
|
C.consoleClients,
|
|
1037
1004
|
'regenerateSecret',
|
|
@@ -1131,6 +1098,8 @@ export function registerAuthHost(router, opts = {}) {
|
|
|
1131
1098
|
withApiThrottle(router.patch('/clients/:id', [C.apiClients, 'update']));
|
|
1132
1099
|
withApiThrottle(router.post('/clients/:id/regenerate-secret', [C.apiClients, 'regenerateSecret']));
|
|
1133
1100
|
withApiThrottle(router.delete('/clients/:id', [C.apiClients, 'destroy']));
|
|
1101
|
+
withApiThrottle(router.post('/clients/:id/disable', [C.apiClients, 'disable']));
|
|
1102
|
+
withApiThrottle(router.post('/clients/:id/enable', [C.apiClients, 'enable']));
|
|
1134
1103
|
// Organizações.
|
|
1135
1104
|
withApiThrottle(router.get('/organizations', [C.apiOrgs, 'index']));
|
|
1136
1105
|
withApiThrottle(router.post('/organizations', [C.apiOrgs, 'store']));
|