@adonis-agora/authkit-server 1.0.0-rc.1 → 1.0.0-rc.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +107 -0
- package/README.md +4 -5
- package/build/commands/agora_doctor.d.ts +22 -0
- package/build/commands/agora_doctor.js +109 -0
- package/build/commands/clients_create.d.ts +4 -0
- package/build/commands/clients_create.js +97 -0
- package/build/commands/commands.json +78 -0
- package/build/commands/doctor.js +8 -74
- package/build/host/views/account/apps.edge +1 -5
- package/build/host/views/account/extension.edge +77 -0
- package/build/host/views/account/mfa.edge +2 -0
- package/build/host/views/account/partials/extension_actions.edge +14 -0
- package/build/host/views/account/partials/extension_block.edge +197 -0
- package/build/host/views/account/partials/extension_value.edge +8 -0
- package/build/host/views/account/partials/nav.edge +14 -0
- package/build/host/views/account/security.edge +1 -5
- package/build/host/views/account/tokens.edge +2 -2
- package/build/host/views/agents/consent.edge +11 -2
- package/build/host/views/agents/done.edge +1 -1
- package/build/host/views/partials/styles.edge +1 -1
- package/build/index.d.ts +12 -12
- package/build/index.js +7 -9
- package/build/providers/authkit_server_provider.js +29 -8
- package/build/src/account/extension.d.ts +10 -0
- package/build/src/account/extension.js +9 -0
- package/build/src/account/http.d.ts +77 -0
- package/build/src/account/http.js +381 -0
- package/build/src/account/mount.d.ts +40 -0
- package/build/src/account/mount.js +171 -0
- package/build/src/account/registry.d.ts +41 -0
- package/build/src/account/registry.js +81 -0
- package/build/src/account/remote.d.ts +72 -0
- package/build/src/account/remote.js +406 -0
- package/build/src/account/runtime.d.ts +289 -0
- package/build/src/account/runtime.js +641 -0
- package/build/src/accounts/account_store.d.ts +13 -7
- package/build/src/accounts/lucid_account_store.d.ts +3 -3
- package/build/src/accounts/lucid_account_store.js +42 -17
- package/build/src/accounts/lucid_store/mfa.js +3 -3
- package/build/src/accounts/lucid_store/organization_models.d.ts +14 -3
- package/build/src/accounts/lucid_store/organization_models.js +31 -3
- package/build/src/accounts/lucid_store/organizations.d.ts +2 -2
- package/build/src/accounts/lucid_store/organizations.js +40 -31
- package/build/src/accounts/lucid_store/shared.d.ts +11 -3
- package/build/src/accounts/lucid_store/shared.js +7 -5
- package/build/src/accounts/lucid_store/webauthn.js +1 -1
- package/build/src/adapters/factory.d.ts +13 -2
- package/build/src/adapters/factory.js +2 -2
- package/build/src/agents/config.d.ts +9 -13
- package/build/src/agents/config.js +17 -11
- package/build/src/agents/delegation_service.js +3 -3
- package/build/src/agents/delegation_store.d.ts +3 -2
- package/build/src/agents/delegation_store.js +6 -5
- package/build/src/agents/extension.d.ts +261 -0
- package/build/src/agents/extension.js +40 -0
- package/build/src/agents/middleware.js +1 -1
- package/build/src/agents/protocol.d.ts +4 -4
- package/build/src/agents/registry_store.d.ts +3 -2
- package/build/src/agents/registry_store.js +1 -1
- package/build/src/agents/runtime.d.ts +26 -7
- package/build/src/agents/runtime.js +110 -75
- package/build/src/audit/audit_sink.d.ts +1 -1
- package/build/src/audit/audit_sink.js +10 -1
- package/build/src/commands/expire_scan_command.js +1 -1
- package/build/src/commands/settings_commands.js +2 -2
- package/build/src/console_switcher.d.ts +132 -0
- package/build/src/console_switcher.js +206 -0
- package/build/src/define_config.d.ts +38 -13
- package/build/src/define_config.js +32 -10
- package/build/src/dev_tools/contract.d.ts +72 -0
- package/build/src/dev_tools/contract.js +10 -0
- package/build/src/dev_tools/gate.d.ts +17 -0
- package/build/src/dev_tools/gate.js +69 -0
- package/build/src/dev_tools/register.d.ts +11 -0
- package/build/src/dev_tools/register.js +25 -0
- package/build/src/dev_tools/tools.d.ts +24 -0
- package/build/src/dev_tools/tools.js +384 -0
- package/build/src/doctor/agora_checks.d.ts +15 -0
- package/build/src/doctor/agora_checks.js +150 -0
- package/build/src/doctor/agora_doctor.d.ts +115 -0
- package/build/src/doctor/agora_doctor.js +692 -0
- package/build/src/doctor/checks.js +2 -2
- package/build/src/doctor/doctor_input.d.ts +27 -0
- package/build/src/doctor/doctor_input.js +86 -0
- package/build/src/doctor/index.d.ts +5 -0
- package/build/src/doctor/index.js +4 -0
- package/build/src/host/access_token_verifier.js +7 -0
- package/build/src/host/account_api/account_api_controller.js +8 -6
- package/build/src/host/account_session_sign_in.d.ts +12 -0
- package/build/src/host/account_session_sign_in.js +19 -0
- package/build/src/host/admin_api/admin_users_service.d.ts +5 -2
- package/build/src/host/admin_api/api_clients_controller.d.ts +37 -0
- package/build/src/host/admin_api/api_clients_controller.js +31 -1
- package/build/src/host/admin_api/dto.d.ts +3 -1
- package/build/src/host/admin_api/dto.js +2 -0
- package/build/src/host/admin_authorize.d.ts +8 -0
- package/build/src/host/admin_authorize.js +25 -0
- package/build/src/host/admin_clients_service.d.ts +20 -0
- package/build/src/host/admin_clients_service.js +55 -0
- package/build/src/host/admin_console/admin_console_entry.d.ts +14 -0
- package/build/src/host/admin_console/admin_console_entry.js +80 -0
- package/build/src/host/admin_console/admin_shell_controller.js +8 -0
- package/build/src/host/admin_console/console_clients_controller.d.ts +41 -0
- package/build/src/host/admin_console/console_clients_controller.js +34 -1
- package/build/src/host/admin_console/console_personal_agents_controller.d.ts +1 -6
- package/build/src/host/admin_console/console_personal_agents_controller.js +50 -30
- package/build/src/host/admin_console/console_switcher_controller.d.ts +11 -0
- package/build/src/host/admin_console/console_switcher_controller.js +23 -0
- package/build/src/host/admin_sessions_service.js +8 -5
- package/build/src/host/admin_validators.d.ts +29 -0
- package/build/src/host/admin_validators.js +30 -0
- package/build/src/host/auth_host_config.d.ts +9 -2
- package/build/src/host/controllers/account_apps_controller.js +15 -11
- package/build/src/host/csrf.d.ts +3 -3
- package/build/src/host/csrf.js +3 -13
- package/build/src/host/default_mailer.d.ts +11 -6
- package/build/src/host/default_mailer.js +16 -12
- package/build/src/host/i18n.d.ts +41 -14
- package/build/src/host/i18n.js +55 -13
- package/build/src/host/rate_limit.d.ts +8 -0
- package/build/src/host/rate_limit.js +9 -0
- package/build/src/host/register_auth_host.d.ts +14 -0
- package/build/src/host/register_auth_host.js +167 -106
- package/build/src/host/renderers/edge_renderer.js +5 -0
- package/build/src/host/renderers/inertia_renderer.js +7 -1
- package/build/src/host/runtime_settings.d.ts +6 -6
- package/build/src/host/runtime_settings.js +4 -3
- package/build/src/host/screen_props.d.ts +39 -8
- package/build/src/host/ui-dist/assets/index-Dq3dJVBv.js +67 -0
- package/build/src/host/ui-dist/assets/{index-DTSmD4RU.css → index-WNIyCEPA.css} +1 -1
- package/build/src/host/ui-dist/index.html +2 -2
- package/build/src/provider/build_provider.js +37 -6
- package/build/src/provider/oidc_service.d.ts +1 -1
- package/build/src/provider/oidc_service.js +1 -1
- package/build/src/schema/ensure.d.ts +24 -1
- package/build/src/schema/ensure.js +44 -154
- package/build/src/schema/tables_location.d.ts +27 -0
- package/build/src/schema/tables_location.js +22 -0
- package/build/src/service_tenants/client_metadata.d.ts +39 -0
- package/build/src/service_tenants/client_metadata.js +80 -0
- package/build/src/service_tenants/config.d.ts +44 -0
- package/build/src/service_tenants/config.js +44 -0
- package/build/src/service_tenants/provider_hooks.d.ts +52 -0
- package/build/src/service_tenants/provider_hooks.js +124 -0
- package/build/src/service_tenants/tenant_verifier.d.ts +73 -0
- package/build/src/service_tenants/tenant_verifier.js +104 -0
- package/build/src/tenancy/tenant_labels.d.ts +30 -0
- package/build/src/tenancy/tenant_labels.js +64 -0
- package/build/types.d.ts +3 -1
- package/package.json +9 -6
- package/build/src/agents/poppy/client_registry.d.ts +0 -50
- package/build/src/agents/poppy/client_registry.js +0 -260
- package/build/src/agents/poppy/config.d.ts +0 -396
- package/build/src/agents/poppy/config.js +0 -228
- package/build/src/agents/poppy/dpop.d.ts +0 -61
- package/build/src/agents/poppy/dpop.js +0 -158
- package/build/src/agents/poppy/email_code_sign_in.d.ts +0 -27
- package/build/src/agents/poppy/email_code_sign_in.js +0 -140
- package/build/src/agents/poppy/errors.d.ts +0 -25
- package/build/src/agents/poppy/errors.js +0 -25
- package/build/src/agents/poppy/replay.d.ts +0 -25
- package/build/src/agents/poppy/replay.js +0 -106
- package/build/src/agents/poppy/resource.d.ts +0 -100
- package/build/src/agents/poppy/resource.js +0 -129
- package/build/src/agents/poppy/safe_fetch.d.ts +0 -48
- package/build/src/agents/poppy/safe_fetch.js +0 -262
- package/build/src/agents/poppy/service.d.ts +0 -293
- package/build/src/agents/poppy/service.js +0 -1458
- package/build/src/agents/poppy/session_jwt.d.ts +0 -30
- package/build/src/agents/poppy/session_jwt.js +0 -133
- package/build/src/agents/poppy/store.d.ts +0 -106
- package/build/src/agents/poppy/store.js +0 -261
- package/build/src/agents/poppy/web_session.d.ts +0 -39
- package/build/src/agents/poppy/web_session.js +0 -122
- package/build/src/host/controllers/poppy_consent_controller.d.ts +0 -19
- package/build/src/host/controllers/poppy_consent_controller.js +0 -199
- package/build/src/host/controllers/poppy_controller.d.ts +0 -33
- package/build/src/host/controllers/poppy_controller.js +0 -219
- package/build/src/host/poppy_session_key.d.ts +0 -9
- package/build/src/host/poppy_session_key.js +0 -9
- package/build/src/host/ui-dist/assets/index-D0EIeyKK.js +0 -67
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,112 @@
|
|
|
1
1
|
# @adonis-agora/authkit-server
|
|
2
2
|
|
|
3
|
+
## 1.0.0-rc.11
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#473](https://github.com/DavideCarvalho/adonis-agora-authkit/pull/473) [`427c4a3`](https://github.com/DavideCarvalho/adonis-agora-authkit/commit/427c4a301907dc78d8e7c5405d4e73e29b8a2be4) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Every table AuthKit owns now uses one Lucid connection: `schema.connection` (default: the primary connection). Before, some of them followed other connections, so with the account model on a named connection the boot created a table on one connection and the code read it on another:
|
|
8
|
+
|
|
9
|
+
- `auth_settings` (runtime settings, the settings CLI, `authkit:expire-scan`, the SDK's embedded driver) followed the account model's connection.
|
|
10
|
+
- `auth_mfa` and `auth_password_history` used the account model's connection.
|
|
11
|
+
- The built-in organization models had no connection, so they always used the primary one.
|
|
12
|
+
- `adapters.database()` and the `lucid` keystore without their own `connection` used the primary connection.
|
|
13
|
+
|
|
14
|
+
They all use `schema.connection` now; `adapters.database({ connection })` and `jwks.store: { driver: 'lucid', connection }` still win when set. The app's account table stays on the account model's connection, and the boot adds the `login_methods` column there. If your app had `schema.connection` unset and the account model on a named connection, those tables move to the primary connection: set `schema.connection` to the connection where they already are.
|
|
15
|
+
|
|
16
|
+
## 1.0.0-rc.10
|
|
17
|
+
|
|
18
|
+
### Minor Changes
|
|
19
|
+
|
|
20
|
+
- [#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:
|
|
21
|
+
|
|
22
|
+
- 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.
|
|
23
|
+
- 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.
|
|
24
|
+
- The contract types and validation now live in authkit-core (still exported from `@adonis-agora/authkit-server/account-extension`).
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- Updated dependencies [[`66ccc36`](https://github.com/DavideCarvalho/adonis-agora-authkit/commit/66ccc365dbbd7d9d5bf22dba7aad196aff9f5d4b)]:
|
|
29
|
+
- @adonis-agora/authkit-core@1.0.0-rc.1
|
|
30
|
+
|
|
31
|
+
## 1.0.0-rc.9
|
|
32
|
+
|
|
33
|
+
### Minor Changes
|
|
34
|
+
|
|
35
|
+
- [#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/*`.
|
|
36
|
+
|
|
37
|
+
- 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.
|
|
38
|
+
- 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).
|
|
39
|
+
- `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`.
|
|
40
|
+
- The account screens share one navigation (`accountNav`) that lists the mounted screens and the extension pages; `AuthHostRouteMap.account.extensions` lists the pages.
|
|
41
|
+
- JSON API: `GET {prefix}/api/extensions`, `GET {prefix}/api/x/:pageId`, `POST {prefix}/api/x/:pageId/actions/:action`.
|
|
42
|
+
- authkit-react: `<AccountExtensionPage>`, `useAccountExtensions()`, the page/action query options and `client.account.extensions.*`.
|
|
43
|
+
|
|
44
|
+
## 1.0.0-rc.8
|
|
45
|
+
|
|
46
|
+
### Minor Changes
|
|
47
|
+
|
|
48
|
+
- [#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.
|
|
49
|
+
|
|
50
|
+
## 1.0.0-rc.7
|
|
51
|
+
|
|
52
|
+
### Minor Changes
|
|
53
|
+
|
|
54
|
+
- [#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).
|
|
55
|
+
|
|
56
|
+
## 1.0.0-rc.6
|
|
57
|
+
|
|
58
|
+
### Minor Changes
|
|
59
|
+
|
|
60
|
+
- [#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`.
|
|
61
|
+
|
|
62
|
+
`@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`).
|
|
63
|
+
|
|
64
|
+
## 1.0.0-rc.5
|
|
65
|
+
|
|
66
|
+
### Minor Changes
|
|
67
|
+
|
|
68
|
+
- [#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.
|
|
69
|
+
|
|
70
|
+
## 1.0.0-rc.4
|
|
71
|
+
|
|
72
|
+
### Patch Changes
|
|
73
|
+
|
|
74
|
+
- fix(deps): update dependency jose to v6.2.16 ([#454](https://github.com/DavideCarvalho/adonis-agora-authkit/issues/454))
|
|
75
|
+
|
|
76
|
+
## 1.0.0-rc.3
|
|
77
|
+
|
|
78
|
+
### Minor Changes
|
|
79
|
+
|
|
80
|
+
- [#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.
|
|
81
|
+
|
|
82
|
+
- `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.
|
|
83
|
+
- 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`.
|
|
84
|
+
- 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.
|
|
85
|
+
- `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.
|
|
86
|
+
- New audit events: `client.disabled`, `client.enabled`, `service_tenant.token_issued`.
|
|
87
|
+
- 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.
|
|
88
|
+
|
|
89
|
+
## 1.0.0-rc.2
|
|
90
|
+
|
|
91
|
+
### Major Changes
|
|
92
|
+
|
|
93
|
+
- [#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).
|
|
94
|
+
|
|
95
|
+
Breaking changes (`@adonis-agora/authkit-server`):
|
|
96
|
+
|
|
97
|
+
- `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.
|
|
98
|
+
- 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`.
|
|
99
|
+
- `mail.onPoppySignInCode` is removed: pass `sendCode` to `poppyEmailCodeSignIn()`.
|
|
100
|
+
- `authkitCsrfExceptions` no longer takes `poppyPrefix`; extensions' endpoints are exempted from the config (`personalAgentExtensions: false` turns that off).
|
|
101
|
+
- `ensureAuthkitSchema()` no longer creates the `auth_poppy_*` tables (the extension's tables are ensured at boot; `ensurePoppySchema()` for migrations).
|
|
102
|
+
- Admin console API: an extension's `protocols` entry is `{ label, requireRegistration, hostResolver, allowHttpIdentifiers, identifierLabel, identifierPlaceholder }`.
|
|
103
|
+
|
|
104
|
+
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`.
|
|
105
|
+
|
|
106
|
+
Breaking changes (`@adonis-agora/authkit-react`):
|
|
107
|
+
|
|
108
|
+
- `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`.
|
|
109
|
+
|
|
3
110
|
## 1.0.0-rc.1
|
|
4
111
|
|
|
5
112
|
### 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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
}
|
package/build/commands/doctor.js
CHANGED
|
@@ -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 {
|
|
11
|
-
import {
|
|
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
|
-
|
|
52
|
-
|
|
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
|
-
|
|
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
|
|
78
|
-
if (
|
|
79
|
-
|
|
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
|
-
|
|
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">
|