@adonis-agora/authkit-server 1.0.0-rc.1 → 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 (163) hide show
  1. package/CHANGELOG.md +94 -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 +12 -12
  22. package/build/index.js +7 -9
  23. package/build/providers/authkit_server_provider.js +28 -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_switcher.d.ts +132 -0
  54. package/build/src/console_switcher.js +206 -0
  55. package/build/src/define_config.d.ts +34 -12
  56. package/build/src/define_config.js +15 -7
  57. package/build/src/dev_tools/contract.d.ts +72 -0
  58. package/build/src/dev_tools/contract.js +10 -0
  59. package/build/src/dev_tools/gate.d.ts +17 -0
  60. package/build/src/dev_tools/gate.js +69 -0
  61. package/build/src/dev_tools/register.d.ts +11 -0
  62. package/build/src/dev_tools/register.js +25 -0
  63. package/build/src/dev_tools/tools.d.ts +24 -0
  64. package/build/src/dev_tools/tools.js +384 -0
  65. package/build/src/doctor/agora_checks.d.ts +15 -0
  66. package/build/src/doctor/agora_checks.js +150 -0
  67. package/build/src/doctor/agora_doctor.d.ts +115 -0
  68. package/build/src/doctor/agora_doctor.js +692 -0
  69. package/build/src/doctor/doctor_input.d.ts +27 -0
  70. package/build/src/doctor/doctor_input.js +86 -0
  71. package/build/src/doctor/index.d.ts +5 -0
  72. package/build/src/doctor/index.js +4 -0
  73. package/build/src/host/access_token_verifier.js +7 -0
  74. package/build/src/host/account_api/account_api_controller.js +8 -6
  75. package/build/src/host/account_session_sign_in.d.ts +12 -0
  76. package/build/src/host/account_session_sign_in.js +19 -0
  77. package/build/src/host/admin_api/admin_users_service.d.ts +5 -2
  78. package/build/src/host/admin_api/api_clients_controller.d.ts +37 -0
  79. package/build/src/host/admin_api/api_clients_controller.js +31 -1
  80. package/build/src/host/admin_api/dto.d.ts +3 -1
  81. package/build/src/host/admin_api/dto.js +2 -0
  82. package/build/src/host/admin_authorize.d.ts +8 -0
  83. package/build/src/host/admin_authorize.js +25 -0
  84. package/build/src/host/admin_clients_service.d.ts +20 -0
  85. package/build/src/host/admin_clients_service.js +55 -0
  86. package/build/src/host/admin_console/admin_console_entry.d.ts +14 -0
  87. package/build/src/host/admin_console/admin_console_entry.js +80 -0
  88. package/build/src/host/admin_console/admin_shell_controller.js +8 -0
  89. package/build/src/host/admin_console/console_clients_controller.d.ts +41 -0
  90. package/build/src/host/admin_console/console_clients_controller.js +34 -1
  91. package/build/src/host/admin_console/console_personal_agents_controller.d.ts +1 -6
  92. package/build/src/host/admin_console/console_personal_agents_controller.js +50 -30
  93. package/build/src/host/admin_console/console_switcher_controller.d.ts +11 -0
  94. package/build/src/host/admin_console/console_switcher_controller.js +23 -0
  95. package/build/src/host/admin_sessions_service.js +8 -5
  96. package/build/src/host/admin_validators.d.ts +29 -0
  97. package/build/src/host/admin_validators.js +30 -0
  98. package/build/src/host/auth_host_config.d.ts +9 -2
  99. package/build/src/host/controllers/account_apps_controller.js +15 -11
  100. package/build/src/host/csrf.d.ts +3 -3
  101. package/build/src/host/csrf.js +3 -13
  102. package/build/src/host/default_mailer.d.ts +11 -6
  103. package/build/src/host/default_mailer.js +16 -12
  104. package/build/src/host/i18n.d.ts +41 -14
  105. package/build/src/host/i18n.js +55 -13
  106. package/build/src/host/rate_limit.d.ts +8 -0
  107. package/build/src/host/rate_limit.js +9 -0
  108. package/build/src/host/register_auth_host.d.ts +14 -0
  109. package/build/src/host/register_auth_host.js +167 -106
  110. package/build/src/host/renderers/edge_renderer.js +5 -0
  111. package/build/src/host/renderers/inertia_renderer.js +7 -1
  112. package/build/src/host/screen_props.d.ts +39 -8
  113. package/build/src/host/ui-dist/assets/index-Dq3dJVBv.js +67 -0
  114. package/build/src/host/ui-dist/assets/{index-DTSmD4RU.css → index-WNIyCEPA.css} +1 -1
  115. package/build/src/host/ui-dist/index.html +2 -2
  116. package/build/src/provider/build_provider.js +37 -6
  117. package/build/src/provider/oidc_service.d.ts +1 -1
  118. package/build/src/provider/oidc_service.js +1 -1
  119. package/build/src/schema/ensure.d.ts +14 -0
  120. package/build/src/schema/ensure.js +43 -150
  121. package/build/src/service_tenants/client_metadata.d.ts +39 -0
  122. package/build/src/service_tenants/client_metadata.js +80 -0
  123. package/build/src/service_tenants/config.d.ts +44 -0
  124. package/build/src/service_tenants/config.js +44 -0
  125. package/build/src/service_tenants/provider_hooks.d.ts +52 -0
  126. package/build/src/service_tenants/provider_hooks.js +124 -0
  127. package/build/src/service_tenants/tenant_verifier.d.ts +73 -0
  128. package/build/src/service_tenants/tenant_verifier.js +104 -0
  129. package/build/src/tenancy/tenant_labels.d.ts +30 -0
  130. package/build/src/tenancy/tenant_labels.js +64 -0
  131. package/build/types.d.ts +3 -1
  132. package/package.json +9 -6
  133. package/build/src/agents/poppy/client_registry.d.ts +0 -50
  134. package/build/src/agents/poppy/client_registry.js +0 -260
  135. package/build/src/agents/poppy/config.d.ts +0 -396
  136. package/build/src/agents/poppy/config.js +0 -228
  137. package/build/src/agents/poppy/dpop.d.ts +0 -61
  138. package/build/src/agents/poppy/dpop.js +0 -158
  139. package/build/src/agents/poppy/email_code_sign_in.d.ts +0 -27
  140. package/build/src/agents/poppy/email_code_sign_in.js +0 -140
  141. package/build/src/agents/poppy/errors.d.ts +0 -25
  142. package/build/src/agents/poppy/errors.js +0 -25
  143. package/build/src/agents/poppy/replay.d.ts +0 -25
  144. package/build/src/agents/poppy/replay.js +0 -106
  145. package/build/src/agents/poppy/resource.d.ts +0 -100
  146. package/build/src/agents/poppy/resource.js +0 -129
  147. package/build/src/agents/poppy/safe_fetch.d.ts +0 -48
  148. package/build/src/agents/poppy/safe_fetch.js +0 -262
  149. package/build/src/agents/poppy/service.d.ts +0 -293
  150. package/build/src/agents/poppy/service.js +0 -1458
  151. package/build/src/agents/poppy/session_jwt.d.ts +0 -30
  152. package/build/src/agents/poppy/session_jwt.js +0 -133
  153. package/build/src/agents/poppy/store.d.ts +0 -106
  154. package/build/src/agents/poppy/store.js +0 -261
  155. package/build/src/agents/poppy/web_session.d.ts +0 -39
  156. package/build/src/agents/poppy/web_session.js +0 -122
  157. package/build/src/host/controllers/poppy_consent_controller.d.ts +0 -19
  158. package/build/src/host/controllers/poppy_consent_controller.js +0 -199
  159. package/build/src/host/controllers/poppy_controller.d.ts +0 -33
  160. package/build/src/host/controllers/poppy_controller.js +0 -219
  161. package/build/src/host/poppy_session_key.d.ts +0 -9
  162. package/build/src/host/poppy_session_key.js +0 -9
  163. package/build/src/host/ui-dist/assets/index-D0EIeyKK.js +0 -67
@@ -1,260 +0,0 @@
1
- /**
2
- * Identidade do personal agent no Poppy (§4.1): o `client_id` é uma URL HTTPS
3
- * que devolve o documento de metadata do agente (OAuth Client ID Metadata
4
- * Document). Buscamos (à prova de SSRF), validamos, guardamos em cache e
5
- * importamos as chaves públicas do `jwks_uri`.
6
- */
7
- import { createLocalJWKSet } from 'jose';
8
- import { PoppyError } from './errors.js';
9
- import { insecureDevFetchJson, SafeFetchError, safeFetchJson, } from './safe_fetch.js';
10
- const MIN_TTL_MS = 60 * 1000;
11
- const MAX_TTL_MS = 24 * 3600 * 1000;
12
- const DEFAULT_TTL_MS = 3600 * 1000;
13
- const FAILURE_TTL_MS = 60 * 1000;
14
- const MAX_CACHED = 1000;
15
- /** Releitura do JWKS quando o `kid` não está no conjunto em cache — no máximo a cada 30 s. */
16
- const JWKS_REFRESH_COOLDOWN_MS = 30 * 1000;
17
- function hostOf(url) {
18
- try {
19
- return new URL(url).host.toLowerCase();
20
- }
21
- catch {
22
- return null;
23
- }
24
- }
25
- function isHttpsUrl(value, allowHttp = false) {
26
- if (typeof value !== 'string')
27
- return false;
28
- try {
29
- const url = new URL(value);
30
- const schemeOk = url.protocol === 'https:' || (allowHttp && url.protocol === 'http:');
31
- return schemeOk && !url.username && !url.password && !url.hash;
32
- }
33
- catch {
34
- return false;
35
- }
36
- }
37
- /** Loopback redirect URI (RFC 8252 §7.3): `http`, IP de loopback literal, sem fragmento. */
38
- function isLoopbackRedirect(value) {
39
- if (typeof value !== 'string')
40
- return false;
41
- try {
42
- const url = new URL(value);
43
- return (url.protocol === 'http:' &&
44
- (url.hostname === '127.0.0.1' || url.hostname === '[::1]') &&
45
- !url.username &&
46
- !url.password &&
47
- !url.hash);
48
- }
49
- catch {
50
- return false;
51
- }
52
- }
53
- /**
54
- * `redirect_uri` igual a uma entrada de `redirect_uris`; numa loopback redirect URI a porta não
55
- * conta (§4.2, RFC 8252 §7.3).
56
- */
57
- export function redirectUriAllowed(registered, redirectUri) {
58
- if (registered.includes(redirectUri))
59
- return true;
60
- if (!isLoopbackRedirect(redirectUri))
61
- return false;
62
- const withoutPort = (value) => {
63
- const url = new URL(value);
64
- url.port = '';
65
- return url.toString();
66
- };
67
- return registered.some((r) => isLoopbackRedirect(r) && withoutPort(r) === withoutPort(redirectUri));
68
- }
69
- function ttlFrom(maxAge) {
70
- if (maxAge === null)
71
- return DEFAULT_TTL_MS;
72
- return Math.min(MAX_TTL_MS, Math.max(MIN_TTL_MS, maxAge * 1000));
73
- }
74
- function setBounded(map, key, value) {
75
- map.delete(key);
76
- map.set(key, value);
77
- if (map.size > MAX_CACHED)
78
- map.delete(map.keys().next().value);
79
- }
80
- /** Valida um documento de metadata e devolve os campos usados. Lança `invalid_client`. */
81
- export function validateClientMetadata(fetchedFrom, doc, options = {}) {
82
- const allowHttp = options.allowHttp === true;
83
- const bad = (why) => new PoppyError('invalid_client', `client metadata: ${why}`);
84
- if (!doc || typeof doc !== 'object' || Array.isArray(doc))
85
- throw bad('not a JSON object');
86
- const d = doc;
87
- if (d.client_id !== fetchedFrom)
88
- throw bad('client_id must equal the URL it was fetched from');
89
- const host = hostOf(fetchedFrom);
90
- if (!isHttpsUrl(d.jwks_uri, allowHttp) || hostOf(d.jwks_uri) !== host) {
91
- throw bad('jwks_uri must be an HTTPS URL on the same domain as client_id');
92
- }
93
- const redirects = d.redirect_uris ?? [];
94
- if (!Array.isArray(redirects))
95
- throw bad('redirect_uris must be an array');
96
- for (const uri of redirects) {
97
- if (isLoopbackRedirect(uri))
98
- continue;
99
- if (!isHttpsUrl(uri, allowHttp) || hostOf(uri) !== host) {
100
- throw bad('every redirect_uri must be an HTTPS URL on the same domain as client_id, or a loopback URI');
101
- }
102
- }
103
- if (d.token_endpoint_auth_method !== 'private_key_jwt') {
104
- throw bad('token_endpoint_auth_method must be private_key_jwt');
105
- }
106
- if (d.jwks !== undefined)
107
- throw bad('keys are published at jwks_uri only');
108
- const name = typeof d.client_name === 'string' && d.client_name.trim() ? d.client_name : host;
109
- const extensions = d.extensions && typeof d.extensions === 'object' && !Array.isArray(d.extensions)
110
- ? d.extensions
111
- : {};
112
- return {
113
- clientId: fetchedFrom,
114
- name: name.slice(0, 200),
115
- logoUri: isHttpsUrl(d.logo_uri, allowHttp) ? d.logo_uri : null,
116
- jwksUri: d.jwks_uri,
117
- redirectUris: redirects,
118
- extensions,
119
- };
120
- }
121
- /** Só chaves públicas (§4.1) e pelo menos uma. */
122
- function validateJwks(doc) {
123
- const keys = doc?.keys;
124
- if (!Array.isArray(keys) || keys.length === 0) {
125
- throw new PoppyError('invalid_client', 'jwks_uri must serve a non-empty JWK set');
126
- }
127
- for (const key of keys) {
128
- if (!key || typeof key !== 'object')
129
- throw new PoppyError('invalid_client', 'invalid JWK');
130
- const k = key;
131
- if ('d' in k || 'p' in k || 'q' in k || k.kty === 'oct') {
132
- throw new PoppyError('invalid_client', 'jwks_uri must contain only public keys');
133
- }
134
- }
135
- return { keys: keys };
136
- }
137
- /**
138
- * Resolve e valida agentes pelo `client_id`, com cache (respeita `max-age`,
139
- * entre 1 min e 24 h; falhas ficam 1 min em cache para que o mesmo `client_id`
140
- * não vire uma busca de saída por request).
141
- */
142
- export class PoppyClientRegistry {
143
- #cfg;
144
- #fetch;
145
- #now;
146
- #clients = new Map();
147
- #jwks = new Map();
148
- constructor(cfg, options = {}) {
149
- this.#cfg = cfg;
150
- this.#fetch =
151
- options.fetchJson ??
152
- (cfg.allowInsecureClientIds
153
- ? (url) => insecureDevFetchJson(url)
154
- : (url) => safeFetchJson(url));
155
- this.#now = options.now ?? Date.now;
156
- }
157
- /**
158
- * O agente, ou `invalid_client`: `client_id` não é HTTPS, bloqueado, não
159
- * registrado (quando o app exige registro), metadata inválida/inalcançável.
160
- */
161
- async resolve(clientId) {
162
- if (!isHttpsUrl(clientId, this.#cfg.allowInsecureClientIds)) {
163
- throw new PoppyError('invalid_client', 'client_id must be an HTTPS URL');
164
- }
165
- if (await this.#cfg.clients.isBlocked(clientId)) {
166
- throw new PoppyError('invalid_client', 'client_id is blocked');
167
- }
168
- if (this.#cfg.clients.requireRegistration &&
169
- !(await this.#cfg.clients.isRegistered(clientId))) {
170
- throw new PoppyError('invalid_client', 'client_id is not registered');
171
- }
172
- const meta = await this.#metadata(clientId);
173
- return { ...meta, keys: this.#keySet(meta.jwksUri) };
174
- }
175
- /** Nome de exibição sem falhar (console de conta): metadata em cache/buscada, ou o host. */
176
- async displayName(clientId) {
177
- try {
178
- return (await this.#metadata(clientId)).name;
179
- }
180
- catch {
181
- return hostOf(clientId) ?? clientId;
182
- }
183
- }
184
- #metadata(clientId) {
185
- const now = this.#now();
186
- const cached = this.#clients.get(clientId);
187
- if (cached && cached.expiresAt > now)
188
- return cached.value;
189
- const entry = {
190
- value: Promise.resolve(null),
191
- expiresAt: now + FAILURE_TTL_MS,
192
- };
193
- entry.value = (async () => {
194
- let fetched;
195
- try {
196
- fetched = await this.#fetch(clientId);
197
- }
198
- catch (error) {
199
- const why = error instanceof SafeFetchError ? error.message : 'unreachable';
200
- throw new PoppyError('invalid_client', `client metadata: ${why}`);
201
- }
202
- const meta = validateClientMetadata(clientId, fetched.body, {
203
- allowHttp: this.#cfg.allowInsecureClientIds,
204
- });
205
- entry.expiresAt = this.#now() + ttlFrom(fetched.maxAge);
206
- return meta;
207
- })();
208
- entry.value.catch(() => {
209
- entry.expiresAt = this.#now() + FAILURE_TTL_MS;
210
- });
211
- setBounded(this.#clients, clientId, entry);
212
- return entry.value;
213
- }
214
- #fetchJwks(jwksUri, force) {
215
- const now = this.#now();
216
- const cached = this.#jwks.get(jwksUri);
217
- if (cached && cached.expiresAt > now && !force)
218
- return cached.value;
219
- if (cached && force && now - cached.fetchedAt < JWKS_REFRESH_COOLDOWN_MS)
220
- return cached.value;
221
- const entry = {
222
- value: Promise.resolve({ keys: [] }),
223
- expiresAt: now + FAILURE_TTL_MS,
224
- fetchedAt: now,
225
- };
226
- entry.value = (async () => {
227
- let fetched;
228
- try {
229
- fetched = await this.#fetch(jwksUri);
230
- }
231
- catch (error) {
232
- const why = error instanceof SafeFetchError ? error.message : 'unreachable';
233
- throw new PoppyError('invalid_client', `jwks_uri: ${why}`);
234
- }
235
- const jwks = validateJwks(fetched.body);
236
- entry.expiresAt = this.#now() + ttlFrom(fetched.maxAge);
237
- return jwks;
238
- })();
239
- entry.value.catch(() => {
240
- entry.expiresAt = this.#now() + FAILURE_TTL_MS;
241
- });
242
- setBounded(this.#jwks, jwksUri, entry);
243
- return entry.value;
244
- }
245
- /** `JWTVerifyGetKey` que relê o JWKS uma vez quando o `kid` é desconhecido (rotação). */
246
- #keySet(jwksUri) {
247
- return async (header, token) => {
248
- const jwks = await this.#fetchJwks(jwksUri, false);
249
- try {
250
- return await createLocalJWKSet(jwks)(header, token);
251
- }
252
- catch (error) {
253
- const refreshed = await this.#fetchJwks(jwksUri, true);
254
- if (refreshed === jwks)
255
- throw error;
256
- return createLocalJWKSet(refreshed)(header, token);
257
- }
258
- };
259
- }
260
- }
@@ -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;