@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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,116 @@
1
1
  # @adonis-agora/authkit-server
2
2
 
3
+ ## 1.0.0-rc.10
4
+
5
+ ### Minor Changes
6
+
7
+ - [#471](https://github.com/DavideCarvalho/adonis-agora-authkit/pull/471) [`66ccc36`](https://github.com/DavideCarvalho/adonis-agora-authkit/commit/66ccc365dbbd7d9d5bf22dba7aad196aff9f5d4b) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Account console extensions from another app. An app that signs in through an authkit IdP can now add pages to the IdP's account console:
8
+
9
+ - authkit-client binds the same `authkit.accountExtensions` registry, so packages register their pages the same way in both setups. With `accountConsole: { remote: true }` the app serves them at `/.well-known/agora-account-extensions` (mounted by `registerOidcClient`, or `registerAccountExtensionsEndpoint(router)`), only to requests the IdP signed for its client id with the scope `authkit:account-extensions`. `accountConsole.subject` maps the IdP account to the app's user.
10
+ - authkit-server: `accountConsole.remote: [{ clientId, url }]` lists those apps; their pages show in the console next to the in-process ones, with the same sign-in, sudo, confirmation, rate limit and audit rules.
11
+ - The contract types and validation now live in authkit-core (still exported from `@adonis-agora/authkit-server/account-extension`).
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies [[`66ccc36`](https://github.com/DavideCarvalho/adonis-agora-authkit/commit/66ccc365dbbd7d9d5bf22dba7aad196aff9f5d4b)]:
16
+ - @adonis-agora/authkit-core@1.0.0-rc.1
17
+
18
+ ## 1.0.0-rc.9
19
+
20
+ ### Minor Changes
21
+
22
+ - [#470](https://github.com/DavideCarvalho/adonis-agora-authkit/pull/470) [`d8ee87a`](https://github.com/DavideCarvalho/adonis-agora-authkit/commit/d8ee87abd875e8c141cde051023f1035d8274513) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Account console extensions: other packages (and your app) can add pages to `/account/*`.
23
+
24
+ - New subpath `@adonis-agora/authkit-server/account-extension` with the stable contract (`AccountExtension`, blocks, actions) and the `authkit.accountExtensions` registry binding. Contributors register from their provider's `boot()`; AuthKit renders the pages (Edge or your Inertia page) behind the account sign-in, with CSRF, sudo for sensitive actions, a per-account rate limit, and audit of external redirects.
25
+ - Blocks: `notice`, `keyValue`, `list`, `table`, `meter`, `actions`, `link`, `toolPreview` (approve/reject previews) and `component` (a provider's card fields, scripts loaded with the response's CSP nonce, with a link fallback).
26
+ - `accountConsole` in `config/authkit.ts`: one `subject(account, { activeOrg, tenant })` mapping shared by every extension, host overrides to disable, rename or reorder pages, and `loadTimeoutMs`.
27
+ - The account screens share one navigation (`accountNav`) that lists the mounted screens and the extension pages; `AuthHostRouteMap.account.extensions` lists the pages.
28
+ - JSON API: `GET {prefix}/api/extensions`, `GET {prefix}/api/x/:pageId`, `POST {prefix}/api/x/:pageId/actions/:action`.
29
+ - authkit-react: `<AccountExtensionPage>`, `useAccountExtensions()`, the page/action query options and `client.account.extensions.*`.
30
+
31
+ ## 1.0.0-rc.8
32
+
33
+ ### Minor Changes
34
+
35
+ - [#467](https://github.com/DavideCarvalho/adonis-agora-authkit/pull/467) [`65dea93`](https://github.com/DavideCarvalho/adonis-agora-authkit/commit/65dea936568e24a450f64834cae8d1e5885dde57) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Admin console: switch between the `@adonis-agora` consoles. The admin console registers itself in the shared `agora.consoles` registry (title "Identity"); the sidebar brand becomes a menu of the other consoles the viewer may open, the Overview page shows them as a "Consoles" grid, and Ctrl/Cmd-K searches users and organizations plus every other console the viewer may open. New routes `GET {prefix}/api/consoles` and `GET {prefix}/api/consoles/search?q=`. `registerAgoraConsole` is exported for consoles you mount yourself, and `isAgoraProbe(ctx)` tells an auditing `authorize` that another console is only checking access (the structural `ctx.agoraProbe` marker). Nothing to configure.
36
+
37
+ ## 1.0.0-rc.7
38
+
39
+ ### Minor Changes
40
+
41
+ - [#465](https://github.com/DavideCarvalho/adonis-agora-authkit/pull/465) [`6bd9575`](https://github.com/DavideCarvalho/adonis-agora-authkit/commit/6bd95759752c0fac14a061875abebb38c0e238b7) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - The active organization is published as the request's tenant in `@adonis-agora/context`: `Context.tenant()` returns `{ id, kind: 'organization', source: 'authkit', slug, role }` (and `tenantId` as before). New client option `tenancy`: `eager` resolves the session at request start (default on when `config/context.ts` enables tenancy and the resolver is `jwt`), `personal` makes the user the tenant when there is no active organization (off by default), and `resolve` maps the session to your own (optionally nested) tenants. A tenant the app sets itself with `Context.setTenant` is never overwritten. authkit-server ships `authkitTenantLabels()`, a label resolver for `config/context.ts` → `tenancy.label` that shows organization names in the consoles, and account stores gain the optional `findOrgsByIds` (implemented by the Lucid store).
42
+
43
+ ## 1.0.0-rc.6
44
+
45
+ ### Minor Changes
46
+
47
+ - [#464](https://github.com/DavideCarvalho/adonis-agora-authkit/pull/464) [`3250c35`](https://github.com/DavideCarvalho/adonis-agora-authkit/commit/3250c35eee328e8e3780df32001323be45fe1bae) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Developer tools over MCP. When the app runs `@adonis-agora/agent`'s dev tools endpoint (`/mcp/agora`), the admin console's data is served there with no config: `authkit_get_overview`, `authkit_list_users`, `authkit_get_user`, `authkit_list_user_sessions`, `authkit_list_user_grants`, `authkit_list_orgs`, `authkit_get_org`, `authkit_list_clients`, `authkit_list_audit`, and the destructive actions `authkit_revoke_user_sessions` and `authkit_disable_user` (hidden until the endpoint allows actions; audited with `mcp:<caller>` as actor). Access follows `admin.authorize`; when it allows the MCP request, `admin.roles` is not checked. Without `admin.authorize`, production refuses and other environments need an AuthKit sign-in holding one of `admin.roles`.
48
+
49
+ `@adonis-agora/authkit-poppy` offers the `agora:dev` scope when the app serves those dev tools (`devTools` option to force it on or off). The consent screen never ticks it for the person, and Mediated Sign-In can't grant it. The personal-agent consent screen supports opt-in scopes (`optIn`, `descriptionKey` on each scope of `agents/consent`).
50
+
51
+ ## 1.0.0-rc.5
52
+
53
+ ### Minor Changes
54
+
55
+ - [#463](https://github.com/DavideCarvalho/adonis-agora-authkit/pull/463) [`e9f02d8`](https://github.com/DavideCarvalho/adonis-agora-authkit/commit/e9f02d8e6858f9ec8e5cdb2b77409fd0c31cb13d) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - `node ace agora:doctor`: one health check for every installed `@adonis-agora` lib. authkit registers its checks (every `authkit:doctor` finding under a stable id such as `authkit.issuer`, plus `authkit.signing-key-age`, `authkit.console-gate`, `authkit.peers` and `authkit.agora-siblings`). Flags: `--json`, `--strict`, `--deep`, `--lib`, `--only`, `--verbose`, `--no-boot`; exit `0` / `1` (error, or warning with `--strict`) / `2` (the doctor could not run). New `@adonis-agora/authkit-server/doctor` entry exports the checks for `--no-boot`. `authkit:doctor` is unchanged.
56
+
57
+ ## 1.0.0-rc.4
58
+
59
+ ### Patch Changes
60
+
61
+ - fix(deps): update dependency jose to v6.2.16 ([#454](https://github.com/DavideCarvalho/adonis-agora-authkit/issues/454))
62
+
63
+ ## 1.0.0-rc.3
64
+
65
+ ### Minor Changes
66
+
67
+ - [#460](https://github.com/DavideCarvalho/adonis-agora-authkit/pull/460) [`1858ee8`](https://github.com/DavideCarvalho/adonis-agora-authkit/commit/1858ee837bb4edcf0578f86d224693ac9a86a495) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Service tenants: a client can act as one partition of another service, such as a durable control plane.
68
+
69
+ - `serviceTenants: { resources: { 'urn:agora:durable': { expiresIn: 600 } } }` in `config/authkit.ts` declares the resources (RFC 8707) that issue service-tenant tokens: JWT access tokens (RFC 9068) with `aud` = the resource and the client's `partition` and `capabilities` as claims.
70
+ - A service-tenant client is confidential, uses only `client_credentials` and has no redirect URIs. Create and edit it from the console (**Service tenant** kind), the Admin API and the SDKs (`serviceTenant: { partition, capabilities? }`), or `node ace authkit:clients:create --partition=acme --capability=reports`.
71
+ - Any client can now be disabled and enabled again (console, `POST /clients/:id/disable|enable`, SDK `clients.disable/enable`): the token endpoint answers `invalid_client` from the next request on.
72
+ - `authkitTenantVerifier()` (authkit-server) verifies those tokens against the local issuer and keys and returns `{ tenant, capabilities }`; `authkitTenantToken({ issuer, clientId, clientSecret, resource })` (authkit-client) fetches, caches and refreshes them on the tenant side. Both fit durable's `verifyTenant` and `tenant.token` as a function or a class, without depending on durable.
73
+ - New audit events: `client.disabled`, `client.enabled`, `service_tenant.token_issued`.
74
+ - Access-token verification (`oidcBearerGuard`, in-process and remote) now refuses a token whose `sub` is a client (a `client_credentials` token), so a service-tenant token never authenticates a user.
75
+
76
+ ## 1.0.0-rc.2
77
+
78
+ ### Major Changes
79
+
80
+ - [#456](https://github.com/DavideCarvalho/adonis-agora-authkit/pull/456) [`9c12ef9`](https://github.com/DavideCarvalho/adonis-agora-authkit/commit/9c12ef9c1c49db9d54a7da6d241264499ad76e18) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Poppy (the Personal Agent Protocol) moves out of `@adonis-agora/authkit-server` into the new `@adonis-agora/authkit-poppy` package, and authkit-server gains a stable extension surface for personal-agent protocols. See [Install `@adonis-agora/authkit-poppy` for Poppy](https://github.com/DavideCarvalho/adonis-agora-authkit/blob/main/apps/docs/content/docs/migrating-to-1-0.mdx#install-adonis-agoraauthkit-poppy-for-poppy).
81
+
82
+ Breaking changes (`@adonis-agora/authkit-server`):
83
+
84
+ - `personalAgents.poppy` is removed: install `@adonis-agora/authkit-poppy` and use `personalAgents: { extensions: [poppy({ ... })] }` with the same options. A config that still has `personalAgents.poppy` fails at boot. Routes, tables, tokens and sessions are unchanged, so no data migration is needed.
85
+ - Every Poppy export (`PoppyConfigInput` and the other `Poppy*` types, `poppyEmailCodeSignIn`, `verifyPoppyRequest`, `poppyAuth`, `poppyOf`, `poppyWebSession`, `poppyWebSessionOf`, `installPoppySlots`, `POPPY_AUTHENTICATE_SLOT`, `POPPY_WEB_SESSION_KEY`, `PoppyError`, `verifyDpopProof`, `accessTokenHash`, `hmacNonceSource`, `databaseReplayStore`, `memoryReplayStore`, `redisReplayStore`, `safeFetchJson`, `isBlockedAddress`, `SafeFetchError`, `maskEmail`) now comes from `@adonis-agora/authkit-poppy`.
86
+ - `mail.onPoppySignInCode` is removed: pass `sendCode` to `poppyEmailCodeSignIn()`.
87
+ - `authkitCsrfExceptions` no longer takes `poppyPrefix`; extensions' endpoints are exempted from the config (`personalAgentExtensions: false` turns that off).
88
+ - `ensureAuthkitSchema()` no longer creates the `auth_poppy_*` tables (the extension's tables are ensured at boot; `ensurePoppySchema()` for migrations).
89
+ - Admin console API: an extension's `protocols` entry is `{ label, requireRegistration, hostResolver, allowHttpIdentifiers, identifierLabel, identifierPlaceholder }`.
90
+
91
+ New, stable for 1.x: `personalAgents.extensions` and the types `PersonalAgentExtension`, `PersonalAgentExtensionDefinition`, `PersonalAgentExtensionContext`, `PersonalAgentRouteKit`, `PersonalAgentHost`, `PersonalAgentGrantLedger`, `PersonalAgentGrant`, `PersonalAgentRefreshToken`, `PersonalAgentAccessTokens`, `PersonalAgentProtocolRuntime`, `PersonalAgentGrantSummary`, `PersonalAgentConsoleSummary`, `ResolvedPersonalAgentExtension`, `AuthkitTable`; the functions `personalAgentExtensionRuntime`, `ensureAuthkitTables`, `sendAuthkitCodeEmail`, `signInAccountSession` and `signOutAccountSession`; and the `tables` option of `ensureAuthkitSchema`.
92
+
93
+ Breaking changes (`@adonis-agora/authkit-react`):
94
+
95
+ - `PersonalAgentProtocol` is `'pact'` or any extension id, and `PersonalAgentsOverview.protocols` has one entry per extension (`PersonalAgentExtensionOverview`) instead of a fixed `poppy` key. `AccountAgentGrant.protocol` uses `PersonalAgentProtocol`.
96
+
97
+ ## 1.0.0-rc.1
98
+
99
+ ### Major Changes
100
+
101
+ - [#451](https://github.com/DavideCarvalho/adonis-agora-authkit/pull/451) [`1162aee`](https://github.com/DavideCarvalho/adonis-agora-authkit/commit/1162aeeee00d4706b8515d89e37f93d172cec44a) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - The account and admin consoles now sign in through the IdP login by default.
102
+
103
+ - `accountSession.acceptIdpSession` defaults to `true`. Without a console session, `/account/*` and the admin console send the visitor to the IdP login (`/auth/console/sso`) and come back with the console open; a user already signed in to an app opens them directly.
104
+ - `/account/login` redirects to the IdP login. The password form stays at `/account/login?method=password` while password login is on.
105
+ - The console's idle timeout now also ends the IdP session the console was opened from, like "Sign out" already did.
106
+ - `accountSession: { acceptIdpSession: false }` with `authMethods: { password: false }` and no `accountLoginUrl` (passed to `registerAuthHost` or set in `routes`) now fails at boot when the routes are registered, since the consoles would have no way in. `authkit:doctor` warns about it, and when SSO is off.
107
+ - The client id `authkit-console` is reserved for the consoles' internal client (`response_type=none`, never listed in the console) and cannot be registered.
108
+ - New `admin.authorize`, the access check every `@adonis-agora` console takes: an inline function, an authorizer class (built through the container, so `@inject()` works) or a lazy import (`() => import('#policies/console_access')`). It runs after `admin.roles` (both must pass) on the shell and on every `{prefix}/api/*` route. An account without one of the roles still goes to `accountHome`.
109
+ - **Breaking:** without `admin.authorize`, the admin console is open outside production and answers 403 to every request in production. The app logs `[@adonis-agora/authkit-server] console has no \`authorize\` configured: …` once at boot, and `authkit:doctor` warns. To keep letting every admin in, set `admin: { enabled: true, roles: ['ADMIN'], authorize: () => true }`.
110
+ - An `authorize` denial answers 403 (an access-denied page, or `{ error: 'forbidden' }` for JSON); `reason: 'unauthenticated'` answers 401 (`{ error: 'unauthenticated' }`); `{ redirectTo }` redirects page navigations and answers 401 `{ error: 'unauthenticated', redirectTo }` to JSON calls; a thrown error is a 403.
111
+ - New `authorizeByToken({ token })`: a `ConsoleAuthorize` that allows `Authorization: Bearer <token>` (timing-safe compare) and answers 401 otherwise. An empty token throws when the config loads.
112
+ - The types `ConsoleAuthorize`, `ConsoleAuthorizer`, `ConsoleAuthorizerClass` and `ConsoleAuthorizeResult` are exported, next to `authorizeByToken`.
113
+
3
114
  ## 1.0.0-rc.0
4
115
 
5
116
  ### Major Changes
package/README.md CHANGED
@@ -15,11 +15,10 @@ wrapper around [`oidc-provider`](https://github.com/panva/node-oidc-provider).
15
15
  auth via `acr_values`, Dynamic Client Registration (RFC 7591/7592).
16
16
  - **Personal agents** — AI assistants acting for your users: agent identity by signed JWT
17
17
  (JWKS, no shared secrets) and user-approved, revocable delegation with scopes you define,
18
- over a pluggable protocol adapter ([PACT](https://openpactprotocol.org) built in).
19
- Plus the [Personal Agent Protocol ("Poppy")](https://personalagentprotocol.org/docs/spec):
20
- discovery, signed `Poppy-Session` agent sessions, Direct/Device/Mediated sign-in, MCP and browser sessions.
21
- ⚠️ **Experimental** — tracks the spec's Draft 0.3 and will change (possibly breaking, outside
22
- semver majors) while the spec is a draft.
18
+ over a pluggable protocol adapter ([PACT](https://openpactprotocol.org) built in), plus
19
+ `personalAgents.extensions` for protocols shipped as their own packages — the
20
+ [Personal Agent Protocol ("Poppy")](https://personalagentprotocol.org/docs/spec) is
21
+ [`@adonis-agora/authkit-poppy`](https://www.npmjs.com/package/@adonis-agora/authkit-poppy).
23
22
  - **Native apps** — RFC 8252 native clients (`authkit:clients:create --native`: private-use
24
23
  scheme / claimed https / loopback redirects, always public + PKCE) and the `@adonisjs/auth`
25
24
  guards `oidcRpGuard` (session) and `oidcBearerGuard` (`Authorization: Bearer` access tokens,
@@ -0,0 +1,22 @@
1
+ import { BaseCommand } from '@adonisjs/core/ace';
2
+ import type { CommandOptions } from '@adonisjs/core/types/ace';
3
+ /**
4
+ * `node ace agora:doctor`: one health check for every installed `@adonis-agora` lib. Exit code `0`
5
+ * when nothing is an error (`--strict`: nor a warning), `1` otherwise, `2` when the doctor itself
6
+ * cannot run.
7
+ */
8
+ export default class AgoraDoctor extends BaseCommand {
9
+ static commandName: string;
10
+ static description: string;
11
+ static options: CommandOptions;
12
+ /** A `<lib>:doctor` alias sets this to pre-filter its lib. */
13
+ protected presetLib: string | undefined;
14
+ json: boolean;
15
+ lib?: string;
16
+ deep: boolean;
17
+ only?: string;
18
+ strict: boolean;
19
+ verbose: boolean;
20
+ boot: boolean;
21
+ run(): Promise<void>;
22
+ }
@@ -0,0 +1,109 @@
1
+ // agora-doctor command v1 — keep identical across libs
2
+ // Revision 1.1 (see the canonical README for the changelog).
3
+ // Every @adonis-agora lib ships this command (commands/agora_doctor.ts); whichever copy Ace loads
4
+ // last wins, and they all behave the same. Each repo's CI checks its sha256 against the pinned
5
+ // canonical one. Never edit a lib's copy: change the canonical copy, then re-copy it into every lib.
6
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
7
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
8
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
9
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
10
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
11
+ };
12
+ import { BaseCommand, flags } from '@adonisjs/core/ace';
13
+ import { AgoraDoctorUsageError, discoverDoctorChecks, readDoctorChecks, renderAgoraDoctor, runAgoraDoctor, } from '../src/doctor/agora_doctor.js';
14
+ /**
15
+ * `node ace agora:doctor`: one health check for every installed `@adonis-agora` lib. Exit code `0`
16
+ * when nothing is an error (`--strict`: nor a warning), `1` otherwise, `2` when the doctor itself
17
+ * cannot run.
18
+ */
19
+ export default class AgoraDoctor extends BaseCommand {
20
+ static commandName = 'agora:doctor';
21
+ static description = 'Check the configuration of every installed @adonis-agora lib';
22
+ // The app is booted by `run()` itself, so `--no-boot` can skip it.
23
+ static options = { startApp: false };
24
+ /** A `<lib>:doctor` alias sets this to pre-filter its lib. */
25
+ presetLib = undefined;
26
+ async run() {
27
+ const err = (line) => process.stderr.write(`${line}\n`);
28
+ try {
29
+ let checks;
30
+ if (this.boot === false) {
31
+ checks = await discoverDoctorChecks(this.app);
32
+ }
33
+ else {
34
+ if (!this.app.isReady) {
35
+ try {
36
+ await this.app.boot();
37
+ await this.app.start(() => { });
38
+ }
39
+ catch (error) {
40
+ const reason = error instanceof Error ? error.message : String(error);
41
+ throw new AgoraDoctorUsageError(`the app did not boot (${reason}). Fix the boot error, or run with --no-boot.`);
42
+ }
43
+ }
44
+ checks = await readDoctorChecks(this.app.container);
45
+ }
46
+ const libs = (this.lib ?? this.presetLib)
47
+ ?.split(',')
48
+ .map((name) => name.trim())
49
+ .filter(Boolean);
50
+ const { report, exitCode } = await runAgoraDoctor(this.app, checks, {
51
+ ...(libs ? { libs } : {}),
52
+ ...(this.only ? { only: this.only } : {}),
53
+ deep: this.deep === true,
54
+ noBoot: this.boot === false,
55
+ strict: this.strict === true,
56
+ });
57
+ if (this.json) {
58
+ process.stdout.write(`${JSON.stringify(report)}\n`);
59
+ }
60
+ else {
61
+ const fancy = process.stdout.isTTY === true && !process.env.NO_COLOR;
62
+ const c = this.colors;
63
+ process.stdout.write(renderAgoraDoctor(report, {
64
+ verbose: this.verbose === true,
65
+ fancy,
66
+ paint: {
67
+ green: (t) => c.green(t),
68
+ yellow: (t) => c.yellow(t),
69
+ red: (t) => c.red(t),
70
+ dim: (t) => c.dim(t),
71
+ bold: (t) => c.bold(t),
72
+ },
73
+ }));
74
+ }
75
+ this.exitCode = exitCode;
76
+ }
77
+ catch (error) {
78
+ if (!(error instanceof AgoraDoctorUsageError))
79
+ throw error;
80
+ err(`agora:doctor: ${error.message}`);
81
+ this.exitCode = 2;
82
+ }
83
+ }
84
+ }
85
+ __decorate([
86
+ flags.boolean({ description: 'Print one JSON object to stdout' })
87
+ ], AgoraDoctor.prototype, "json", void 0);
88
+ __decorate([
89
+ flags.string({ description: 'Only these libs, comma separated (durable,payments)' })
90
+ ], AgoraDoctor.prototype, "lib", void 0);
91
+ __decorate([
92
+ flags.boolean({ description: 'Also run checks that touch the database, Redis or the network' })
93
+ ], AgoraDoctor.prototype, "deep", void 0);
94
+ __decorate([
95
+ flags.string({ description: 'Run one check by id' })
96
+ ], AgoraDoctor.prototype, "only", void 0);
97
+ __decorate([
98
+ flags.boolean({ description: 'Fail (exit 1) on warnings too' })
99
+ ], AgoraDoctor.prototype, "strict", void 0);
100
+ __decorate([
101
+ flags.boolean({ description: 'Show skipped checks and every message' })
102
+ ], AgoraDoctor.prototype, "verbose", void 0);
103
+ __decorate([
104
+ flags.boolean({
105
+ description: 'Boot the app first; --no-boot checks the config only (when boot itself fails)',
106
+ default: true,
107
+ showNegatedVariantInHelp: true,
108
+ })
109
+ ], AgoraDoctor.prototype, "boot", void 0);
@@ -11,8 +11,10 @@ import type { CommandOptions } from '@adonisjs/core/types/ace';
11
11
  * node ace authkit:clients:create --client-id=my-api --redirect-uri=https://api/cb --redirect-uri=https://api/cb2 --grant=client_credentials
12
12
  * node ace authkit:clients:create --redirect-uri=https://app/cb --json
13
13
  * node ace authkit:clients:create --client-id=my-mobile --native --redirect-uri=com.example.app:/oauth
14
+ * node ace authkit:clients:create --client-id=worker-acme --partition=acme --capability=reports
14
15
  */
15
16
  export default class AuthkitClientsCreate extends BaseCommand {
17
+ #private;
16
18
  static commandName: string;
17
19
  static description: string;
18
20
  static help: string[];
@@ -25,6 +27,8 @@ export default class AuthkitClientsCreate extends BaseCommand {
25
27
  native?: boolean;
26
28
  backchannelLogoutUri?: string;
27
29
  clientSecret?: string;
30
+ partition?: string;
31
+ capability?: string[];
28
32
  json?: boolean;
29
33
  run(): Promise<void>;
30
34
  }
@@ -16,6 +16,7 @@ import { BaseCommand, flags } from '@adonisjs/core/ace';
16
16
  * node ace authkit:clients:create --client-id=my-api --redirect-uri=https://api/cb --redirect-uri=https://api/cb2 --grant=client_credentials
17
17
  * node ace authkit:clients:create --redirect-uri=https://app/cb --json
18
18
  * node ace authkit:clients:create --client-id=my-mobile --native --redirect-uri=com.example.app:/oauth
19
+ * node ace authkit:clients:create --client-id=worker-acme --partition=acme --capability=reports
19
20
  */
20
21
  export default class AuthkitClientsCreate extends BaseCommand {
21
22
  static commandName = 'authkit:clients:create';
@@ -41,9 +42,18 @@ export default class AuthkitClientsCreate extends BaseCommand {
41
42
  ' node ace authkit:clients:create --redirect-uri=https://app/cb --backchannel-logout-uri=https://app/bc',
42
43
  ' node ace authkit:clients:create --client-id=my-app --redirect-uri=https://a/cb --redirect-uri=https://b/cb --json',
43
44
  ' node ace authkit:clients:create --client-id=my-mobile --native --redirect-uri=com.example.app:/oauth',
45
+ '',
46
+ 'Service tenant (a process that acts as one partition, e.g. a durable tenant pod):',
47
+ 'use --partition (and --capability, repeatable). Implies a confidential client with',
48
+ 'only the client_credentials grant and no redirect URIs. Needs serviceTenants.resources',
49
+ 'in config/authkit.ts.',
50
+ ' node ace authkit:clients:create --client-id=worker-acme --partition=acme --capability=reports',
44
51
  ];
45
52
  static options = { startApp: true };
46
53
  async run() {
54
+ if (this.partition !== undefined || this.capability?.length) {
55
+ return this.#createServiceTenant();
56
+ }
47
57
  const redirectUris = this.redirectUri ?? [];
48
58
  if (redirectUris.length === 0) {
49
59
  this.logger.logError('❌ --redirect-uri é obrigatório. Passe ao menos uma URI de callback.');
@@ -120,6 +130,83 @@ export default class AuthkitClientsCreate extends BaseCommand {
120
130
  this.logger.info('');
121
131
  }
122
132
  }
133
+ /** `--partition`: a service tenant (client_credentials only, `partition` claim). */
134
+ async #createServiceTenant() {
135
+ if (!this.partition) {
136
+ this.logger.logError('❌ --capability needs --partition.');
137
+ this.exitCode = 1;
138
+ return;
139
+ }
140
+ const conflicting = [
141
+ this.redirectUri?.length && '--redirect-uri',
142
+ this.postLogoutUri?.length && '--post-logout-uri',
143
+ this.public && '--public',
144
+ this.native && '--native',
145
+ this.backchannelLogoutUri && '--backchannel-logout-uri',
146
+ this.grant?.some((g) => g !== 'client_credentials') && '--grant',
147
+ ].filter(Boolean);
148
+ if (conflicting.length) {
149
+ this.logger.logError(`❌ ${conflicting.join(', ')} cannot be used with --partition (a service tenant only uses client_credentials).`);
150
+ this.exitCode = 1;
151
+ return;
152
+ }
153
+ const service = await this.app.container.make('authkit.server');
154
+ const { AdminClientsService } = await import('../src/host/admin_clients_service.js');
155
+ const { ClientMetadataError } = await import('../src/host/client_metadata.js');
156
+ const svc = new AdminClientsService(service);
157
+ const input = {
158
+ clientId: this.clientId,
159
+ applicationType: 'web',
160
+ redirectUris: [],
161
+ postLogoutRedirectUris: [],
162
+ grantTypes: ['client_credentials'],
163
+ tokenEndpointAuthMethod: 'client_secret_basic',
164
+ serviceTenant: { partition: this.partition, capabilities: this.capability ?? [] },
165
+ };
166
+ let created;
167
+ try {
168
+ created = this.clientSecret
169
+ ? await svc.createWithSecret(input, this.clientSecret)
170
+ : await svc.create(input);
171
+ }
172
+ catch (err) {
173
+ if (err instanceof ClientMetadataError) {
174
+ this.logger.logError(`❌ ${err.message}`);
175
+ this.exitCode = 1;
176
+ return;
177
+ }
178
+ throw err;
179
+ }
180
+ const tenant = (await svc.find(created.clientId))?.serviceTenant;
181
+ await service.config.audit
182
+ ?.record({
183
+ type: 'client.created',
184
+ clientId: created.clientId,
185
+ ip: null,
186
+ metadata: { actor: 'cli', partition: tenant?.partition },
187
+ })
188
+ .catch(() => { });
189
+ if (this.json) {
190
+ this.logger.info(JSON.stringify({
191
+ clientId: created.clientId,
192
+ clientSecret: created.clientSecret,
193
+ grantTypes: ['client_credentials'],
194
+ serviceTenant: tenant,
195
+ resources: Object.keys(service.config.serviceTenants.resources),
196
+ }, null, 2));
197
+ return;
198
+ }
199
+ this.logger.success(`Service tenant created: ${created.clientId}`);
200
+ this.logger.info(` partition: ${tenant?.partition}`);
201
+ if (tenant?.capabilities.length) {
202
+ this.logger.info(` capabilities: ${tenant.capabilities.join(', ')}`);
203
+ }
204
+ this.logger.info(` resources: ${Object.keys(service.config.serviceTenants.resources).join(', ')}`);
205
+ this.logger.info('');
206
+ this.logger.success('CLIENT SECRET (shown ONCE - store it now):');
207
+ this.logger.success(` ${created.clientSecret}`);
208
+ this.logger.info('');
209
+ }
123
210
  }
124
211
  __decorate([
125
212
  flags.string({ description: 'client_id desejado. Omitir gera um UUID aleatório.' })
@@ -155,6 +242,16 @@ __decorate([
155
242
  description: 'Usa este client_secret em vez de gerar um (client confidencial). Útil para manter o secret que os RPs já usam.',
156
243
  })
157
244
  ], AuthkitClientsCreate.prototype, "clientSecret", void 0);
245
+ __decorate([
246
+ flags.string({
247
+ description: 'Service tenant: the partition its tokens act as (`partition` claim). Implies client_credentials only, no redirect URIs.',
248
+ })
249
+ ], AuthkitClientsCreate.prototype, "partition", void 0);
250
+ __decorate([
251
+ flags.array({
252
+ description: 'Service tenant: capability granted in its tokens (repeatable). Needs --partition.',
253
+ })
254
+ ], AuthkitClientsCreate.prototype, "capability", void 0);
158
255
  __decorate([
159
256
  flags.boolean({
160
257
  description: 'Output em JSON machine-readable (inclui clientId e clientSecret).',
@@ -130,6 +130,20 @@
130
130
  "type": "string",
131
131
  "description": "Usa este client_secret em vez de gerar um (client confidencial)."
132
132
  },
133
+ {
134
+ "name": "partition",
135
+ "flagName": "partition",
136
+ "required": false,
137
+ "type": "string",
138
+ "description": "Service tenant: the partition its tokens act as (`partition` claim). Implies client_credentials only, no redirect URIs."
139
+ },
140
+ {
141
+ "name": "capability",
142
+ "flagName": "capability",
143
+ "required": false,
144
+ "type": "array",
145
+ "description": "Service tenant: capability granted in its tokens (repeatable). Needs --partition."
146
+ },
133
147
  {
134
148
  "name": "json",
135
149
  "flagName": "json",
@@ -319,6 +333,70 @@
319
333
  "args": [],
320
334
  "options": { "startApp": true },
321
335
  "filePath": "expire_scan.js"
336
+ },
337
+ {
338
+ "commandName": "agora:doctor",
339
+ "description": "Check the configuration of every installed @adonis-agora lib",
340
+ "namespace": "agora",
341
+ "aliases": [],
342
+ "flags": [
343
+ {
344
+ "name": "json",
345
+ "flagName": "json",
346
+ "required": false,
347
+ "type": "boolean",
348
+ "description": "Print one JSON object to stdout"
349
+ },
350
+ {
351
+ "name": "lib",
352
+ "flagName": "lib",
353
+ "required": false,
354
+ "type": "string",
355
+ "description": "Only these libs, comma separated (durable,payments)"
356
+ },
357
+ {
358
+ "name": "deep",
359
+ "flagName": "deep",
360
+ "required": false,
361
+ "type": "boolean",
362
+ "description": "Also run checks that touch the database, Redis or the network"
363
+ },
364
+ {
365
+ "name": "only",
366
+ "flagName": "only",
367
+ "required": false,
368
+ "type": "string",
369
+ "description": "Run one check by id"
370
+ },
371
+ {
372
+ "name": "strict",
373
+ "flagName": "strict",
374
+ "required": false,
375
+ "type": "boolean",
376
+ "description": "Fail (exit 1) on warnings too"
377
+ },
378
+ {
379
+ "name": "verbose",
380
+ "flagName": "verbose",
381
+ "required": false,
382
+ "type": "boolean",
383
+ "description": "Show skipped checks and every message"
384
+ },
385
+ {
386
+ "name": "boot",
387
+ "flagName": "boot",
388
+ "required": false,
389
+ "type": "boolean",
390
+ "description": "Boot the app first; --no-boot checks the config only (when boot itself fails)",
391
+ "default": true,
392
+ "showNegatedVariantInHelp": true
393
+ }
394
+ ],
395
+ "args": [],
396
+ "options": {
397
+ "startApp": false
398
+ },
399
+ "filePath": "agora_doctor.js"
322
400
  }
323
401
  ]
324
402
  }
@@ -1,43 +1,6 @@
1
- var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
- if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
- return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
- return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
- });
6
- }
7
- return path;
8
- };
9
1
  import { BaseCommand } from '@adonisjs/core/ace';
10
- import { DatabaseAdapter } from '../src/adapters/database_adapter.js';
11
- import { resolveAuthkitConfig } from '../src/commands/resolve_config.js';
12
- import { defaultEncryptForStore } from '../src/define_config.js';
13
- import { hasErrors, runAllChecks, signingKeyAgeFinding, } from '../src/doctor/checks.js';
14
- import { signingKeyAgeDays } from '../src/keys/keystore.js';
15
- import { KeystoreCodec } from '../src/keys/keystore_codec.js';
16
- import { loadEncryptionService } from '../src/keys/keystore_crypto.js';
17
- import { KeystoreManager, resolveKeystoreVault } from '../src/keys/keystore_manager.js';
18
- /**
19
- * Tenta importar um peer; `true` se importável.
20
- *
21
- * Distingue "não instalado" de "instalado mas quebrado": só um erro de
22
- * módulo-não-encontrado (`ERR_MODULE_NOT_FOUND`/`MODULE_NOT_FOUND`) é tratado como
23
- * peer ausente (silencioso — é o caso esperado). Qualquer outro erro significa que
24
- * o peer ESTÁ presente mas falhou ao carregar (import circular, erro de sintaxe,
25
- * config inválida) — isso o doctor precisa gritar, senão um peer quebrado aparece
26
- * como "ausente" e o diagnóstico mente.
27
- */
28
- async function canImport(specifier, logger) {
29
- try {
30
- await import(__rewriteRelativeImportExtension(specifier));
31
- return true;
32
- }
33
- catch (err) {
34
- const code = err?.code;
35
- if (code !== 'ERR_MODULE_NOT_FOUND' && code !== 'MODULE_NOT_FOUND') {
36
- logger?.warning(`⚠️ ${specifier} está instalado mas falhou ao carregar: ${err.message}`);
37
- }
38
- return false;
39
- }
40
- }
2
+ import { hasErrors, runAllChecks } from '../src/doctor/checks.js';
3
+ import { buildDoctorInput, managedSigningKeyAgeFinding } from '../src/doctor/doctor_input.js';
41
4
  export default class AuthkitDoctor extends BaseCommand {
42
5
  static commandName = 'authkit:doctor';
43
6
  static description = 'Valida a configuração do AuthKit no host e imprime achados (✅/⚠️/❌). Sai com código !=0 se houver erros.';
@@ -48,50 +11,21 @@ export default class AuthkitDoctor extends BaseCommand {
48
11
  ];
49
12
  static options = { startApp: true };
50
13
  async run() {
51
- const config = await this.app.container.make('config');
52
- // `defineConfig` exporta um config provider — resolve antes de inspecionar,
53
- // senão todo campo aparece como "ausente" (provider cru não tem issuer etc.).
54
- let authkitConfig = null;
14
+ // `buildDoctorInput` resolve o config provider de `config/authkit.ts` antes de inspecionar.
15
+ let input;
55
16
  try {
56
- authkitConfig = await resolveAuthkitConfig(this.app, config.get('authkit', null));
17
+ input = await buildDoctorInput(this.app, this.logger);
57
18
  }
58
19
  catch (error) {
59
20
  this.logger.logError(`❌ config/authkit.ts falhou ao resolver: ${error.message}`);
60
21
  this.exitCode = 1;
61
22
  return;
62
23
  }
63
- const sessionConfig = config.get('session', null) ?? null;
64
- const input = {
65
- authkitConfig,
66
- sessionConfig,
67
- peers: {
68
- session: await canImport('@adonisjs/session', this.logger),
69
- shield: await canImport('@adonisjs/shield', this.logger),
70
- ally: await canImport('@adonisjs/ally', this.logger),
71
- limiter: await canImport('@adonisjs/limiter', this.logger),
72
- },
73
- __adapterClasses: { DatabaseAdapter },
74
- };
75
24
  const findings = runAllChecks(input);
76
25
  // Adiciona o finding de idade da chave de assinatura managed (best-effort).
77
- const jwksInput = (authkitConfig?.jwksConfig ?? authkitConfig?.jwks);
78
- if (jwksInput?.source === 'managed' && jwksInput?.store) {
79
- try {
80
- const vault = resolveKeystoreVault(jwksInput.store, {
81
- makePath: (p) => this.app.makePath(p),
82
- container: this.app.container,
83
- });
84
- const encrypt = jwksInput.encrypt ?? defaultEncryptForStore(jwksInput.store);
85
- const enc = encrypt ? await loadEncryptionService().catch(() => undefined) : undefined;
86
- const mgr = new KeystoreManager(vault, new KeystoreCodec({ encrypt, enc }), jwksInput.algorithm ?? 'RS256');
87
- const store = await mgr.read().catch(() => null);
88
- const maxAge = jwksInput.rotationDays ?? 90;
89
- findings.push(signingKeyAgeFinding(signingKeyAgeDays(store), maxAge));
90
- }
91
- catch {
92
- /* idade é best-effort no doctor */
93
- }
94
- }
26
+ const keyAge = await managedSigningKeyAgeFinding(this.app, input.authkitConfig);
27
+ if (keyAge)
28
+ findings.push(keyAge);
95
29
  this.print(findings);
96
30
  if (hasErrors(findings)) {
97
31
  this.exitCode = 1;
@@ -17,11 +17,7 @@
17
17
  </form>
18
18
  </div>
19
19
 
20
- <nav class="mb-6 flex gap-4 text-sm font-medium">
21
- <a href="{{ accountPaths?.tokens ?? '/account/tokens' }}" class="text-gray-500 hover:underline">{{ t('account.tokens.title') }}</a>
22
- <a href="{{ accountPaths?.security ?? '/account/security' }}" class="text-gray-500 hover:underline">{{ t('account.security.title') }}</a>
23
- <a href="{{ accountPaths?.apps ?? '/account/apps' }}" class="text-gray-900 underline">{{ t('account.apps.title') }}</a>
24
- </nav>
20
+ @include('authkit::account/partials/nav')
25
21
 
26
22
  @if(revoked)
27
23
  <div class="mb-6 rounded-lg border border-emerald-300 bg-emerald-50 p-4">