@adonis-agora/authkit-server 1.0.0-rc.1 → 1.0.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +94 -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 +28 -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 +6 -0
- package/build/src/accounts/lucid_store/organizations.js +6 -0
- 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/console_switcher.d.ts +132 -0
- package/build/src/console_switcher.js +206 -0
- package/build/src/define_config.d.ts +34 -12
- package/build/src/define_config.js +15 -7
- 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/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/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 +14 -0
- package/build/src/schema/ensure.js +43 -150
- 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
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { getBootedApp } from '../../../services/booted_app.js';
|
|
2
|
+
import { supportsOrganizations } from '../../accounts/account_store.js';
|
|
3
|
+
import { readAgoraConsoles, } from '../../console_switcher.js';
|
|
4
|
+
/** The admin console's id in the `'agora.consoles'` registry. */
|
|
5
|
+
export const ADMIN_CONSOLE_ID = 'authkit';
|
|
6
|
+
/** The `'agora.consoles'` registry, or `null` (none bound, or no booted app). */
|
|
7
|
+
export async function consoleRegistry() {
|
|
8
|
+
try {
|
|
9
|
+
return await readAgoraConsoles(getBootedApp());
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const ORG_CACHE_MS = 30_000;
|
|
16
|
+
const orgCache = new WeakMap();
|
|
17
|
+
/** The store's organizations, cached for 30 s per store so a search session does one query. */
|
|
18
|
+
function cachedOrgs(store, load) {
|
|
19
|
+
const hit = orgCache.get(store);
|
|
20
|
+
if (hit && Date.now() - hit.at < ORG_CACHE_MS)
|
|
21
|
+
return hit.rows;
|
|
22
|
+
const rows = load();
|
|
23
|
+
orgCache.set(store, { at: Date.now(), rows });
|
|
24
|
+
rows.catch(() => orgCache.delete(store));
|
|
25
|
+
return rows;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Global search of the admin console: users by email (as the account store's `listAccounts`
|
|
29
|
+
* search matches them), and organizations by name or slug.
|
|
30
|
+
* Each hit opens the matching drawer in the console.
|
|
31
|
+
*/
|
|
32
|
+
export async function searchAdminConsole(ctx, adminPrefix, query, limit) {
|
|
33
|
+
const service = await ctx.containerResolver.make('authkit.server');
|
|
34
|
+
const store = service.config.accountStore;
|
|
35
|
+
const hits = [];
|
|
36
|
+
const accounts = await store.listAccounts({ search: query, page: 1, size: limit });
|
|
37
|
+
for (const account of accounts.data.slice(0, limit)) {
|
|
38
|
+
hits.push({
|
|
39
|
+
title: account.email,
|
|
40
|
+
subtitle: account.name ? `User · ${account.name}` : 'User',
|
|
41
|
+
href: `${adminPrefix}?view=users&user=${encodeURIComponent(account.id)}`,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
// Organizations only from a store that lists them in one query, for queries of 2+ characters,
|
|
45
|
+
// from a list cached briefly per process: search runs on every (debounced) keystroke.
|
|
46
|
+
if (hits.length < limit && query.length >= 2 && supportsOrganizations(store)) {
|
|
47
|
+
const listAll = store
|
|
48
|
+
.listAllOrgsWithMemberCount;
|
|
49
|
+
if (typeof listAll === 'function') {
|
|
50
|
+
const needle = query.toLowerCase();
|
|
51
|
+
const orgs = await cachedOrgs(store, () => listAll.call(store));
|
|
52
|
+
for (const org of orgs) {
|
|
53
|
+
if (hits.length >= limit)
|
|
54
|
+
break;
|
|
55
|
+
if (!org.name.toLowerCase().includes(needle) && !org.slug.toLowerCase().includes(needle)) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
hits.push({
|
|
59
|
+
title: org.name,
|
|
60
|
+
subtitle: `Organization · ${org.slug}`,
|
|
61
|
+
href: `${adminPrefix}?view=orgs&org=${encodeURIComponent(org.id)}`,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return hits;
|
|
67
|
+
}
|
|
68
|
+
/** The admin console's registry entry, for the shell mounted at `adminPrefix`. */
|
|
69
|
+
export function adminConsoleEntry(adminPrefix, authorize) {
|
|
70
|
+
return {
|
|
71
|
+
id: ADMIN_CONSOLE_ID,
|
|
72
|
+
title: 'Identity',
|
|
73
|
+
package: '@adonis-agora/authkit-server',
|
|
74
|
+
icon: 'identity',
|
|
75
|
+
path: adminPrefix,
|
|
76
|
+
order: 90,
|
|
77
|
+
authorize,
|
|
78
|
+
search: (ctx, query, limit) => searchAdminConsole(ctx, adminPrefix, query, limit),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { extname } from 'node:path';
|
|
3
|
+
import { countOtherConsoles } from '../../console_switcher.js';
|
|
3
4
|
import { accountPath } from '../account_paths.js';
|
|
4
5
|
import { resolveAccountRoles, rolesManagedExternally } from '../account_roles.js';
|
|
5
6
|
import { getAdminPrefix } from '../admin_prefix.js';
|
|
7
|
+
import { ADMIN_CONSOLE_ID, consoleRegistry } from './admin_console_entry.js';
|
|
6
8
|
// ─── Shell HTML cache ─────────────────────────────────────────────────────────
|
|
7
9
|
/**
|
|
8
10
|
* Points to the Vite-built index.html inside build/host/ui-dist/.
|
|
@@ -141,6 +143,9 @@ export default class AdminShellController {
|
|
|
141
143
|
}
|
|
142
144
|
}
|
|
143
145
|
const adminBase = getAdminPrefix();
|
|
146
|
+
// Console switcher: only announced when other `@adonis-agora` consoles are registered; the SPA
|
|
147
|
+
// then asks the endpoint which of them this viewer may open.
|
|
148
|
+
const others = countOtherConsoles(await consoleRegistry(), ADMIN_CONSOLE_ID);
|
|
144
149
|
const authkitConfig = {
|
|
145
150
|
adminBase,
|
|
146
151
|
csrfToken: ctx.request.csrfToken ?? null,
|
|
@@ -152,6 +157,9 @@ export default class AdminShellController {
|
|
|
152
157
|
// Confirmação de identidade (sudo) das ações destrutivas.
|
|
153
158
|
confirm: accountPath('confirm'),
|
|
154
159
|
},
|
|
160
|
+
...(others > 0 ? { switcher: { endpoint: `${adminBase}/api/consoles`, others } } : {}),
|
|
161
|
+
// Global search (Ctrl/Cmd-K) across this console and every other one the viewer may open.
|
|
162
|
+
search: { endpoint: `${adminBase}/api/consoles/search` },
|
|
155
163
|
features: {
|
|
156
164
|
// A página de personal agents só aparece com `personalAgents` no config.
|
|
157
165
|
personalAgents: !!cfg.personalAgents,
|
|
@@ -9,8 +9,11 @@ import type { HttpContext } from '@adonisjs/core/http';
|
|
|
9
9
|
* PATCH {prefix}/api/clients/:id → atualizar metadata
|
|
10
10
|
* DELETE {prefix}/api/clients/:id → remover client
|
|
11
11
|
* POST {prefix}/api/clients/:id/regenerate-secret → gerar novo secret
|
|
12
|
+
* POST {prefix}/api/clients/:id/disable → stop issuing tokens to the client
|
|
13
|
+
* POST {prefix}/api/clients/:id/enable → issue tokens again
|
|
12
14
|
*/
|
|
13
15
|
export default class ConsoleClientsController {
|
|
16
|
+
#private;
|
|
14
17
|
/** GET {prefix}/api/clients */
|
|
15
18
|
index(ctx: HttpContext): Promise<{
|
|
16
19
|
data: {
|
|
@@ -25,6 +28,8 @@ export default class ConsoleClientsController {
|
|
|
25
28
|
backchannelLogoutSessionRequired: boolean;
|
|
26
29
|
clientName: string | null;
|
|
27
30
|
logoUri: string | null;
|
|
31
|
+
disabled: boolean;
|
|
32
|
+
serviceTenant: import("../../service_tenants/client_metadata.ts").ServiceTenantInfo | null;
|
|
28
33
|
}[];
|
|
29
34
|
canList: boolean;
|
|
30
35
|
}>;
|
|
@@ -41,6 +46,8 @@ export default class ConsoleClientsController {
|
|
|
41
46
|
backchannelLogoutSessionRequired: boolean;
|
|
42
47
|
clientName: string | null;
|
|
43
48
|
logoUri: string | null;
|
|
49
|
+
disabled: boolean;
|
|
50
|
+
serviceTenant: import("../../service_tenants/client_metadata.ts").ServiceTenantInfo | null;
|
|
44
51
|
}>;
|
|
45
52
|
/** POST {prefix}/api/clients */
|
|
46
53
|
store(ctx: HttpContext): Promise<void | {
|
|
@@ -60,12 +67,46 @@ export default class ConsoleClientsController {
|
|
|
60
67
|
backchannelLogoutSessionRequired: boolean;
|
|
61
68
|
clientName: string | null;
|
|
62
69
|
logoUri: string | null;
|
|
70
|
+
disabled: boolean;
|
|
71
|
+
serviceTenant: import("../../service_tenants/client_metadata.ts").ServiceTenantInfo | null;
|
|
63
72
|
}>;
|
|
64
73
|
/** DELETE {prefix}/api/clients/:id */
|
|
65
74
|
destroy(ctx: HttpContext): Promise<void | {
|
|
66
75
|
ok: boolean;
|
|
67
76
|
deleted: string;
|
|
68
77
|
}>;
|
|
78
|
+
/** POST {prefix}/api/clients/:id/disable */
|
|
79
|
+
disable(ctx: HttpContext): Promise<void | {
|
|
80
|
+
clientId: string;
|
|
81
|
+
confidential: boolean;
|
|
82
|
+
grantTypes: string[];
|
|
83
|
+
redirectUris: string[];
|
|
84
|
+
postLogoutRedirectUris: string[];
|
|
85
|
+
tokenEndpointAuthMethod: string;
|
|
86
|
+
applicationType: import("../client_metadata.js").ApplicationType;
|
|
87
|
+
backchannelLogoutUri: string | null;
|
|
88
|
+
backchannelLogoutSessionRequired: boolean;
|
|
89
|
+
clientName: string | null;
|
|
90
|
+
logoUri: string | null;
|
|
91
|
+
disabled: boolean;
|
|
92
|
+
serviceTenant: import("../../service_tenants/client_metadata.ts").ServiceTenantInfo | null;
|
|
93
|
+
}>;
|
|
94
|
+
/** POST {prefix}/api/clients/:id/enable */
|
|
95
|
+
enable(ctx: HttpContext): Promise<void | {
|
|
96
|
+
clientId: string;
|
|
97
|
+
confidential: boolean;
|
|
98
|
+
grantTypes: string[];
|
|
99
|
+
redirectUris: string[];
|
|
100
|
+
postLogoutRedirectUris: string[];
|
|
101
|
+
tokenEndpointAuthMethod: string;
|
|
102
|
+
applicationType: import("../client_metadata.js").ApplicationType;
|
|
103
|
+
backchannelLogoutUri: string | null;
|
|
104
|
+
backchannelLogoutSessionRequired: boolean;
|
|
105
|
+
clientName: string | null;
|
|
106
|
+
logoUri: string | null;
|
|
107
|
+
disabled: boolean;
|
|
108
|
+
serviceTenant: import("../../service_tenants/client_metadata.ts").ServiceTenantInfo | null;
|
|
109
|
+
}>;
|
|
69
110
|
/** POST {prefix}/api/clients/:id/regenerate-secret */
|
|
70
111
|
regenerateSecret(ctx: HttpContext): Promise<void | {
|
|
71
112
|
clientId: string;
|
|
@@ -19,6 +19,8 @@ function invalidMetadata(ctx, err) {
|
|
|
19
19
|
* PATCH {prefix}/api/clients/:id → atualizar metadata
|
|
20
20
|
* DELETE {prefix}/api/clients/:id → remover client
|
|
21
21
|
* POST {prefix}/api/clients/:id/regenerate-secret → gerar novo secret
|
|
22
|
+
* POST {prefix}/api/clients/:id/disable → stop issuing tokens to the client
|
|
23
|
+
* POST {prefix}/api/clients/:id/enable → issue tokens again
|
|
22
24
|
*/
|
|
23
25
|
export default class ConsoleClientsController {
|
|
24
26
|
/** GET {prefix}/api/clients */
|
|
@@ -107,6 +109,34 @@ export default class ConsoleClientsController {
|
|
|
107
109
|
});
|
|
108
110
|
return { ok: true, deleted: id };
|
|
109
111
|
}
|
|
112
|
+
/** POST {prefix}/api/clients/:id/disable */
|
|
113
|
+
async disable(ctx) {
|
|
114
|
+
return this.#setDisabled(ctx, true);
|
|
115
|
+
}
|
|
116
|
+
/** POST {prefix}/api/clients/:id/enable */
|
|
117
|
+
async enable(ctx) {
|
|
118
|
+
return this.#setDisabled(ctx, false);
|
|
119
|
+
}
|
|
120
|
+
/** Disabled clients get `invalid_client` from the next request on. */
|
|
121
|
+
async #setDisabled(ctx, disabled) {
|
|
122
|
+
const service = await ctx.containerResolver.make('authkit.server');
|
|
123
|
+
const svc = new AdminClientsService(service);
|
|
124
|
+
const id = ctx.request.param('id');
|
|
125
|
+
if (!(await svc.setDisabled(id, disabled))) {
|
|
126
|
+
return ctx.response.notFound(apiError('not_found', 'Client não encontrado.'));
|
|
127
|
+
}
|
|
128
|
+
const client = await svc.find(id);
|
|
129
|
+
await service.config.audit?.record({
|
|
130
|
+
type: disabled ? 'client.disabled' : 'client.enabled',
|
|
131
|
+
clientId: id,
|
|
132
|
+
ip: ctx.request.ip?.() ?? null,
|
|
133
|
+
metadata: {
|
|
134
|
+
actor: 'admin-console',
|
|
135
|
+
...(client?.serviceTenant ? { partition: client.serviceTenant.partition } : {}),
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
return clientDto(client);
|
|
139
|
+
}
|
|
110
140
|
/** POST {prefix}/api/clients/:id/regenerate-secret */
|
|
111
141
|
async regenerateSecret(ctx) {
|
|
112
142
|
const service = await ctx.containerResolver.make('authkit.server');
|
|
@@ -129,7 +159,10 @@ export default class ConsoleClientsController {
|
|
|
129
159
|
type: 'client.secret_regenerated',
|
|
130
160
|
clientId: id,
|
|
131
161
|
ip: ctx.request.ip?.() ?? null,
|
|
132
|
-
metadata: {
|
|
162
|
+
metadata: {
|
|
163
|
+
actor: 'admin-console',
|
|
164
|
+
...(existing.serviceTenant ? { partition: existing.serviceTenant.partition } : {}),
|
|
165
|
+
},
|
|
133
166
|
});
|
|
134
167
|
return { clientId: id, clientSecret: newSecret };
|
|
135
168
|
}
|
|
@@ -22,7 +22,7 @@ interface AgentEntry {
|
|
|
22
22
|
connections: number;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
-
* Endpoints JSON de personal agents (PACT e
|
|
25
|
+
* Endpoints JSON de personal agents (PACT e `personalAgents.extensions`) do console admin React.
|
|
26
26
|
*
|
|
27
27
|
* GET {prefix}/api/personal-agents → agentes (config + console + conectados)
|
|
28
28
|
* GET {prefix}/api/personal-agents/connections?clientId=&accountId=&protocol=&page=&size=
|
|
@@ -41,11 +41,6 @@ export default class ConsolePersonalAgentsController {
|
|
|
41
41
|
/** A lista do config é uma função do host (o console não enxerga). */
|
|
42
42
|
hostResolver: boolean;
|
|
43
43
|
} | null;
|
|
44
|
-
poppy: {
|
|
45
|
-
requireRegistration: boolean;
|
|
46
|
-
hostResolver: boolean;
|
|
47
|
-
allowInsecureClientIds: boolean;
|
|
48
|
-
} | null;
|
|
49
44
|
};
|
|
50
45
|
agents: AgentEntry[];
|
|
51
46
|
}>;
|
|
@@ -31,10 +31,21 @@ function enabledProtocols(runtime) {
|
|
|
31
31
|
const out = [];
|
|
32
32
|
if (runtime.config.pact)
|
|
33
33
|
out.push('pact');
|
|
34
|
-
|
|
35
|
-
out.push('poppy');
|
|
34
|
+
out.push(...runtime.extensions.keys());
|
|
36
35
|
return out;
|
|
37
36
|
}
|
|
37
|
+
/** O protocolo dono de um grant, pelo prefixo do id. */
|
|
38
|
+
function protocolOfGrant(runtime, grantId) {
|
|
39
|
+
for (const [id, { extension }] of runtime.extensions) {
|
|
40
|
+
if (grantId.startsWith(extension.grantIdPrefix))
|
|
41
|
+
return id;
|
|
42
|
+
}
|
|
43
|
+
return 'pact';
|
|
44
|
+
}
|
|
45
|
+
/** Prefixo de id dos grants de um protocolo (`agrant_` = PACT). */
|
|
46
|
+
function grantPrefixOf(runtime, protocol) {
|
|
47
|
+
return runtime.extensions.get(protocol)?.extension.grantIdPrefix ?? 'agrant_';
|
|
48
|
+
}
|
|
38
49
|
function isUrl(value, allowHttp) {
|
|
39
50
|
if (typeof value !== 'string' || value.length > 2048)
|
|
40
51
|
return false;
|
|
@@ -52,14 +63,14 @@ function isUrl(value, allowHttp) {
|
|
|
52
63
|
async function agentName(runtime, protocol, identifier, fallback) {
|
|
53
64
|
if (fallback)
|
|
54
65
|
return fallback;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
66
|
+
const extension = runtime.extensions.get(protocol);
|
|
67
|
+
if (extension)
|
|
68
|
+
return extension.runtime.displayName(identifier);
|
|
58
69
|
const registration = await runtime.config.resolveAgent(identifier);
|
|
59
70
|
return displayNameFor(registration ?? { issuer: identifier, jwksUri: '' });
|
|
60
71
|
}
|
|
61
72
|
/**
|
|
62
|
-
* Endpoints JSON de personal agents (PACT e
|
|
73
|
+
* Endpoints JSON de personal agents (PACT e `personalAgents.extensions`) do console admin React.
|
|
63
74
|
*
|
|
64
75
|
* GET {prefix}/api/personal-agents → agentes (config + console + conectados)
|
|
65
76
|
* GET {prefix}/api/personal-agents/connections?clientId=&accountId=&protocol=&page=&size=
|
|
@@ -105,17 +116,20 @@ export default class ConsolePersonalAgentsController {
|
|
|
105
116
|
e.jwksUri = agent.jwksUri;
|
|
106
117
|
names.set(keyOf('pact', agent.issuer), agent.name ?? null);
|
|
107
118
|
}
|
|
108
|
-
for (const [clientId, count] of await runtime.grants.countLiveGrantsByClient(now, '
|
|
119
|
+
for (const [clientId, count] of await runtime.grants.countLiveGrantsByClient(now, 'agrant_')) {
|
|
109
120
|
entry('pact', clientId).connections = count;
|
|
110
121
|
}
|
|
111
122
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
123
|
+
const summaries = {};
|
|
124
|
+
for (const [protocol, { extension, runtime: ext }] of runtime.extensions) {
|
|
125
|
+
const summary = ext.consoleSummary();
|
|
126
|
+
summaries[protocol] = { label: extension.label, ...summary };
|
|
127
|
+
for (const id of summary.configured.allowed)
|
|
128
|
+
entry(protocol, id).config = 'allowed';
|
|
129
|
+
for (const id of summary.configured.blocked)
|
|
130
|
+
entry(protocol, id).config = 'blocked';
|
|
131
|
+
for (const [clientId, count] of await runtime.grants.countLiveGrantsByClient(now, extension.grantIdPrefix)) {
|
|
132
|
+
entry(protocol, clientId).connections = count;
|
|
119
133
|
}
|
|
120
134
|
}
|
|
121
135
|
for (const row of await runtime.registry.list()) {
|
|
@@ -153,13 +167,17 @@ export default class ConsolePersonalAgentsController {
|
|
|
153
167
|
hostResolver: cfg.staticAgents === null,
|
|
154
168
|
}
|
|
155
169
|
: null,
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
170
|
+
...Object.fromEntries(Object.entries(summaries).map(([protocol, summary]) => [
|
|
171
|
+
protocol,
|
|
172
|
+
{
|
|
173
|
+
label: summary.label,
|
|
174
|
+
requireRegistration: summary.requireRegistration,
|
|
175
|
+
hostResolver: summary.hostResolver,
|
|
176
|
+
allowHttpIdentifiers: summary.allowHttpIdentifiers,
|
|
177
|
+
identifierLabel: summary.identifierLabel,
|
|
178
|
+
identifierPlaceholder: summary.identifierPlaceholder ?? null,
|
|
179
|
+
},
|
|
180
|
+
])),
|
|
163
181
|
},
|
|
164
182
|
agents,
|
|
165
183
|
};
|
|
@@ -172,11 +190,9 @@ export default class ConsolePersonalAgentsController {
|
|
|
172
190
|
const qs = ctx.request.qs();
|
|
173
191
|
const page = parseListPage(qs.page);
|
|
174
192
|
const size = parseListSize(qs.size);
|
|
175
|
-
const protocol = qs.protocol
|
|
176
|
-
? qs.protocol
|
|
177
|
-
: undefined;
|
|
193
|
+
const protocol = qs.protocol && enabledProtocols(runtime).includes(qs.protocol) ? qs.protocol : undefined;
|
|
178
194
|
const filter = {
|
|
179
|
-
protocol,
|
|
195
|
+
idPrefix: protocol ? grantPrefixOf(runtime, protocol) : undefined,
|
|
180
196
|
clientId: qs.clientId?.trim() || undefined,
|
|
181
197
|
accountId: qs.accountId?.trim() || undefined,
|
|
182
198
|
};
|
|
@@ -189,7 +205,7 @@ export default class ConsolePersonalAgentsController {
|
|
|
189
205
|
const emails = new Map();
|
|
190
206
|
const nameCache = new Map();
|
|
191
207
|
const data = await Promise.all(rows.map(async (g) => {
|
|
192
|
-
const rowProtocol = g.id
|
|
208
|
+
const rowProtocol = protocolOfGrant(runtime, g.id);
|
|
193
209
|
if (!emails.has(g.accountId)) {
|
|
194
210
|
const account = await store.findById(g.accountId).catch(() => null);
|
|
195
211
|
emails.set(g.accountId, account?.email ?? null);
|
|
@@ -248,17 +264,21 @@ export default class ConsolePersonalAgentsController {
|
|
|
248
264
|
const invalid = (message) => ctx.response.unprocessableEntity(apiError('invalid_request', message));
|
|
249
265
|
const protocol = body.protocol;
|
|
250
266
|
if (!enabledProtocols(runtime).includes(protocol)) {
|
|
251
|
-
return invalid(
|
|
267
|
+
return invalid(`\`protocol\` precisa ser um protocolo ligado (${enabledProtocols(runtime)
|
|
268
|
+
.map((p) => `\`${p}\``)
|
|
269
|
+
.join(', ')}).`);
|
|
252
270
|
}
|
|
253
271
|
const status = body.status;
|
|
254
272
|
if (status !== 'allowed' && status !== 'blocked') {
|
|
255
273
|
return invalid('`status` precisa ser `allowed` ou `blocked`.');
|
|
256
274
|
}
|
|
257
|
-
const
|
|
275
|
+
const extension = runtime.extensions.get(protocol);
|
|
276
|
+
const summary = extension?.runtime.consoleSummary();
|
|
277
|
+
const allowHttp = !!summary?.allowHttpIdentifiers;
|
|
258
278
|
const identifier = typeof body.identifier === 'string' ? body.identifier.trim() : '';
|
|
259
279
|
if (!isUrl(identifier, allowHttp)) {
|
|
260
|
-
return invalid(
|
|
261
|
-
?
|
|
280
|
+
return invalid(summary
|
|
281
|
+
? `\`identifier\` precisa ser uma URL ${allowHttp ? 'http(s)' : 'HTTPS'}: ${summary.identifierLabel}.`
|
|
262
282
|
: '`identifier` precisa ser o `iss` do agente (URL HTTPS).');
|
|
263
283
|
}
|
|
264
284
|
const name = typeof body.name === 'string' && body.name.trim() ? body.name.trim().slice(0, 200) : null;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
2
|
+
/**
|
|
3
|
+
* The console switcher endpoints of the admin console (behind the admin console's own guard).
|
|
4
|
+
*
|
|
5
|
+
* GET {prefix}/api/consoles → the other consoles this viewer may open
|
|
6
|
+
* GET {prefix}/api/consoles/search?q= → hits from every console this viewer may open
|
|
7
|
+
*/
|
|
8
|
+
export default class ConsoleSwitcherController {
|
|
9
|
+
index(ctx: HttpContext): Promise<import("../../console_switcher.js").VisibleConsoles>;
|
|
10
|
+
search(ctx: HttpContext): Promise<import("../../console_switcher.js").ConsoleSearchResults>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { listVisibleConsoles, searchConsoles } from '../../console_switcher.js';
|
|
2
|
+
import { ADMIN_CONSOLE_ID, consoleRegistry } from './admin_console_entry.js';
|
|
3
|
+
/**
|
|
4
|
+
* The console switcher endpoints of the admin console (behind the admin console's own guard).
|
|
5
|
+
*
|
|
6
|
+
* GET {prefix}/api/consoles → the other consoles this viewer may open
|
|
7
|
+
* GET {prefix}/api/consoles/search?q= → hits from every console this viewer may open
|
|
8
|
+
*/
|
|
9
|
+
export default class ConsoleSwitcherController {
|
|
10
|
+
async index(ctx) {
|
|
11
|
+
const body = await listVisibleConsoles(await consoleRegistry(), ADMIN_CONSOLE_ID, ctx, {
|
|
12
|
+
logger: ctx.logger,
|
|
13
|
+
});
|
|
14
|
+
ctx.response.header('Cache-Control', 'private, no-store');
|
|
15
|
+
return body;
|
|
16
|
+
}
|
|
17
|
+
async search(ctx) {
|
|
18
|
+
const q = ctx.request.input('q', '');
|
|
19
|
+
const body = await searchConsoles(await consoleRegistry(), ADMIN_CONSOLE_ID, ctx, typeof q === 'string' ? q : '', { logger: ctx.logger });
|
|
20
|
+
ctx.response.header('Cache-Control', 'private, no-store');
|
|
21
|
+
return body;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -87,7 +87,7 @@ export class AdminSessionsService {
|
|
|
87
87
|
/**
|
|
88
88
|
* Revogação total também corta os personal agents: "sair de todas as sessões",
|
|
89
89
|
* reset de senha, a ação do admin e a exclusão de conta não podem deixar um
|
|
90
|
-
* agente agindo na conta (delegações do PACT e
|
|
90
|
+
* agente agindo na conta (delegações do PACT e conexões das extensions). Sem
|
|
91
91
|
* `personalAgents` é no-op — e aí nem resolve o banco. Diferente do
|
|
92
92
|
* `recordSubRevocation`, uma falha AQUI propaga: não há outra camada que corte
|
|
93
93
|
* esses tokens.
|
|
@@ -142,11 +142,14 @@ export class AdminSessionsService {
|
|
|
142
142
|
return [];
|
|
143
143
|
return (await adapter.list()).filter((r) => grantIds.has(r.payload.grantId));
|
|
144
144
|
}
|
|
145
|
-
/**
|
|
145
|
+
/**
|
|
146
|
+
* Grants da conta, exceto os espelhos dos grants das `personalAgents.extensions` (os access
|
|
147
|
+
* tokens de MCP que elas emitem pelo oidc-provider usam o id do grant delas) — aparecem e são
|
|
148
|
+
* revogados como personal agent, não como app OIDC.
|
|
149
|
+
*/
|
|
146
150
|
async #accountGrants(accountId) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
return (await this.#listModelOfAccount('Grant', accountId)).filter((r) => !r.id.startsWith('pgrant_'));
|
|
151
|
+
const prefixes = (this.#oidc.config.personalAgents?.extensions ?? []).map((e) => e.grantIdPrefix);
|
|
152
|
+
return (await this.#listModelOfAccount('Grant', accountId)).filter((r) => !prefixes.some((prefix) => r.id.startsWith(prefix)));
|
|
150
153
|
}
|
|
151
154
|
/**
|
|
152
155
|
* Conta TODAS as sessões ativas do IdP (todas as contas). 0 quando o adapter não
|
|
@@ -17,6 +17,19 @@ export declare const clientInputValidator: import("@vinejs/vine").VineValidator<
|
|
|
17
17
|
backchannelLogoutSessionRequired: import("@vinejs/vine/schema/base/literal").OptionalModifier<import("@vinejs/vine").VineBoolean>;
|
|
18
18
|
clientName: import("@vinejs/vine/schema/base/literal").OptionalModifier<import("@vinejs/vine/schema/base/literal").NullableModifier<import("@vinejs/vine").VineString>>;
|
|
19
19
|
logoUri: import("@vinejs/vine/schema/base/literal").OptionalModifier<import("@vinejs/vine/schema/base/literal").NullableModifier<import("@vinejs/vine").VineString>>;
|
|
20
|
+
serviceTenant: import("@vinejs/vine").OptionalModifier<import("@vinejs/vine").NullableModifier<import("@vinejs/vine").VineObject<{
|
|
21
|
+
partition: import("@vinejs/vine").VineString;
|
|
22
|
+
capabilities: import("@vinejs/vine").OptionalModifier<import("@vinejs/vine").VineArray<import("@vinejs/vine").VineString>>;
|
|
23
|
+
}, {
|
|
24
|
+
capabilities?: string[] | null | undefined;
|
|
25
|
+
partition: string;
|
|
26
|
+
}, {
|
|
27
|
+
capabilities?: string[] | undefined;
|
|
28
|
+
partition: string;
|
|
29
|
+
}, {
|
|
30
|
+
capabilities?: string[] | undefined;
|
|
31
|
+
partition: string;
|
|
32
|
+
}>>>;
|
|
20
33
|
}, {
|
|
21
34
|
applicationType?: "native" | "web" | null | undefined;
|
|
22
35
|
backchannelLogoutSessionRequired?: string | number | boolean | null | undefined;
|
|
@@ -27,6 +40,10 @@ export declare const clientInputValidator: import("@vinejs/vine").VineValidator<
|
|
|
27
40
|
logoUri?: string | null | undefined;
|
|
28
41
|
postLogoutRedirectUris?: string[] | null | undefined;
|
|
29
42
|
redirectUris?: string[] | null | undefined;
|
|
43
|
+
serviceTenant?: {
|
|
44
|
+
capabilities?: string[] | null | undefined;
|
|
45
|
+
partition: string;
|
|
46
|
+
} | null | undefined;
|
|
30
47
|
tokenEndpointAuthMethod?: "client_secret_basic" | "client_secret_post" | "none" | null | undefined;
|
|
31
48
|
}, {
|
|
32
49
|
applicationType?: "native" | "web" | undefined;
|
|
@@ -38,6 +55,10 @@ export declare const clientInputValidator: import("@vinejs/vine").VineValidator<
|
|
|
38
55
|
logoUri?: string | null | undefined;
|
|
39
56
|
postLogoutRedirectUris?: string[] | undefined;
|
|
40
57
|
redirectUris?: string[] | undefined;
|
|
58
|
+
serviceTenant?: {
|
|
59
|
+
capabilities?: string[] | undefined;
|
|
60
|
+
partition: string;
|
|
61
|
+
} | null | undefined;
|
|
41
62
|
tokenEndpointAuthMethod?: "client_secret_basic" | "client_secret_post" | "none" | undefined;
|
|
42
63
|
}, {
|
|
43
64
|
applicationType?: "native" | "web" | undefined;
|
|
@@ -49,6 +70,10 @@ export declare const clientInputValidator: import("@vinejs/vine").VineValidator<
|
|
|
49
70
|
logoUri?: string | null | undefined;
|
|
50
71
|
postLogoutRedirectUris?: string[] | undefined;
|
|
51
72
|
redirectUris?: string[] | undefined;
|
|
73
|
+
serviceTenant?: {
|
|
74
|
+
capabilities?: string[] | undefined;
|
|
75
|
+
partition: string;
|
|
76
|
+
} | null | undefined;
|
|
52
77
|
tokenEndpointAuthMethod?: "client_secret_basic" | "client_secret_post" | "none" | undefined;
|
|
53
78
|
}>, Record<string, any> | undefined>;
|
|
54
79
|
/** Forma validada de um client (saída do {@link clientInputValidator}). */
|
|
@@ -63,6 +88,10 @@ export type ClientInputFields = {
|
|
|
63
88
|
backchannelLogoutSessionRequired?: boolean;
|
|
64
89
|
clientName?: string | null;
|
|
65
90
|
logoUri?: string | null;
|
|
91
|
+
serviceTenant?: {
|
|
92
|
+
partition: string;
|
|
93
|
+
capabilities?: string[];
|
|
94
|
+
} | null;
|
|
66
95
|
};
|
|
67
96
|
/**
|
|
68
97
|
* Mapeia o input validado para um {@link ClientInput} COMPLETO (create), com defaults.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import vine from '@vinejs/vine';
|
|
2
|
+
import { CAPABILITY_PATTERN, MAX_CAPABILITIES, PARTITION_PATTERN, } from '../service_tenants/client_metadata.js';
|
|
2
3
|
import { APPLICATION_TYPES, redirectUriProblem } from './client_metadata.js';
|
|
3
4
|
import { normalizeEmailIdentifier } from './email_identifier.js';
|
|
4
5
|
/**
|
|
@@ -103,6 +104,18 @@ export const clientInputValidator = vine.compile(vine.object({
|
|
|
103
104
|
// `null` (update) removes them.
|
|
104
105
|
clientName: vine.string().trim().maxLength(200).nullable().optional(),
|
|
105
106
|
logoUri: vine.string().trim().url(URL_OPTS).nullable().optional(),
|
|
107
|
+
// Service tenant (`client_credentials` + partition claim). `null` (update) turns it
|
|
108
|
+
// back into a regular client.
|
|
109
|
+
serviceTenant: vine
|
|
110
|
+
.object({
|
|
111
|
+
partition: vine.string().trim().regex(PARTITION_PATTERN),
|
|
112
|
+
capabilities: vine
|
|
113
|
+
.array(vine.string().trim().regex(CAPABILITY_PATTERN))
|
|
114
|
+
.maxLength(MAX_CAPABILITIES)
|
|
115
|
+
.optional(),
|
|
116
|
+
})
|
|
117
|
+
.nullable()
|
|
118
|
+
.optional(),
|
|
106
119
|
}));
|
|
107
120
|
/**
|
|
108
121
|
* Mapeia o input validado para um {@link ClientInput} COMPLETO (create), com defaults.
|
|
@@ -111,6 +124,21 @@ export const clientInputValidator = vine.compile(vine.object({
|
|
|
111
124
|
*/
|
|
112
125
|
export function clientCreateInput(v) {
|
|
113
126
|
const applicationType = v.applicationType ?? 'web';
|
|
127
|
+
// A service tenant only uses client_credentials: that is its default grant.
|
|
128
|
+
if (v.serviceTenant) {
|
|
129
|
+
return {
|
|
130
|
+
clientId: v.clientId?.trim() || undefined,
|
|
131
|
+
applicationType,
|
|
132
|
+
redirectUris: v.redirectUris ?? [],
|
|
133
|
+
postLogoutRedirectUris: v.postLogoutRedirectUris ?? [],
|
|
134
|
+
grantTypes: v.grantTypes ?? ['client_credentials'],
|
|
135
|
+
tokenEndpointAuthMethod: v.tokenEndpointAuthMethod ?? 'client_secret_basic',
|
|
136
|
+
backchannelLogoutUri: v.backchannelLogoutUri || undefined,
|
|
137
|
+
clientName: v.clientName || undefined,
|
|
138
|
+
logoUri: v.logoUri || undefined,
|
|
139
|
+
serviceTenant: v.serviceTenant,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
114
142
|
return {
|
|
115
143
|
clientId: v.clientId?.trim() || undefined,
|
|
116
144
|
applicationType,
|
|
@@ -148,6 +176,8 @@ export function clientPartialInput(v) {
|
|
|
148
176
|
out.clientName = v.clientName?.trim() || null;
|
|
149
177
|
if (v.logoUri !== undefined)
|
|
150
178
|
out.logoUri = v.logoUri?.trim() || null;
|
|
179
|
+
if (v.serviceTenant !== undefined)
|
|
180
|
+
out.serviceTenant = v.serviceTenant;
|
|
151
181
|
return out;
|
|
152
182
|
}
|
|
153
183
|
// ─── Usuários ───────────────────────────────────────────────────────────────
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { HttpContext } from '@adonisjs/core/http';
|
|
2
|
+
import type { AccountConsoleConfig } from '../account/runtime.js';
|
|
3
|
+
import type { ResolvedPersonalAgentExtension } from '../agents/extension.js';
|
|
2
4
|
import type { AuthSocialConfig, ResolvedRateLimitConfig } from '../define_config.js';
|
|
3
5
|
import type { PolicyRouteOption } from './config_locks.js';
|
|
4
6
|
import type { AuthHostOptions } from './register_auth_host.js';
|
|
@@ -35,16 +37,21 @@ export interface AuthHostRuntimeConfig {
|
|
|
35
37
|
* Ver `deriveLockedRouteOptions`.
|
|
36
38
|
*/
|
|
37
39
|
lockedRouteOptions?: PolicyRouteOption[];
|
|
38
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* Personal agents ligados → prefixo das rotas do PACT (`/agents`) e as
|
|
42
|
+
* `personalAgents.extensions` resolvidas (rotas, isenções de CSRF, navegador do agente).
|
|
43
|
+
*/
|
|
39
44
|
personalAgents?: {
|
|
40
45
|
prefix: string;
|
|
41
|
-
|
|
46
|
+
extensions?: readonly ResolvedPersonalAgentExtension[];
|
|
42
47
|
};
|
|
43
48
|
/**
|
|
44
49
|
* `accountSession.acceptIdpSession: false` com `authMethods: { password: false }`:
|
|
45
50
|
* o console só entra por um `accountLoginUrl` do host (`registerAuthHost` recusa sem ele).
|
|
46
51
|
*/
|
|
47
52
|
consoleNeedsLoginUrl?: boolean;
|
|
53
|
+
/** `accountConsole` do config: os overrides das páginas de extension. */
|
|
54
|
+
accountConsole?: AccountConsoleConfig;
|
|
48
55
|
/**
|
|
49
56
|
* API headless (Clerk-style). Presente quando o host a declarou — o provider o
|
|
50
57
|
* stash no boot para `registerAuthHost` montar as rotas sem reler o config inteiro.
|