@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.
Files changed (98) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +4 -5
  3. package/build/commands/clients_create.d.ts +4 -0
  4. package/build/commands/clients_create.js +97 -0
  5. package/build/commands/commands.json +14 -0
  6. package/build/index.d.ts +8 -11
  7. package/build/index.js +5 -9
  8. package/build/providers/authkit_server_provider.js +6 -8
  9. package/build/src/agents/config.d.ts +9 -13
  10. package/build/src/agents/config.js +17 -11
  11. package/build/src/agents/delegation_service.js +3 -3
  12. package/build/src/agents/delegation_store.d.ts +3 -2
  13. package/build/src/agents/delegation_store.js +6 -5
  14. package/build/src/agents/extension.d.ts +261 -0
  15. package/build/src/agents/extension.js +40 -0
  16. package/build/src/agents/middleware.js +1 -1
  17. package/build/src/agents/protocol.d.ts +4 -4
  18. package/build/src/agents/registry_store.d.ts +3 -2
  19. package/build/src/agents/registry_store.js +1 -1
  20. package/build/src/agents/runtime.d.ts +26 -7
  21. package/build/src/agents/runtime.js +110 -75
  22. package/build/src/audit/audit_sink.d.ts +1 -1
  23. package/build/src/audit/audit_sink.js +6 -1
  24. package/build/src/define_config.d.ts +16 -12
  25. package/build/src/define_config.js +11 -7
  26. package/build/src/host/access_token_verifier.js +7 -0
  27. package/build/src/host/account_api/account_api_controller.js +8 -6
  28. package/build/src/host/account_session_sign_in.d.ts +12 -0
  29. package/build/src/host/account_session_sign_in.js +19 -0
  30. package/build/src/host/admin_api/api_clients_controller.d.ts +37 -0
  31. package/build/src/host/admin_api/api_clients_controller.js +31 -1
  32. package/build/src/host/admin_api/dto.d.ts +3 -1
  33. package/build/src/host/admin_api/dto.js +2 -0
  34. package/build/src/host/admin_clients_service.d.ts +20 -0
  35. package/build/src/host/admin_clients_service.js +55 -0
  36. package/build/src/host/admin_console/console_clients_controller.d.ts +41 -0
  37. package/build/src/host/admin_console/console_clients_controller.js +34 -1
  38. package/build/src/host/admin_console/console_personal_agents_controller.d.ts +1 -6
  39. package/build/src/host/admin_console/console_personal_agents_controller.js +50 -30
  40. package/build/src/host/admin_sessions_service.js +8 -5
  41. package/build/src/host/admin_validators.d.ts +29 -0
  42. package/build/src/host/admin_validators.js +30 -0
  43. package/build/src/host/auth_host_config.d.ts +6 -2
  44. package/build/src/host/controllers/account_apps_controller.js +15 -11
  45. package/build/src/host/csrf.d.ts +3 -3
  46. package/build/src/host/csrf.js +3 -13
  47. package/build/src/host/default_mailer.d.ts +11 -6
  48. package/build/src/host/default_mailer.js +16 -12
  49. package/build/src/host/i18n.d.ts +3 -14
  50. package/build/src/host/i18n.js +15 -13
  51. package/build/src/host/register_auth_host.js +30 -61
  52. package/build/src/host/ui-dist/assets/index-u0bMxkLg.js +67 -0
  53. package/build/src/host/ui-dist/index.html +1 -1
  54. package/build/src/provider/build_provider.js +37 -6
  55. package/build/src/provider/oidc_service.d.ts +1 -1
  56. package/build/src/provider/oidc_service.js +1 -1
  57. package/build/src/schema/ensure.d.ts +14 -0
  58. package/build/src/schema/ensure.js +43 -150
  59. package/build/src/service_tenants/client_metadata.d.ts +39 -0
  60. package/build/src/service_tenants/client_metadata.js +80 -0
  61. package/build/src/service_tenants/config.d.ts +44 -0
  62. package/build/src/service_tenants/config.js +44 -0
  63. package/build/src/service_tenants/provider_hooks.d.ts +52 -0
  64. package/build/src/service_tenants/provider_hooks.js +124 -0
  65. package/build/src/service_tenants/tenant_verifier.d.ts +73 -0
  66. package/build/src/service_tenants/tenant_verifier.js +104 -0
  67. package/package.json +2 -2
  68. package/build/src/agents/poppy/client_registry.d.ts +0 -50
  69. package/build/src/agents/poppy/client_registry.js +0 -260
  70. package/build/src/agents/poppy/config.d.ts +0 -396
  71. package/build/src/agents/poppy/config.js +0 -228
  72. package/build/src/agents/poppy/dpop.d.ts +0 -61
  73. package/build/src/agents/poppy/dpop.js +0 -158
  74. package/build/src/agents/poppy/email_code_sign_in.d.ts +0 -27
  75. package/build/src/agents/poppy/email_code_sign_in.js +0 -140
  76. package/build/src/agents/poppy/errors.d.ts +0 -25
  77. package/build/src/agents/poppy/errors.js +0 -25
  78. package/build/src/agents/poppy/replay.d.ts +0 -25
  79. package/build/src/agents/poppy/replay.js +0 -106
  80. package/build/src/agents/poppy/resource.d.ts +0 -100
  81. package/build/src/agents/poppy/resource.js +0 -129
  82. package/build/src/agents/poppy/safe_fetch.d.ts +0 -48
  83. package/build/src/agents/poppy/safe_fetch.js +0 -262
  84. package/build/src/agents/poppy/service.d.ts +0 -293
  85. package/build/src/agents/poppy/service.js +0 -1458
  86. package/build/src/agents/poppy/session_jwt.d.ts +0 -30
  87. package/build/src/agents/poppy/session_jwt.js +0 -133
  88. package/build/src/agents/poppy/store.d.ts +0 -106
  89. package/build/src/agents/poppy/store.js +0 -261
  90. package/build/src/agents/poppy/web_session.d.ts +0 -39
  91. package/build/src/agents/poppy/web_session.js +0 -122
  92. package/build/src/host/controllers/poppy_consent_controller.d.ts +0 -19
  93. package/build/src/host/controllers/poppy_consent_controller.js +0 -199
  94. package/build/src/host/controllers/poppy_controller.d.ts +0 -33
  95. package/build/src/host/controllers/poppy_controller.js +0 -219
  96. package/build/src/host/poppy_session_key.d.ts +0 -9
  97. package/build/src/host/poppy_session_key.js +0 -9
  98. 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
- /** Personal agents ligados → prefixo das rotas deles (`/agents`) e do Poppy (`/poppy`). */
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
- poppyPrefix?: string;
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
- // Refresh tokens do Poppy: o agente é desconectado pela mesma lista (§5.5).
42
- const poppy = agentsRuntime?.poppy;
43
- const poppyAgents = poppy
44
- ? await Promise.all((await poppy.listGrants(accountId)).map(async (g) => ({
45
- id: g.id,
46
- name: await poppy.registry.displayName(g.clientId),
47
- scopes: g.scopes,
48
- })))
49
- : null;
50
- const agents = pactAgents || poppyAgents ? [...(pactAgents ?? []), ...(poppyAgents ?? [])] : null;
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?.delegation && !runtime?.poppy)
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);
@@ -11,10 +11,10 @@ export interface AuthkitCsrfOptions {
11
11
  */
12
12
  personalAgentsPrefix?: string | false;
13
13
  /**
14
- * Prefixo do Poppy: isenta token/revoke/device authorization e a entrada do navegador
15
- * (o código com o cookie `SameSite=Strict` é o que protege, §6).
14
+ * Isenta os `csrfExemptPaths` das `personalAgents.extensions` do config. Default: true.
15
+ * `false` desliga.
16
16
  */
17
- poppyPrefix?: string | false;
17
+ personalAgentExtensions?: boolean;
18
18
  }
19
19
  /**
20
20
  * Retorna `true` quando `url` é uma rota AuthKit que deve ser ISENTA de CSRF
@@ -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 poppyExempt = !!poppyPrefix &&
44
- (path === `${poppyPrefix}/oauth/token` ||
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
- poppyExempt);
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 o código do Mediated Sign-In pronto do Poppy (`poppyEmailCodeSignIn`) pelo
71
- * mailer default do host. Só código, sem link: quem digita é a pessoa, no agente.
72
- * Best-effort: no fallback (sem mail) loga o código, como o magic link loga o link; nunca lança.
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 sendPoppySignInCodeEmail(ctx: HttpContext, data: {
78
+ export declare function sendAuthkitCodeEmail(ctx: HttpContext, data: {
75
79
  email: string;
76
80
  code: string;
77
- agentName: string;
78
- expiresInMinutes: number;
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 o código do Mediated Sign-In pronto do Poppy (`poppyEmailCodeSignIn`) pelo
299
- * mailer default do host. Só código, sem link: quem digita é a pessoa, no agente.
300
- * Best-effort: no fallback (sem mail) loga o código, como o magic link loga o link; nunca lança.
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 sendPoppySignInCodeEmail(ctx, data) {
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 params = { agent: data.agentName, minutes: data.expiresInMinutes };
310
+ const key = (name) => `${data.messages}.${name}`;
307
311
  const content = renderTransactionalEmail({
308
312
  brand,
309
313
  locale,
310
- subject: translate(t, 'mail.poppy_sign_in.subject', params),
311
- heading: translate(t, 'mail.poppy_sign_in.heading', params),
312
- intro: translate(t, 'mail.poppy_sign_in.intro', params),
313
- footnote: translate(t, 'mail.poppy_sign_in.fallback'),
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, 'mail.poppy_sign_in.code_label'),
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, agent: data.agentName, code: data.code }, 'authkit: código do Poppy Mediated Sign-In (dev — @adonisjs/mail ausente)');
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 código do Poppy Mediated Sign-In');
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
  /**
@@ -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
- * Resolve o catálogo ativo. Começa do catálogo embutido do locale selecionado
1551
- * (ou do default `en` quando o locale não é embutido), depois mescla os
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
@@ -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
- /** A sessão é do navegador de um personal agent Poppy (§5)? */
125
- function isPoppyBrowser(ctx) {
126
- const marker = ctx.session?.get?.(POPPY_WEB_SESSION_KEY);
127
- return typeof marker === 'object' && marker !== null && typeof marker.sessionId === 'string';
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 Poppy (§5) não entra no console de conta nem
153
- // nas telas de consentimento: a sessão é do agente, não do usuário.
154
- if (isPoppyBrowser(ctx))
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 (isPoppyBrowser(ctx))
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
- // Personal Agent Protocol (Poppy): descoberta, authorization server (token,
675
- // revogação, device authorization), Mediated Sign-In e sessão de navegador —
676
- // server-to-server ou POST cross-site do navegador do agente (sem CSRF, ver
677
- // `authkitCsrfExceptions`). As telas do usuário (authorize/device) exigem a
678
- // sessão de conta e mantêm o CSRF.
679
- const poppyPrefix = hostCfg?.personalAgents?.poppyPrefix;
680
- if (poppyPrefix) {
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']));