@bevel-software/platform-core-backend 0.12.1 → 0.13.3
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/THIRD-PARTY-NOTICES.md +5 -3
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +8 -1
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +58 -2
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +174 -33
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/admin-locked-commit.d.ts.map +1 -1
- package/dist/modules/access/admin-locked-commit.js +1 -0
- package/dist/modules/access/admin-locked-commit.js.map +1 -1
- package/dist/modules/access/synced-groups-committer.js +1 -1
- package/dist/modules/access/synced-groups-committer.js.map +1 -1
- package/dist/modules/access-model/access-errors.d.ts +11 -0
- package/dist/modules/access-model/access-errors.d.ts.map +1 -1
- package/dist/modules/access-model/access-errors.js +14 -0
- package/dist/modules/access-model/access-errors.js.map +1 -1
- package/dist/modules/access-model/access-grammar.d.ts +24 -8
- package/dist/modules/access-model/access-grammar.d.ts.map +1 -1
- package/dist/modules/access-model/access-grammar.js +123 -7
- package/dist/modules/access-model/access-grammar.js.map +1 -1
- package/dist/modules/declared-variables/declared-variables.routes.d.ts +42 -0
- package/dist/modules/declared-variables/declared-variables.routes.d.ts.map +1 -0
- package/dist/modules/declared-variables/declared-variables.routes.js +135 -0
- package/dist/modules/declared-variables/declared-variables.routes.js.map +1 -0
- package/dist/modules/declared-variables/index.d.ts +2 -0
- package/dist/modules/declared-variables/index.d.ts.map +1 -0
- package/dist/modules/declared-variables/index.js +2 -0
- package/dist/modules/declared-variables/index.js.map +1 -0
- package/dist/modules/diff/diff.routes.d.ts +1 -1
- package/dist/modules/diff/diff.routes.d.ts.map +1 -1
- package/dist/modules/diff/diff.routes.js +3 -3
- package/dist/modules/diff/diff.routes.js.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.d.ts +16 -0
- package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.js +20 -0
- package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
- package/dist/modules/kb-fs/repo-path.d.ts +32 -0
- package/dist/modules/kb-fs/repo-path.d.ts.map +1 -0
- package/dist/modules/kb-fs/repo-path.js +54 -0
- package/dist/modules/kb-fs/repo-path.js.map +1 -0
- package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts.map +1 -1
- package/dist/modules/secrets-vault/db-secrets-vault.service.js +60 -18
- package/dist/modules/secrets-vault/db-secrets-vault.service.js.map +1 -1
- package/dist/modules/secrets-vault/mcp-oauth-discovery.service.d.ts +20 -0
- package/dist/modules/secrets-vault/mcp-oauth-discovery.service.d.ts.map +1 -1
- package/dist/modules/secrets-vault/mcp-oauth-discovery.service.js +121 -47
- package/dist/modules/secrets-vault/mcp-oauth-discovery.service.js.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.js +20 -2
- package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
- package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-context.js +1 -0
- package/dist/modules/tool-helpers/tool-context.js.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.js +45 -12
- package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
- package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/mcp-server-edit.service.js +2 -1
- package/dist/modules/tool-manuals/mcp-server-edit.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +40 -8
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts +38 -14
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.js +164 -55
- package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.js +10 -5
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
- package/dist/modules/tool-manuals/utcp-cli-parse-only.d.ts +55 -0
- package/dist/modules/tool-manuals/utcp-cli-parse-only.d.ts.map +1 -0
- package/dist/modules/tool-manuals/utcp-cli-parse-only.js +76 -0
- package/dist/modules/tool-manuals/utcp-cli-parse-only.js.map +1 -0
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts +3 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.js +19 -2
- package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
- package/dist/modules/workflow/git/git.service.d.ts +32 -1
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +70 -5
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/git/pull-request.service.d.ts +3 -3
- package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/pull-request.service.js +20 -2
- package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
- package/dist/modules/workflow/pending-commits.worker.d.ts +8 -0
- package/dist/modules/workflow/pending-commits.worker.d.ts.map +1 -1
- package/dist/modules/workflow/pending-commits.worker.js +74 -16
- package/dist/modules/workflow/pending-commits.worker.js.map +1 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.js +7 -0
- package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
- package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.routes.js +12 -0
- package/dist/modules/workflow/workflow.routes.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts +1 -0
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +4 -0
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +31 -15
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- package/kb-template/AGENTS.md +52 -8
- package/package.json +4 -3
- package/src/core/create-core-server.ts +13 -1
- package/src/core/create-core-services.ts +1 -0
- package/src/modules/access/__tests__/access-control.atref-cache.test.ts +260 -0
- package/src/modules/access/__tests__/access-groups.test.ts +28 -0
- package/src/modules/access/__tests__/access-own-read-grant.test.ts +116 -0
- package/src/modules/access/access-control.service.ts +198 -37
- package/src/modules/access/admin-locked-commit.ts +1 -0
- package/src/modules/access/synced-groups-committer.ts +1 -1
- package/src/modules/access-model/__tests__/access-grammar.test.ts +199 -1
- package/src/modules/access-model/access-errors.ts +19 -0
- package/src/modules/access-model/access-grammar.ts +123 -6
- package/src/modules/declared-variables/__tests__/declared-variables.route.test.ts +166 -0
- package/src/modules/declared-variables/declared-variables.routes.ts +151 -0
- package/src/modules/declared-variables/index.ts +1 -0
- package/src/modules/diff/__tests__/diff.routes.rejectPathsLocked.test.ts +4 -4
- package/src/modules/diff/diff.routes.ts +3 -2
- package/src/modules/kb-fs/__tests__/locking-filesystem.test.ts +306 -131
- package/src/modules/kb-fs/__tests__/repo-path.test.ts +106 -0
- package/src/modules/kb-fs/locking-filesystem.ts +30 -0
- package/src/modules/kb-fs/repo-path.ts +56 -0
- package/src/modules/secrets-vault/__tests__/db-secrets-vault.oauth.test.ts +104 -0
- package/src/modules/secrets-vault/__tests__/mcp-oauth-discovery.service.test.ts +52 -0
- package/src/modules/secrets-vault/__tests__/tool-owner-gate.route.test.ts +48 -1
- package/src/modules/secrets-vault/db-secrets-vault.service.ts +73 -22
- package/src/modules/secrets-vault/mcp-oauth-discovery.service.ts +141 -50
- package/src/modules/secrets-vault/secrets-vault.routes.ts +600 -582
- package/src/modules/tool-helpers/tool-context.ts +1 -0
- package/src/modules/tool-manuals/__tests__/mcp-json-discovery.test.ts +38 -0
- package/src/modules/tool-manuals/__tests__/mcp-server-edit.service.test.ts +2 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.cli.test.ts +243 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +95 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +17 -1
- package/src/modules/tool-manuals/mcp-json-discovery.ts +39 -15
- package/src/modules/tool-manuals/mcp-server-edit.service.ts +2 -1
- package/src/modules/tool-manuals/tool-manuals.contract.ts +40 -9
- package/src/modules/tool-manuals/tool-manuals.service.ts +156 -28
- package/src/modules/tool-manuals/tool-manuals.tools.ts +10 -5
- package/src/modules/tool-manuals/utcp-cli-parse-only.ts +76 -0
- package/src/modules/workflow/__tests__/pending-commits.worker.test.ts +46 -0
- package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +60 -1
- package/src/modules/workflow/agent-tools/workflow.tools.ts +18 -1
- package/src/modules/workflow/git/__tests__/git.service.changedFilesForPr.test.ts +56 -0
- package/src/modules/workflow/git/__tests__/git.service.commitFile.strayPath.test.ts +162 -0
- package/src/modules/workflow/git/__tests__/pull-request.service.getPrDetail.test.ts +114 -0
- package/src/modules/workflow/git/git.service.ts +73 -6
- package/src/modules/workflow/git/pull-request.service.ts +22 -4
- package/src/modules/workflow/pending-commits.worker.ts +80 -18
- package/src/modules/workflow/review-workflow/__tests__/approval-states.test.ts +60 -0
- package/src/modules/workflow/review-workflow/review-workflow.service.ts +5 -0
- package/src/modules/workflow/workflow.routes.ts +12 -0
- package/src/modules/workflow/workflow.service.ts +5 -1
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +45 -0
- package/src/modules/workspace/workspace.tools.ts +35 -15
|
@@ -10,6 +10,22 @@ export type McpAuthDiscovery =
|
|
|
10
10
|
/** The server wants auth but the spec chain didn't complete — leave the tool as-is. */
|
|
11
11
|
| { status: 'unsupported'; reason: string };
|
|
12
12
|
|
|
13
|
+
/** Where a server's sign-in lives — the spec chain's answer before any client exists. */
|
|
14
|
+
interface AuthorizationServerInfo {
|
|
15
|
+
authorizationUrl: string;
|
|
16
|
+
tokenUrl: string;
|
|
17
|
+
/** RFC 7591 endpoint, when the AS offers dynamic registration. */
|
|
18
|
+
registrationUrl?: string;
|
|
19
|
+
/** RFC 8707 resource indicator: the PRM's `resource`, else the MCP URL itself. */
|
|
20
|
+
resource: string;
|
|
21
|
+
scopes?: string[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type AuthorizationServerResolution =
|
|
25
|
+
| { status: 'open' }
|
|
26
|
+
| { status: 'found'; as: AuthorizationServerInfo }
|
|
27
|
+
| { status: 'unsupported'; reason: string };
|
|
28
|
+
|
|
13
29
|
const FETCH_TIMEOUT_MS = 5_000;
|
|
14
30
|
/** Re-probe an open/unsupported server after this long (it may grow/lose auth). */
|
|
15
31
|
const NEGATIVE_TTL_MS = 5 * 60_000;
|
|
@@ -37,12 +53,21 @@ const OAUTH_TTL_MS = 60 * 60_000;
|
|
|
37
53
|
* row from the shared one, and the UTCP variable loader injects the fresh
|
|
38
54
|
* access token into the manual's `Authorization` header at call time.
|
|
39
55
|
*
|
|
56
|
+
* Providers without dynamic registration (HubSpot, Google) stop at step 3.
|
|
57
|
+
* For those, `providerForDeclaredClient` runs steps 1–3 for a client id the
|
|
58
|
+
* OWNER registered by hand and declared on the manual — same endpoints, same
|
|
59
|
+
* PKCE, same resource indicator, nothing persisted (the owner's client-secret
|
|
60
|
+
* save pins the completed provider).
|
|
61
|
+
*
|
|
40
62
|
* Every fetch is SSRF-guarded (https-only, no redirects, bounded) — these URLs
|
|
41
63
|
* originate from user-authored `.tool` files and remote servers' own metadata.
|
|
42
64
|
*/
|
|
43
65
|
export class McpOAuthDiscoveryService {
|
|
44
66
|
private readonly cache = new Map<string, { result: McpAuthDiscovery; expiresAt: number }>();
|
|
45
67
|
private readonly inflight = new Map<string, Promise<McpAuthDiscovery>>();
|
|
68
|
+
/** The metadata walk alone, per MCP URL — shared by every declared client on that server. */
|
|
69
|
+
private readonly asCache = new Map<string, { result: AuthorizationServerResolution; expiresAt: number }>();
|
|
70
|
+
private readonly asInflight = new Map<string, Promise<AuthorizationServerResolution>>();
|
|
46
71
|
|
|
47
72
|
constructor(
|
|
48
73
|
private readonly deps: {
|
|
@@ -75,6 +100,30 @@ export class McpOAuthDiscoveryService {
|
|
|
75
100
|
return pending;
|
|
76
101
|
}
|
|
77
102
|
|
|
103
|
+
/**
|
|
104
|
+
* The provider for a client the OWNER registered: the server's discovered
|
|
105
|
+
* endpoints + resource indicator around the declared `clientId`, PKCE on (the
|
|
106
|
+
* MCP spec requires it; a provider without it ignores the parameters). No
|
|
107
|
+
* registration call, no vault write — the metadata is cached per URL so a
|
|
108
|
+
* re-scan or a second declared client on the same server costs no network.
|
|
109
|
+
*/
|
|
110
|
+
async providerForDeclaredClient(manualName: string, mcpUrl: string, clientId: string): Promise<McpAuthDiscovery> {
|
|
111
|
+
const resolved = await this.resolveAuthorizationServer(manualName, mcpUrl);
|
|
112
|
+
if (resolved.status === 'open') {
|
|
113
|
+
return {
|
|
114
|
+
status: 'unsupported',
|
|
115
|
+
reason:
|
|
116
|
+
'the server did not ask for a sign-in and publishes no OAuth metadata — declare `authorizationUrl` and `tokenUrl` on the sign-in variable if it needs one',
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
if (resolved.status === 'unsupported') return resolved;
|
|
120
|
+
const { authorizationUrl, tokenUrl, resource, scopes } = resolved.as;
|
|
121
|
+
return {
|
|
122
|
+
status: 'oauth',
|
|
123
|
+
provider: { authorizationUrl, tokenUrl, clientId, scopes, pkce: true, resource },
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
78
127
|
private remember(key: string, result: McpAuthDiscovery): McpAuthDiscovery {
|
|
79
128
|
const ttl = result.status === 'oauth' ? OAUTH_TTL_MS : NEGATIVE_TTL_MS;
|
|
80
129
|
this.cache.set(key, { result, expiresAt: this.now() + ttl });
|
|
@@ -114,6 +163,89 @@ export class McpOAuthDiscoveryService {
|
|
|
114
163
|
}
|
|
115
164
|
|
|
116
165
|
private async discoverFresh(manualName: string, key: string, mcpUrl: string): Promise<McpAuthDiscovery> {
|
|
166
|
+
const resolved = await this.resolveAuthorizationServer(manualName, mcpUrl);
|
|
167
|
+
if (resolved.status !== 'found') return resolved;
|
|
168
|
+
const { authorizationUrl, tokenUrl, registrationUrl, resource, scopes } = resolved.as;
|
|
169
|
+
if (!registrationUrl) {
|
|
170
|
+
return {
|
|
171
|
+
status: 'unsupported',
|
|
172
|
+
reason:
|
|
173
|
+
'the server requires sign-in but does not allow automatic client registration — register an OAuth app ' +
|
|
174
|
+
`with the provider (redirect URI: ${this.deps.redirectUri}), declare its client id on a user-scoped ` +
|
|
175
|
+
'sign-in variable (the server editor on the tool\'s page, or the plugin.json extensions entry), then set its client secret',
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// 4. Dynamic client registration (RFC 7591), as a PUBLIC client: PKCE
|
|
180
|
+
// carries the proof, no secret to store or leak.
|
|
181
|
+
assertSafeFetchUrl(registrationUrl, { requireHttps: true, label: 'registration_endpoint' });
|
|
182
|
+
const regRes = await this.fetchRaw(registrationUrl, {
|
|
183
|
+
method: 'POST',
|
|
184
|
+
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
|
185
|
+
body: JSON.stringify({
|
|
186
|
+
client_name: 'Bevel Knowledge Base',
|
|
187
|
+
redirect_uris: [this.deps.redirectUri],
|
|
188
|
+
grant_types: ['authorization_code', 'refresh_token'],
|
|
189
|
+
response_types: ['code'],
|
|
190
|
+
token_endpoint_auth_method: 'none',
|
|
191
|
+
}),
|
|
192
|
+
});
|
|
193
|
+
if (!regRes.ok) {
|
|
194
|
+
return { status: 'unsupported', reason: `client registration failed: HTTP ${regRes.status}` };
|
|
195
|
+
}
|
|
196
|
+
const reg = (await regRes.json().catch(() => null)) as Record<string, unknown> | null;
|
|
197
|
+
const clientId = typeof reg?.client_id === 'string' ? reg.client_id : '';
|
|
198
|
+
if (!clientId) {
|
|
199
|
+
return { status: 'unsupported', reason: 'client registration returned no client_id' };
|
|
200
|
+
}
|
|
201
|
+
const clientSecret = typeof reg?.client_secret === 'string' && reg.client_secret ? reg.client_secret : undefined;
|
|
202
|
+
|
|
203
|
+
const provider: OAuthProviderConfig = {
|
|
204
|
+
authorizationUrl,
|
|
205
|
+
tokenUrl,
|
|
206
|
+
clientId,
|
|
207
|
+
clientSecret,
|
|
208
|
+
scopes,
|
|
209
|
+
pkce: true,
|
|
210
|
+
publicClient: !clientSecret,
|
|
211
|
+
resource,
|
|
212
|
+
};
|
|
213
|
+
await this.deps.secretsVault.putSharedOAuthProvider({
|
|
214
|
+
key,
|
|
215
|
+
label: `${manualName} sign-in`,
|
|
216
|
+
provider,
|
|
217
|
+
});
|
|
218
|
+
// Return the stored (public) shape — no secret rides in cache entries.
|
|
219
|
+
const publicProvider = { ...provider };
|
|
220
|
+
delete publicProvider.clientSecret;
|
|
221
|
+
return { status: 'oauth', provider: publicProvider };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** Steps 1–3, memoised per MCP URL (positive and negative alike, short TTL). */
|
|
225
|
+
private async resolveAuthorizationServer(manualName: string, mcpUrl: string): Promise<AuthorizationServerResolution> {
|
|
226
|
+
const cached = this.asCache.get(mcpUrl);
|
|
227
|
+
if (cached && cached.expiresAt > this.now()) return cached.result;
|
|
228
|
+
let pending = this.asInflight.get(mcpUrl);
|
|
229
|
+
if (!pending) {
|
|
230
|
+
pending = this.resolveAuthorizationServerFresh(mcpUrl)
|
|
231
|
+
.catch((err: unknown): AuthorizationServerResolution => ({
|
|
232
|
+
status: 'unsupported',
|
|
233
|
+
reason: err instanceof Error ? err.message : String(err),
|
|
234
|
+
}))
|
|
235
|
+
.then((result) => {
|
|
236
|
+
if (result.status === 'unsupported') {
|
|
237
|
+
console.warn(`[mcp-oauth-discovery] "${manualName}" (${mcpUrl}): ${result.reason}`);
|
|
238
|
+
}
|
|
239
|
+
this.asCache.set(mcpUrl, { result, expiresAt: this.now() + NEGATIVE_TTL_MS });
|
|
240
|
+
return result;
|
|
241
|
+
})
|
|
242
|
+
.finally(() => this.asInflight.delete(mcpUrl));
|
|
243
|
+
this.asInflight.set(mcpUrl, pending);
|
|
244
|
+
}
|
|
245
|
+
return pending;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private async resolveAuthorizationServerFresh(mcpUrl: string): Promise<AuthorizationServerResolution> {
|
|
117
249
|
// 1. Probe: an unauthenticated initialize. A 401/403, or ANY response
|
|
118
250
|
// carrying a `WWW-Authenticate` challenge, means the server wants OAuth;
|
|
119
251
|
// a redirect to a login page (fetch throws under `redirect:'error'`)
|
|
@@ -205,57 +337,16 @@ export class McpOAuthDiscoveryService {
|
|
|
205
337
|
if (!authorizationUrl || !tokenUrl) {
|
|
206
338
|
return { status: 'unsupported', reason: 'authorization-server metadata is missing endpoints' };
|
|
207
339
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
// carries the proof, no secret to store or leak.
|
|
218
|
-
assertSafeFetchUrl(registrationUrl, { requireHttps: true, label: 'registration_endpoint' });
|
|
219
|
-
const regRes = await this.fetchRaw(registrationUrl, {
|
|
220
|
-
method: 'POST',
|
|
221
|
-
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
|
222
|
-
body: JSON.stringify({
|
|
223
|
-
client_name: 'Bevel Knowledge Base',
|
|
224
|
-
redirect_uris: [this.deps.redirectUri],
|
|
225
|
-
grant_types: ['authorization_code', 'refresh_token'],
|
|
226
|
-
response_types: ['code'],
|
|
227
|
-
token_endpoint_auth_method: 'none',
|
|
228
|
-
}),
|
|
229
|
-
});
|
|
230
|
-
if (!regRes.ok) {
|
|
231
|
-
return { status: 'unsupported', reason: `client registration failed: HTTP ${regRes.status}` };
|
|
232
|
-
}
|
|
233
|
-
const reg = (await regRes.json().catch(() => null)) as Record<string, unknown> | null;
|
|
234
|
-
const clientId = typeof reg?.client_id === 'string' ? reg.client_id : '';
|
|
235
|
-
if (!clientId) {
|
|
236
|
-
return { status: 'unsupported', reason: 'client registration returned no client_id' };
|
|
237
|
-
}
|
|
238
|
-
const clientSecret = typeof reg?.client_secret === 'string' && reg.client_secret ? reg.client_secret : undefined;
|
|
239
|
-
|
|
240
|
-
const provider: OAuthProviderConfig = {
|
|
241
|
-
authorizationUrl,
|
|
242
|
-
tokenUrl,
|
|
243
|
-
clientId,
|
|
244
|
-
clientSecret,
|
|
245
|
-
scopes,
|
|
246
|
-
pkce: true,
|
|
247
|
-
publicClient: !clientSecret,
|
|
248
|
-
resource,
|
|
340
|
+
return {
|
|
341
|
+
status: 'found',
|
|
342
|
+
as: {
|
|
343
|
+
authorizationUrl,
|
|
344
|
+
tokenUrl,
|
|
345
|
+
...(registrationUrl ? { registrationUrl } : {}),
|
|
346
|
+
resource,
|
|
347
|
+
...(scopes ? { scopes } : {}),
|
|
348
|
+
},
|
|
249
349
|
};
|
|
250
|
-
await this.deps.secretsVault.putSharedOAuthProvider({
|
|
251
|
-
key,
|
|
252
|
-
label: `${manualName} sign-in`,
|
|
253
|
-
provider,
|
|
254
|
-
});
|
|
255
|
-
// Return the stored (public) shape — no secret rides in cache entries.
|
|
256
|
-
const publicProvider = { ...provider };
|
|
257
|
-
delete publicProvider.clientSecret;
|
|
258
|
-
return { status: 'oauth', provider: publicProvider };
|
|
259
350
|
}
|
|
260
351
|
|
|
261
352
|
private async fetchRaw(url: string, init: RequestInit): Promise<Response> {
|