@adonis-agora/authkit-server 1.0.0-rc.0 → 1.0.0-rc.10

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 (177) hide show
  1. package/CHANGELOG.md +111 -0
  2. package/README.md +4 -5
  3. package/build/commands/agora_doctor.d.ts +22 -0
  4. package/build/commands/agora_doctor.js +109 -0
  5. package/build/commands/clients_create.d.ts +4 -0
  6. package/build/commands/clients_create.js +97 -0
  7. package/build/commands/commands.json +78 -0
  8. package/build/commands/doctor.js +8 -74
  9. package/build/host/views/account/apps.edge +1 -5
  10. package/build/host/views/account/extension.edge +77 -0
  11. package/build/host/views/account/mfa.edge +2 -0
  12. package/build/host/views/account/partials/extension_actions.edge +14 -0
  13. package/build/host/views/account/partials/extension_block.edge +197 -0
  14. package/build/host/views/account/partials/extension_value.edge +8 -0
  15. package/build/host/views/account/partials/nav.edge +14 -0
  16. package/build/host/views/account/security.edge +1 -5
  17. package/build/host/views/account/tokens.edge +2 -2
  18. package/build/host/views/agents/consent.edge +11 -2
  19. package/build/host/views/agents/done.edge +1 -1
  20. package/build/host/views/partials/styles.edge +1 -1
  21. package/build/index.d.ts +14 -12
  22. package/build/index.js +8 -9
  23. package/build/providers/authkit_server_provider.js +36 -8
  24. package/build/src/account/extension.d.ts +10 -0
  25. package/build/src/account/extension.js +9 -0
  26. package/build/src/account/http.d.ts +77 -0
  27. package/build/src/account/http.js +381 -0
  28. package/build/src/account/mount.d.ts +40 -0
  29. package/build/src/account/mount.js +171 -0
  30. package/build/src/account/registry.d.ts +41 -0
  31. package/build/src/account/registry.js +81 -0
  32. package/build/src/account/remote.d.ts +72 -0
  33. package/build/src/account/remote.js +406 -0
  34. package/build/src/account/runtime.d.ts +289 -0
  35. package/build/src/account/runtime.js +641 -0
  36. package/build/src/accounts/account_store.d.ts +6 -0
  37. package/build/src/accounts/lucid_store/organizations.js +6 -0
  38. package/build/src/agents/config.d.ts +9 -13
  39. package/build/src/agents/config.js +17 -11
  40. package/build/src/agents/delegation_service.js +3 -3
  41. package/build/src/agents/delegation_store.d.ts +3 -2
  42. package/build/src/agents/delegation_store.js +6 -5
  43. package/build/src/agents/extension.d.ts +261 -0
  44. package/build/src/agents/extension.js +40 -0
  45. package/build/src/agents/middleware.js +1 -1
  46. package/build/src/agents/protocol.d.ts +4 -4
  47. package/build/src/agents/registry_store.d.ts +3 -2
  48. package/build/src/agents/registry_store.js +1 -1
  49. package/build/src/agents/runtime.d.ts +26 -7
  50. package/build/src/agents/runtime.js +110 -75
  51. package/build/src/audit/audit_sink.d.ts +1 -1
  52. package/build/src/audit/audit_sink.js +10 -1
  53. package/build/src/console_authorize.d.ts +56 -0
  54. package/build/src/console_authorize.js +85 -0
  55. package/build/src/console_switcher.d.ts +132 -0
  56. package/build/src/console_switcher.js +206 -0
  57. package/build/src/define_config.d.ts +54 -14
  58. package/build/src/define_config.js +17 -8
  59. package/build/src/dev_tools/contract.d.ts +72 -0
  60. package/build/src/dev_tools/contract.js +10 -0
  61. package/build/src/dev_tools/gate.d.ts +17 -0
  62. package/build/src/dev_tools/gate.js +69 -0
  63. package/build/src/dev_tools/register.d.ts +11 -0
  64. package/build/src/dev_tools/register.js +25 -0
  65. package/build/src/dev_tools/tools.d.ts +24 -0
  66. package/build/src/dev_tools/tools.js +384 -0
  67. package/build/src/doctor/agora_checks.d.ts +15 -0
  68. package/build/src/doctor/agora_checks.js +150 -0
  69. package/build/src/doctor/agora_doctor.d.ts +115 -0
  70. package/build/src/doctor/agora_doctor.js +692 -0
  71. package/build/src/doctor/checks.d.ts +7 -1
  72. package/build/src/doctor/checks.js +47 -1
  73. package/build/src/doctor/doctor_input.d.ts +27 -0
  74. package/build/src/doctor/doctor_input.js +86 -0
  75. package/build/src/doctor/index.d.ts +5 -0
  76. package/build/src/doctor/index.js +4 -0
  77. package/build/src/host/access_token_verifier.js +7 -0
  78. package/build/src/host/account_api/account_api_controller.js +8 -6
  79. package/build/src/host/account_session_sign_in.d.ts +12 -0
  80. package/build/src/host/account_session_sign_in.js +19 -0
  81. package/build/src/host/admin_api/admin_users_service.d.ts +5 -2
  82. package/build/src/host/admin_api/api_clients_controller.d.ts +37 -0
  83. package/build/src/host/admin_api/api_clients_controller.js +31 -1
  84. package/build/src/host/admin_api/dto.d.ts +3 -1
  85. package/build/src/host/admin_api/dto.js +2 -0
  86. package/build/src/host/admin_authorize.d.ts +8 -0
  87. package/build/src/host/admin_authorize.js +25 -0
  88. package/build/src/host/admin_clients_service.d.ts +20 -0
  89. package/build/src/host/admin_clients_service.js +65 -1
  90. package/build/src/host/admin_console/admin_console_entry.d.ts +14 -0
  91. package/build/src/host/admin_console/admin_console_entry.js +80 -0
  92. package/build/src/host/admin_console/admin_shell_controller.js +8 -0
  93. package/build/src/host/admin_console/console_clients_controller.d.ts +41 -0
  94. package/build/src/host/admin_console/console_clients_controller.js +34 -1
  95. package/build/src/host/admin_console/console_personal_agents_controller.d.ts +1 -6
  96. package/build/src/host/admin_console/console_personal_agents_controller.js +50 -30
  97. package/build/src/host/admin_console/console_switcher_controller.d.ts +11 -0
  98. package/build/src/host/admin_console/console_switcher_controller.js +23 -0
  99. package/build/src/host/admin_console_warning.d.ts +8 -0
  100. package/build/src/host/admin_console_warning.js +18 -0
  101. package/build/src/host/admin_sessions_service.js +8 -5
  102. package/build/src/host/admin_validators.d.ts +29 -0
  103. package/build/src/host/admin_validators.js +30 -0
  104. package/build/src/host/auth_host_config.d.ts +14 -2
  105. package/build/src/host/console_denied_page.d.ts +6 -0
  106. package/build/src/host/console_denied_page.js +18 -0
  107. package/build/src/host/console_sso.d.ts +24 -0
  108. package/build/src/host/console_sso.js +70 -0
  109. package/build/src/host/controllers/account_apps_controller.js +15 -11
  110. package/build/src/host/controllers/account_session_controller.js +10 -1
  111. package/build/src/host/controllers/console_sso_controller.d.ts +12 -0
  112. package/build/src/host/controllers/console_sso_controller.js +61 -0
  113. package/build/src/host/controllers/interaction_controller.js +3 -1
  114. package/build/src/host/csrf.d.ts +3 -3
  115. package/build/src/host/csrf.js +3 -13
  116. package/build/src/host/default_mailer.d.ts +11 -6
  117. package/build/src/host/default_mailer.js +16 -12
  118. package/build/src/host/i18n.d.ts +41 -14
  119. package/build/src/host/i18n.js +55 -13
  120. package/build/src/host/rate_limit.d.ts +8 -0
  121. package/build/src/host/rate_limit.js +9 -0
  122. package/build/src/host/register_auth_host.d.ts +16 -0
  123. package/build/src/host/register_auth_host.js +230 -88
  124. package/build/src/host/renderers/edge_renderer.js +5 -0
  125. package/build/src/host/renderers/inertia_renderer.js +7 -1
  126. package/build/src/host/screen_props.d.ts +39 -8
  127. package/build/src/host/ui-dist/assets/index-Dq3dJVBv.js +67 -0
  128. package/build/src/host/ui-dist/assets/{index-DTSmD4RU.css → index-WNIyCEPA.css} +1 -1
  129. package/build/src/host/ui-dist/index.html +2 -2
  130. package/build/src/provider/build_provider.js +43 -6
  131. package/build/src/provider/oidc_service.d.ts +1 -1
  132. package/build/src/provider/oidc_service.js +1 -1
  133. package/build/src/schema/ensure.d.ts +14 -0
  134. package/build/src/schema/ensure.js +43 -150
  135. package/build/src/service_tenants/client_metadata.d.ts +39 -0
  136. package/build/src/service_tenants/client_metadata.js +80 -0
  137. package/build/src/service_tenants/config.d.ts +44 -0
  138. package/build/src/service_tenants/config.js +44 -0
  139. package/build/src/service_tenants/provider_hooks.d.ts +52 -0
  140. package/build/src/service_tenants/provider_hooks.js +124 -0
  141. package/build/src/service_tenants/tenant_verifier.d.ts +73 -0
  142. package/build/src/service_tenants/tenant_verifier.js +104 -0
  143. package/build/src/tenancy/tenant_labels.d.ts +30 -0
  144. package/build/src/tenancy/tenant_labels.js +64 -0
  145. package/build/types.d.ts +3 -1
  146. package/package.json +12 -9
  147. package/build/src/agents/poppy/client_registry.d.ts +0 -50
  148. package/build/src/agents/poppy/client_registry.js +0 -260
  149. package/build/src/agents/poppy/config.d.ts +0 -396
  150. package/build/src/agents/poppy/config.js +0 -228
  151. package/build/src/agents/poppy/dpop.d.ts +0 -61
  152. package/build/src/agents/poppy/dpop.js +0 -158
  153. package/build/src/agents/poppy/email_code_sign_in.d.ts +0 -27
  154. package/build/src/agents/poppy/email_code_sign_in.js +0 -140
  155. package/build/src/agents/poppy/errors.d.ts +0 -25
  156. package/build/src/agents/poppy/errors.js +0 -25
  157. package/build/src/agents/poppy/replay.d.ts +0 -25
  158. package/build/src/agents/poppy/replay.js +0 -106
  159. package/build/src/agents/poppy/resource.d.ts +0 -100
  160. package/build/src/agents/poppy/resource.js +0 -129
  161. package/build/src/agents/poppy/safe_fetch.d.ts +0 -48
  162. package/build/src/agents/poppy/safe_fetch.js +0 -262
  163. package/build/src/agents/poppy/service.d.ts +0 -293
  164. package/build/src/agents/poppy/service.js +0 -1458
  165. package/build/src/agents/poppy/session_jwt.d.ts +0 -30
  166. package/build/src/agents/poppy/session_jwt.js +0 -133
  167. package/build/src/agents/poppy/store.d.ts +0 -106
  168. package/build/src/agents/poppy/store.js +0 -261
  169. package/build/src/agents/poppy/web_session.d.ts +0 -39
  170. package/build/src/agents/poppy/web_session.js +0 -122
  171. package/build/src/host/controllers/poppy_consent_controller.d.ts +0 -19
  172. package/build/src/host/controllers/poppy_consent_controller.js +0 -199
  173. package/build/src/host/controllers/poppy_controller.d.ts +0 -33
  174. package/build/src/host/controllers/poppy_controller.js +0 -219
  175. package/build/src/host/poppy_session_key.d.ts +0 -9
  176. package/build/src/host/poppy_session_key.js +0 -9
  177. package/build/src/host/ui-dist/assets/index-D0EIeyKK.js +0 -67
@@ -1,396 +0,0 @@
1
- /**
2
- * @experimental Draft 0.3 da spec (https://personalagentprotocol.org/docs/spec) — pode mudar de forma
3
- * incompatível fora de majors enquanto a spec for draft.
4
- *
5
- * Personal Agent Protocol ("Poppy", Draft 0.3 — https://personalagentprotocol.org) — config.
6
- *
7
- * Poppy é o segundo protocolo de personal agents ao lado do PACT. O agente se identifica pelo
8
- * `client_id` (uma URL HTTPS com o documento de metadata dele) e declara a Session em cada request
9
- * com o header `Poppy-Session`, um JWT assinado com uma chave delegada. O acesso à conta é OAuth
10
- * puro e separado: este app roda o authorization server, e o usuário entra por Direct (código +
11
- * PKCE), Device (RFC 8628) ou pela extensão Mediated Sign-In. Prender os access tokens com DPoP é
12
- * opcional (RFC 9449).
13
- *
14
- * Reaproveita o núcleo de delegação: o refresh token é um grant na tabela de grants de personal
15
- * agents (`auth_agent_grants`) — lista e revogação no console de conta são as mesmas do PACT — e os
16
- * scopes aprovados viram os `scopes` do principal (que o `@adonis-agora/agent` mapeia para roles
17
- * `scope:<id>`).
18
- */
19
- import type { HttpContext } from '@adonisjs/core/http';
20
- export type PoppySignInType = 'direct' | 'device' | 'mediated';
21
- /** Um campo de credencial do Mediated Sign-In (extensão Mediated Sign-In). */
22
- export interface PoppyMediatedField {
23
- /** Parâmetro que o agente manda no token endpoint. */
24
- name: string;
25
- /** O que pedir ao usuário. */
26
- label: string;
27
- /** Valor que o agente precisa proteger (senha, PIN). */
28
- secret: boolean;
29
- }
30
- /** Entrada do callback que verifica as credenciais do Mediated Sign-In. */
31
- export interface PoppyMediatedVerifyInput {
32
- credentials: Record<string, string>;
33
- /** Scopes pedidos (já validados contra os do tipo mediated). */
34
- scopes: string[];
35
- /** `client_id` do personal agent. */
36
- clientId: string;
37
- ctx: HttpContext;
38
- }
39
- /**
40
- * Resultado do callback de credenciais. Nunca devolva as credenciais.
41
- *
42
- * - `complete`: credenciais certas, nada mais a fazer — o agente recebe os tokens.
43
- * - `code_required`: o app MANDOU um código de uso único (SMS, e-mail, WhatsApp…)
44
- * e o agente precisa submetê-lo. Ou devolve o `code` que mandou (a lib guarda
45
- * só o hash e confere), ou deixa sem `code` e implementa `verifyCode` (ex.: um
46
- * serviço de OTP que confere do lado dele). `state` é opaco e volta no
47
- * `verifyCode`.
48
- * - `failed`: senha errada… (`invalid_grant`).
49
- * - `denied`: a empresa recusa este sign-in (`access_denied`).
50
- *
51
- * **Sem enumeração de contas.** Num fluxo só de identificador (e-mail/telefone + código), `failed`
52
- * para um identificador desconhecido conta ao agente quem tem conta. Devolva então `code_required`
53
- * com `accountId: null` (e o mesmo `sentTo` que uma conta real teria) e NÃO mande código: a lib
54
- * responde igual a uma conta real e recusa qualquer código como um código errado (mesmas
55
- * tentativas, mesmo `invalid_grant` no fim). Faça o mesmo trabalho nos dois caminhos (ex.: enfileire o
56
- * e-mail em vez de mandar na hora) para o tempo de resposta não denunciar.
57
- */
58
- export type PoppyMediatedResult = {
59
- status: 'complete';
60
- accountId: string;
61
- } | {
62
- status: 'code_required';
63
- /** `null` = identificador sem conta elegível: nenhum código vale (ver acima). */
64
- accountId: string | null;
65
- /** Texto para o usuário: "Text to the phone number ending in 71". */
66
- sentTo: string;
67
- code?: string;
68
- state?: unknown;
69
- } | {
70
- status: 'failed';
71
- } | {
72
- status: 'denied';
73
- };
74
- /**
75
- * @experimental Poppy (Personal Agent Protocol) Draft 0.3 — pode mudar de forma incompatível enquanto a spec for draft.
76
- */
77
- export interface PoppyMediatedConfigInput {
78
- /** Campos pedidos (extensão Mediated Sign-In). Ex.: `[{ name: 'email', … }, { name: 'password', secret: true, … }]`. */
79
- fields: PoppyMediatedField[];
80
- /** Scopes que o Mediated Sign-In pode conceder (`scopes` da extensão). */
81
- scopes: string[];
82
- /** Confere as credenciais (e, se for o caso, manda o código). */
83
- verify: (input: PoppyMediatedVerifyInput) => Promise<PoppyMediatedResult>;
84
- /**
85
- * Confere um código quando `verify` devolveu `code_required` SEM `code`.
86
- * `true` = código certo.
87
- */
88
- verifyCode?: (input: {
89
- code: string;
90
- accountId: string;
91
- state: unknown;
92
- clientId: string;
93
- }) => Promise<boolean>;
94
- /** Validade do sign-in pendente de código, em segundos. Default: 600. */
95
- codeTtl?: number;
96
- /** Tentativas de código antes de `invalid_grant`. Default: 5. */
97
- maxCodeAttempts?: number;
98
- }
99
- /** Uma entrada de `agent.protocols` (§3.1, §8.1). */
100
- export interface PoppyAgentProtocolEntry {
101
- /** `'poppy'` para as conversas deste protocolo (§8). */
102
- type: string;
103
- /** URL HTTPS do endpoint de conversas. */
104
- endpoint: string;
105
- /** A interface precisa da conta desde o início (§3.2). Default: `session`. */
106
- access?: 'session' | 'signed_in';
107
- /**
108
- * Só aceita tokens emitidos para este `resource` (§4.5). Não sai no `poppy.json`: publique a
109
- * protected resource metadata do serviço (RFC 9728) com este `resource`.
110
- */
111
- resource?: string;
112
- }
113
- /** Hints de um scope (§4.7): a ação de maior consequência que o exige. */
114
- export interface PoppyScopeHint {
115
- changes: 'none' | 'reversible' | 'irreversible';
116
- personal_data: boolean;
117
- }
118
- /** Uma entrada de `apis` (§3.1, §7). */
119
- export interface PoppyApiEntry {
120
- type: 'openapi' | 'mcp' | (string & {});
121
- url: string;
122
- description: string;
123
- /** Como em `agent.protocols`. MCP sempre exige o próprio `url` (§7). */
124
- resource?: string;
125
- }
126
- /** Hook de login do navegador do agente (§6) para apps com sessão de login própria. */
127
- export type PoppyBrowserLogin = (ctx: HttpContext, input: {
128
- sessionId: string;
129
- clientId: string;
130
- userId: string;
131
- /** Conta autorizada na entrada do navegador; `null` = entrada sem access token. */
132
- accountId: string | null;
133
- scopes: string[];
134
- }) => Promise<void>;
135
- /**
136
- * Onde o protocolo guarda os `jti` já vistos (client assertions, Session JWTs,
137
- * provas DPoP) até expirarem. `claim` devolve `true` na
138
- * primeira vez que vê a chave e `false` num replay.
139
- *
140
- * @experimental Poppy (Personal Agent Protocol) Draft 0.3 — acompanha a spec em desenvolvimento e PODE MUDAR
141
- * de forma incompatível fora de majors enquanto ela for draft.
142
- */
143
- export interface PoppyReplayStore {
144
- claim(key: string, expiresAt: Date): Promise<boolean>;
145
- }
146
- /** Limitador de taxa: `null` = passa; número = segundos até tentar de novo (429). */
147
- export type PoppyLimiter = (input: {
148
- action: 'session' | 'mediated';
149
- key: string;
150
- }) => Promise<number | null>;
151
- export interface PoppyClientsConfigInput {
152
- /**
153
- * `true` = só agentes registrados (`registered`) são aceitos; os demais levam
154
- * `invalid_client` ou `invalid_session` (§4.1). Default: false — qualquer `client_id` com um
155
- * documento de metadata válido.
156
- */
157
- requireRegistration?: boolean;
158
- /** `client_id`s registrados fora de banda (lista ou função, para quem guarda no banco). */
159
- registered?: string[] | ((clientId: string) => boolean | Promise<boolean>);
160
- /** `client_id`s bloqueados (revogados por mau uso). */
161
- blocked?: string[] | ((clientId: string) => boolean | Promise<boolean>);
162
- }
163
- /**
164
- * @experimental Poppy (Personal Agent Protocol) Draft 0.3 — pode mudar de forma incompatível enquanto a spec for draft.
165
- */
166
- export interface PoppyConfigInput {
167
- /** Nome exibido e domínio (`organization` do poppy.json). */
168
- organization: {
169
- name: string;
170
- /** Domínio da empresa (sem esquema). O poppy.json só é servido nele; `www.` redireciona. */
171
- domain: string;
172
- /** Outros domínios da mesma empresa (um por país…) — também vão para `protected_resources`. */
173
- aliases?: string[];
174
- };
175
- /** Prefixo das rotas e path do issuer. Default: `/poppy`. */
176
- prefix?: string;
177
- /**
178
- * Scopes do app (`auth.scopes`), `id → descrição`. O protocolo não define nenhum (§4.7).
179
- * Default: os de `personalAgents.delegation.scopes`, quando houver — um catálogo só para os
180
- * dois protocolos.
181
- */
182
- scopes?: Record<string, string>;
183
- /** Hints de cada scope (`auth.scope_hints`, §4.7). */
184
- scopeHints?: Record<string, PoppyScopeHint>;
185
- /** Tipos de sign-in. `false` desliga; um array restringe os scopes do tipo. */
186
- signIn?: {
187
- /** Default: todos os scopes. */
188
- direct?: boolean | {
189
- scopes?: string[];
190
- };
191
- /** Default: todos os scopes. */
192
- device?: boolean | {
193
- scopes?: string[];
194
- };
195
- /** Desligado por default: o app precisa conferir as credenciais. */
196
- mediated?: PoppyMediatedConfigInput;
197
- };
198
- /** Company Agent (§8) — o endpoint de conversas é implementado pelo `@adonis-agora/agent`. */
199
- agent?: {
200
- protocols: PoppyAgentProtocolEntry[];
201
- };
202
- /** APIs do app que aceitam o header da Session (§7). */
203
- apis?: PoppyApiEntry[];
204
- /** Endpoint de feedback que o app implementa (§9.3). */
205
- feedback?: {
206
- endpoint: string;
207
- };
208
- /** Documento público das tarefas que a empresa não oferece (§9.1). */
209
- excludedTasks?: {
210
- url: string;
211
- };
212
- /**
213
- * Site (§6). Default: liga o `browser_session_endpoint`. `false` desliga. `login`
214
- * substitui o login default (sessão de conta do authkit + `adonisAuth.guard`). `redirectTo` é
215
- * a página do site para onde o navegador vai depois de entrar (path). Default: `/`.
216
- */
217
- web?: false | {
218
- browserSession?: boolean;
219
- login?: PoppyBrowserLogin;
220
- redirectTo?: string;
221
- };
222
- /** Extensões suportadas (§3.3), publicadas como estão. */
223
- extensions?: Record<string, {
224
- version: string;
225
- } & Record<string, unknown>>;
226
- /** Registro/allowlist/blocklist de agentes. */
227
- clients?: PoppyClientsConfigInput;
228
- /**
229
- * SÓ DESENVOLVIMENTO/TESTE — NÃO conforme à spec. Aceita `client_id` (e o
230
- * `jwks_uri`/`redirect_uris` do agente) em `http://` e busca o documento sem a
231
- * proteção de SSRF (loopback e rede privada liberados), para testar com um
232
- * agente local (`poppy-agent --insecure-dev`). Lança no boot com
233
- * `NODE_ENV=production`. Default: false.
234
- */
235
- allowInsecureClientIds?: boolean;
236
- /** Vida do access token, em segundos. Default: 3600. */
237
- accessTokenTtl?: number;
238
- /** Vida do refresh token, em segundos. Default: 30 dias. */
239
- refreshTokenTtl?: number;
240
- /** Uma Session sem uso por este tempo termina (`invalid_session`). Default: 30 dias. */
241
- sessionTtl?: number;
242
- /** Vida do código de autorização (Direct), em segundos. Default: 60. */
243
- authorizationCodeTtl?: number;
244
- /** Validade do `device_code`, em segundos. Default: 600. */
245
- deviceCodeTtl?: number;
246
- /** Intervalo mínimo de polling do device flow, em segundos. Default: 5. */
247
- pollInterval?: number;
248
- /** Vida máxima das client assertions (`exp - iat`), em segundos. Default: 300. */
249
- assertionMaxLifetime?: number;
250
- /** Session JWT (§5.3). */
251
- sessionJwt?: {
252
- /** Janela de aceitação do `iat`, em segundos. Default: 60. */
253
- maxAge?: number;
254
- };
255
- /** DPoP (§4.5): prender os access tokens é escolha da empresa. */
256
- dpop?: {
257
- /** Recusa pedidos de token sem prova: nenhum access token sai Bearer (MCP à parte). Default: false. */
258
- required?: boolean;
259
- /** Exige `nonce` emitido pelo servidor nas provas (RFC 9449 §8). Default: false. */
260
- requireNonce?: boolean;
261
- /** Janela de aceitação do `iat` da prova, em segundos. Default: 60. */
262
- maxAge?: number;
263
- /** Algoritmos aceitos na prova. Default: `['ES256']`. */
264
- algorithms?: string[];
265
- };
266
- /**
267
- * Onde guardar os `jti` vistos. Default: `'database'` (tabela lib-owned
268
- * `auth_poppy_jtis` — vale entre instâncias). `'memory'` só serve com uma
269
- * instância. `{ redis: 'main' }` usa uma conexão do `@adonisjs/redis`.
270
- */
271
- replay?: 'database' | 'memory' | {
272
- redis: string;
273
- } | PoppyReplayStore;
274
- /**
275
- * Limites por `client_id` (in-memory, por processo) ou um limitador próprio.
276
- * `false` desliga. Default: 120 Sessions novas/min por agente; 10 tentativas
277
- * de Mediated Sign-In/hora por (agente, identificador).
278
- */
279
- rateLimit?: false | {
280
- sessionsPerMinute?: number;
281
- mediatedPerHour?: number;
282
- } | PoppyLimiter;
283
- /**
284
- * Mapeia o principal verificado para o `actor` do app (ex.: o Actor do Agora
285
- * com roles `scope:<id>`). Quando definido, `verifyPoppyRequest` e o slot
286
- * global devolvem `actor`.
287
- */
288
- toActor?: (principal: PoppyPrincipalBase) => unknown | Promise<unknown>;
289
- }
290
- /**
291
- * O que uma request verificada autoriza — sem o `actor`.
292
- *
293
- * @experimental Poppy (Personal Agent Protocol) Draft 0.3 — acompanha a spec em desenvolvimento e PODE MUDAR
294
- * de forma incompatível fora de majors enquanto ela for draft.
295
- */
296
- export interface PoppyPrincipalBase {
297
- /** User ID que o personal agent deu ao usuário NESTE app (opaco, estável). */
298
- userId: string;
299
- /** Agent ID opcional (§5.1): qual dos agentes do usuário é esta Session. */
300
- agentId: string | null;
301
- /** Conta deste app quando a request traz access token; `null` deslogada. */
302
- accountId: string | null;
303
- /** `client_id` do personal agent (URL do documento de metadata). */
304
- clientId: string;
305
- /** Scopes do access token. Vazio deslogado. */
306
- scopes: string[];
307
- /** A Session, única por `client_id` + `sid`. */
308
- sessionId: string;
309
- signedIn: boolean;
310
- /** `resource` para o qual o token foi emitido (RFC 8707), ou `null`. */
311
- resource: string | null;
312
- /** Esquema do access token; `null` deslogado. */
313
- tokenType: 'DPoP' | 'Bearer' | null;
314
- }
315
- export interface ResolvedPoppyMediatedConfig {
316
- fields: PoppyMediatedField[];
317
- scopes: string[];
318
- verify: PoppyMediatedConfigInput['verify'];
319
- verifyCode?: PoppyMediatedConfigInput['verifyCode'];
320
- codeTtl: number;
321
- maxCodeAttempts: number;
322
- }
323
- export interface ResolvedPoppyConfig {
324
- organization: {
325
- name: string;
326
- domain: string;
327
- aliases: string[];
328
- };
329
- prefix: string;
330
- /** Os scopes oferecidos, `id → descrição`. */
331
- scopes: Record<string, string>;
332
- scopeHints: Record<string, PoppyScopeHint>;
333
- signIn: {
334
- direct: {
335
- scopes: string[];
336
- } | null;
337
- device: {
338
- scopes: string[];
339
- } | null;
340
- mediated: ResolvedPoppyMediatedConfig | null;
341
- };
342
- agent: {
343
- protocols: PoppyAgentProtocolEntry[];
344
- } | null;
345
- apis: PoppyApiEntry[];
346
- feedback: {
347
- endpoint: string;
348
- } | null;
349
- excludedTasks: {
350
- url: string;
351
- } | null;
352
- web: {
353
- browserSession: boolean;
354
- login?: PoppyBrowserLogin;
355
- redirectTo: string;
356
- } | null;
357
- extensions: Record<string, {
358
- version: string;
359
- } & Record<string, unknown>>;
360
- clients: {
361
- requireRegistration: boolean;
362
- isRegistered: (clientId: string) => Promise<boolean>;
363
- isBlocked: (clientId: string) => Promise<boolean>;
364
- /** As listas do config quando são arrays (o console mostra); `null` = função do host. */
365
- registeredList: string[] | null;
366
- blockedList: string[] | null;
367
- };
368
- /** Dev/teste: `client_id` http e busca sem guarda de SSRF (ver o input). */
369
- allowInsecureClientIds: boolean;
370
- accessTokenTtl: number;
371
- refreshTokenTtl: number;
372
- sessionTtl: number;
373
- authorizationCodeTtl: number;
374
- deviceCodeTtl: number;
375
- pollInterval: number;
376
- assertionMaxLifetime: number;
377
- sessionJwt: {
378
- maxAge: number;
379
- };
380
- dpop: {
381
- required: boolean;
382
- requireNonce: boolean;
383
- maxAge: number;
384
- algorithms: string[];
385
- };
386
- replay: 'database' | 'memory' | {
387
- redis: string;
388
- } | PoppyReplayStore;
389
- rateLimit: false | {
390
- sessionsPerMinute: number;
391
- mediatedPerHour: number;
392
- } | PoppyLimiter;
393
- toActor?: PoppyConfigInput['toActor'];
394
- }
395
- export declare function normalizePoppyPrefix(prefix: string | undefined): string;
396
- export declare function resolvePoppyConfig(input: PoppyConfigInput | undefined, fallbackScopes: Record<string, string> | undefined): ResolvedPoppyConfig | undefined;
@@ -1,228 +0,0 @@
1
- /** Parâmetros que um campo do Mediated Sign-In não pode usar (extensão, §2). */
2
- const RESERVED_FIELDS = new Set([
3
- 'grant_type',
4
- 'scope',
5
- 'resource',
6
- 'client_id',
7
- 'client_secret',
8
- 'client_assertion',
9
- 'client_assertion_type',
10
- 'sign_in_id',
11
- 'one_time_code',
12
- ]);
13
- const SCOPE_ID = /^[\x21\x23-\x5B\x5D-\x7E]+$/; // RFC 6749 §3.3 scope-token
14
- const DOMAIN = /^(?=.{1,253}$)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)(\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/;
15
- function fail(message) {
16
- throw new Error(`authkit: personalAgents.poppy.${message}`);
17
- }
18
- function positive(value, fallback, what) {
19
- if (value === undefined)
20
- return fallback;
21
- if (!Number.isFinite(value) || value <= 0)
22
- fail(`${what} precisa ser > 0.`);
23
- return Math.floor(value);
24
- }
25
- function absoluteUrl(value, what) {
26
- let url;
27
- try {
28
- url = new URL(value);
29
- }
30
- catch {
31
- fail(`${what} precisa ser uma URL absoluta (recebeu "${value}").`);
32
- }
33
- if (url.protocol !== 'https:' && url.protocol !== 'http:') {
34
- fail(`${what} precisa ser http(s) (recebeu "${value}").`);
35
- }
36
- }
37
- export function normalizePoppyPrefix(prefix) {
38
- const trimmed = (prefix ?? '/poppy').trim().replace(/\/+$/, '');
39
- if (!trimmed)
40
- return '/poppy';
41
- return trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
42
- }
43
- function membership(value) {
44
- if (typeof value === 'function')
45
- return async (id) => (await value(id)) === true;
46
- const set = new Set(value ?? []);
47
- return async (id) => set.has(id);
48
- }
49
- function listOf(value) {
50
- if (typeof value === 'function')
51
- return null;
52
- return [...new Set(value ?? [])];
53
- }
54
- function typeScopes(input, all, what) {
55
- if (input === false)
56
- return null;
57
- const scopes = typeof input === 'object' && input.scopes ? [...new Set(input.scopes)] : all;
58
- for (const id of scopes) {
59
- if (!all.includes(id))
60
- fail(`signIn.${what}.scopes — scope desconhecido "${id}".`);
61
- }
62
- if (scopes.length === 0)
63
- fail(`signIn.${what}.scopes precisa de pelo menos um scope.`);
64
- return { scopes };
65
- }
66
- export function resolvePoppyConfig(input, fallbackScopes) {
67
- if (!input)
68
- return undefined;
69
- const org = input.organization;
70
- if (!org?.name?.trim())
71
- fail('organization.name é obrigatório.');
72
- // Domínios comparados só ignorando caixa, sem nenhuma outra normalização (§3.1).
73
- const domains = [org.domain, ...(org.aliases ?? [])].map((d) => (d ?? '').toLowerCase().replace(/\.$/, ''));
74
- for (const d of domains) {
75
- if (!DOMAIN.test(d))
76
- fail(`organization — domínio inválido "${d}" (só o host, sem esquema).`);
77
- }
78
- const scopes = { ...(input.scopes ?? fallbackScopes ?? {}) };
79
- for (const id of Object.keys(scopes)) {
80
- if (!SCOPE_ID.test(id))
81
- fail(`scopes — id inválido "${id}" (sem espaços/aspas).`);
82
- }
83
- const all = Object.keys(scopes);
84
- const scopeHints = { ...(input.scopeHints ?? {}) };
85
- for (const [id, hint] of Object.entries(scopeHints)) {
86
- if (!['none', 'reversible', 'irreversible'].includes(hint?.changes)) {
87
- fail(`scopeHints.${id}.changes precisa ser none, reversible ou irreversible.`);
88
- }
89
- if (typeof hint.personal_data !== 'boolean') {
90
- fail(`scopeHints.${id}.personal_data é obrigatório (boolean).`);
91
- }
92
- }
93
- const signInInput = input.signIn ?? {};
94
- let mediated = null;
95
- if (signInInput.mediated) {
96
- const m = signInInput.mediated;
97
- if (!Array.isArray(m.fields) || m.fields.length === 0) {
98
- fail('signIn.mediated.fields precisa de pelo menos um campo.');
99
- }
100
- const names = new Set();
101
- for (const f of m.fields) {
102
- if (!f?.name || !f.label || typeof f.secret !== 'boolean') {
103
- fail('signIn.mediated.fields — cada campo precisa de name, label e secret.');
104
- }
105
- if (RESERVED_FIELDS.has(f.name)) {
106
- fail(`signIn.mediated.fields — "${f.name}" é um parâmetro reservado.`);
107
- }
108
- if (names.has(f.name))
109
- fail(`signIn.mediated.fields — campo duplicado "${f.name}".`);
110
- names.add(f.name);
111
- }
112
- if (typeof m.verify !== 'function')
113
- fail('signIn.mediated.verify é obrigatório.');
114
- if (!Array.isArray(m.scopes))
115
- fail('signIn.mediated.scopes é obrigatório.');
116
- const mediatedScopes = typeScopes({ scopes: m.scopes }, all, 'mediated').scopes;
117
- mediated = {
118
- fields: m.fields.map((f) => ({ name: f.name, label: f.label, secret: f.secret })),
119
- scopes: mediatedScopes,
120
- verify: m.verify,
121
- verifyCode: m.verifyCode,
122
- codeTtl: positive(m.codeTtl, 600, 'signIn.mediated.codeTtl'),
123
- maxCodeAttempts: positive(m.maxCodeAttempts, 5, 'signIn.mediated.maxCodeAttempts'),
124
- };
125
- }
126
- if (all.length === 0 && (signInInput.direct !== false || signInInput.device !== false)) {
127
- fail('nenhum scope: declare `scopes` ou desligue signIn.direct e signIn.device.');
128
- }
129
- for (const entry of input.agent?.protocols ?? []) {
130
- if (!entry.type)
131
- fail('agent.protocols[].type é obrigatório.');
132
- absoluteUrl(entry.endpoint, 'agent.protocols[].endpoint');
133
- if (entry.resource)
134
- absoluteUrl(entry.resource, 'agent.protocols[].resource');
135
- if (entry.access && entry.access !== 'session' && entry.access !== 'signed_in') {
136
- fail('agent.protocols[].access precisa ser session ou signed_in.');
137
- }
138
- }
139
- for (const api of input.apis ?? []) {
140
- if (!api.type)
141
- fail('apis[].type é obrigatório.');
142
- absoluteUrl(api.url, 'apis[].url');
143
- if (api.resource)
144
- absoluteUrl(api.resource, 'apis[].resource');
145
- if (typeof api.description !== 'string')
146
- fail('apis[].description é obrigatório.');
147
- }
148
- if (input.feedback)
149
- absoluteUrl(input.feedback.endpoint, 'feedback.endpoint');
150
- if (input.excludedTasks)
151
- absoluteUrl(input.excludedTasks.url, 'excludedTasks.url');
152
- const redirectTo = input.web ? (input.web.redirectTo ?? '/') : '/';
153
- if (!redirectTo.startsWith('/') || redirectTo.startsWith('//')) {
154
- fail('web.redirectTo precisa ser um path do site.');
155
- }
156
- for (const name of Object.keys(input.extensions ?? {})) {
157
- if (typeof input.extensions[name]?.version !== 'string') {
158
- fail(`extensions.${name}.version é obrigatório (string).`);
159
- }
160
- }
161
- const dpopAlgs = input.dpop?.algorithms ?? ['ES256'];
162
- if (dpopAlgs.length === 0 || dpopAlgs.some((a) => a === 'none' || a.startsWith('HS'))) {
163
- fail('dpop.algorithms precisa de algoritmos assimétricos.');
164
- }
165
- const allowInsecureClientIds = input.allowInsecureClientIds === true;
166
- if (allowInsecureClientIds && process.env.NODE_ENV === 'production') {
167
- fail('allowInsecureClientIds é só para desenvolvimento/teste e não pode ser ligado com NODE_ENV=production.');
168
- }
169
- let rateLimit;
170
- if (input.rateLimit === false)
171
- rateLimit = false;
172
- else if (typeof input.rateLimit === 'function')
173
- rateLimit = input.rateLimit;
174
- else {
175
- rateLimit = {
176
- sessionsPerMinute: positive(input.rateLimit?.sessionsPerMinute, 120, 'rateLimit.sessionsPerMinute'),
177
- mediatedPerHour: positive(input.rateLimit?.mediatedPerHour, 10, 'rateLimit.mediatedPerHour'),
178
- };
179
- }
180
- return {
181
- organization: { name: org.name, domain: domains[0], aliases: domains.slice(1) },
182
- prefix: normalizePoppyPrefix(input.prefix),
183
- scopes,
184
- scopeHints,
185
- signIn: {
186
- direct: typeScopes(signInInput.direct, all, 'direct'),
187
- device: typeScopes(signInInput.device, all, 'device'),
188
- mediated,
189
- },
190
- agent: input.agent?.protocols?.length ? { protocols: [...input.agent.protocols] } : null,
191
- apis: [...(input.apis ?? [])],
192
- feedback: input.feedback ?? null,
193
- excludedTasks: input.excludedTasks ?? null,
194
- web: input.web === false
195
- ? null
196
- : {
197
- browserSession: input.web?.browserSession !== false,
198
- login: input.web?.login,
199
- redirectTo,
200
- },
201
- extensions: { ...(input.extensions ?? {}) },
202
- clients: {
203
- requireRegistration: input.clients?.requireRegistration === true,
204
- isRegistered: membership(input.clients?.registered),
205
- isBlocked: membership(input.clients?.blocked),
206
- registeredList: listOf(input.clients?.registered),
207
- blockedList: listOf(input.clients?.blocked),
208
- },
209
- allowInsecureClientIds,
210
- accessTokenTtl: positive(input.accessTokenTtl, 3600, 'accessTokenTtl'),
211
- refreshTokenTtl: positive(input.refreshTokenTtl, 30 * 24 * 3600, 'refreshTokenTtl'),
212
- sessionTtl: positive(input.sessionTtl, 30 * 24 * 3600, 'sessionTtl'),
213
- authorizationCodeTtl: positive(input.authorizationCodeTtl, 60, 'authorizationCodeTtl'),
214
- deviceCodeTtl: positive(input.deviceCodeTtl, 600, 'deviceCodeTtl'),
215
- pollInterval: positive(input.pollInterval, 5, 'pollInterval'),
216
- assertionMaxLifetime: positive(input.assertionMaxLifetime, 300, 'assertionMaxLifetime'),
217
- sessionJwt: { maxAge: positive(input.sessionJwt?.maxAge, 60, 'sessionJwt.maxAge') },
218
- dpop: {
219
- required: input.dpop?.required === true,
220
- requireNonce: input.dpop?.requireNonce === true,
221
- maxAge: positive(input.dpop?.maxAge, 60, 'dpop.maxAge'),
222
- algorithms: [...dpopAlgs],
223
- },
224
- replay: input.replay ?? 'database',
225
- rateLimit,
226
- toActor: input.toActor,
227
- };
228
- }
@@ -1,61 +0,0 @@
1
- import type { PoppyReplayStore } from './config.js';
2
- export type DpopFailure = {
3
- ok: false;
4
- error: 'invalid_dpop_proof' | 'use_dpop_nonce';
5
- description: string;
6
- };
7
- export type DpopResult = {
8
- ok: true;
9
- jkt: string;
10
- jti: string;
11
- } | DpopFailure;
12
- export interface DpopNonceSource {
13
- /** Nonce novo para o header `DPoP-Nonce`. */
14
- issue(): string;
15
- /** O nonce ainda vale? */
16
- valid(nonce: string): boolean;
17
- }
18
- /**
19
- * Nonces sem estado: `base64url(ts).base64url(hmac(ts))`, válidos por
20
- * `ttlSeconds` (default 300). Qualquer instância com o mesmo segredo valida.
21
- *
22
- * @experimental Poppy (Personal Agent Protocol) Draft 0.3 — acompanha a spec em desenvolvimento e PODE MUDAR
23
- * de forma incompatível fora de majors enquanto ela for draft.
24
- */
25
- export declare function hmacNonceSource(secret: Buffer | string, options?: {
26
- ttlSeconds?: number;
27
- now?: () => number;
28
- }): DpopNonceSource;
29
- /** `ath`: base64url(SHA-256(access token)). */
30
- export declare function accessTokenHash(token: string): string;
31
- /**
32
- * `htu` normalizado para comparação: esquema e host minúsculos, sem porta
33
- * default, sem query nem fragmento. `null` se não for URL http(s).
34
- */
35
- export declare function normalizeHtu(value: string): string | null;
36
- export interface VerifyDpopInput {
37
- /** Valor do(s) header(s) `DPoP`. Mais de um = inválido. */
38
- proof: string | string[] | undefined | null;
39
- method: string;
40
- /** URL da request (a query é ignorada). */
41
- url: string;
42
- /** Access token apresentado junto (resource server) — exige `ath`. */
43
- accessToken?: string;
44
- /** Thumbprint (RFC 7638) a que o token está preso. */
45
- expectedJkt?: string;
46
- algorithms: string[];
47
- /** Janela do `iat`, em segundos. */
48
- maxAge: number;
49
- /** Tolerância de relógio para `iat` no futuro, em segundos. Default: 5. */
50
- clockSkew?: number;
51
- replay: PoppyReplayStore;
52
- nonce?: {
53
- required: boolean;
54
- source: DpopNonceSource;
55
- };
56
- now?: () => number;
57
- }
58
- /**
59
- * @experimental Poppy (Personal Agent Protocol) Draft 0.3 — pode mudar de forma incompatível enquanto a spec for draft.
60
- */
61
- export declare function verifyDpopProof(input: VerifyDpopInput): Promise<DpopResult>;