@executor-js/sdk 1.5.17 → 1.5.19
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/dist/{chunk-4XPVLX62.js → chunk-BBLMWFXN.js} +23 -5
- package/dist/chunk-BBLMWFXN.js.map +1 -0
- package/dist/{chunk-5Q35SELN.js → chunk-MYRPYTSV.js} +137 -14
- package/dist/chunk-MYRPYTSV.js.map +1 -0
- package/dist/client.d.ts +13 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js.map +1 -1
- package/dist/core-tools.d.ts +3 -0
- package/dist/core-tools.d.ts.map +1 -1
- package/dist/core.js +2 -2
- package/dist/executor.d.ts +6 -0
- package/dist/executor.d.ts.map +1 -1
- package/dist/host-internal.d.ts +1 -1
- package/dist/host-internal.d.ts.map +1 -1
- package/dist/host-internal.js +61 -6
- package/dist/host-internal.js.map +1 -1
- package/dist/hosted-http-client.d.ts +7 -0
- package/dist/hosted-http-client.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/oauth-helpers.d.ts +3 -0
- package/dist/oauth-helpers.d.ts.map +1 -1
- package/dist/oauth-service.d.ts +1 -0
- package/dist/oauth-service.d.ts.map +1 -1
- package/dist/plugin.d.ts +1 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/testing.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-4XPVLX62.js.map +0 -1
- package/dist/chunk-5Q35SELN.js.map +0 -1
|
@@ -181,10 +181,13 @@ var asFromTokenUrlAndIssuer = (tokenUrl, issuerUrl, options = {}) => {
|
|
|
181
181
|
id_token_signing_alg_values_supported: [...options.idTokenSigningAlgValuesSupported]
|
|
182
182
|
} : withIssuer;
|
|
183
183
|
};
|
|
184
|
-
var oauth4webapiRequestOptions = (targetUrl, timeoutMs, endpointUrlPolicy = {}) => {
|
|
184
|
+
var oauth4webapiRequestOptions = (targetUrl, timeoutMs, endpointUrlPolicy = {}, customFetch2) => {
|
|
185
185
|
const options = {
|
|
186
186
|
signal: AbortSignal.timeout(timeoutMs ?? OAUTH2_DEFAULT_TIMEOUT_MS)
|
|
187
187
|
};
|
|
188
|
+
if (customFetch2) {
|
|
189
|
+
options[oauth.customFetch] = customFetch2;
|
|
190
|
+
}
|
|
188
191
|
if (isLoopbackHttpUrl(targetUrl) || URL.canParse(targetUrl) && new URL(targetUrl).protocol === "http:" && endpointUrlPolicy.allowHttp === true) {
|
|
189
192
|
options[oauth.allowInsecureRequests] = true;
|
|
190
193
|
}
|
|
@@ -244,7 +247,12 @@ var exchangeAuthorizationCode = (input) => Effect.tryPromise({
|
|
|
244
247
|
clientAuth,
|
|
245
248
|
"authorization_code",
|
|
246
249
|
params,
|
|
247
|
-
oauth4webapiRequestOptions(
|
|
250
|
+
oauth4webapiRequestOptions(
|
|
251
|
+
input.tokenUrl,
|
|
252
|
+
input.timeoutMs,
|
|
253
|
+
input.endpointUrlPolicy,
|
|
254
|
+
input.fetch
|
|
255
|
+
)
|
|
248
256
|
);
|
|
249
257
|
return await processTokenEndpointResponse(as, client, response);
|
|
250
258
|
},
|
|
@@ -270,7 +278,12 @@ var exchangeClientCredentials = (input) => Effect.tryPromise({
|
|
|
270
278
|
client,
|
|
271
279
|
clientAuth,
|
|
272
280
|
params,
|
|
273
|
-
oauth4webapiRequestOptions(
|
|
281
|
+
oauth4webapiRequestOptions(
|
|
282
|
+
input.tokenUrl,
|
|
283
|
+
input.timeoutMs,
|
|
284
|
+
input.endpointUrlPolicy,
|
|
285
|
+
input.fetch
|
|
286
|
+
)
|
|
274
287
|
);
|
|
275
288
|
const result = await oauth.processClientCredentialsResponse(as, client, response);
|
|
276
289
|
return tokenResponseFrom(result);
|
|
@@ -302,7 +315,12 @@ var refreshAccessToken = (input) => Effect.tryPromise({
|
|
|
302
315
|
clientAuth,
|
|
303
316
|
input.refreshToken,
|
|
304
317
|
{
|
|
305
|
-
...oauth4webapiRequestOptions(
|
|
318
|
+
...oauth4webapiRequestOptions(
|
|
319
|
+
input.tokenUrl,
|
|
320
|
+
input.timeoutMs,
|
|
321
|
+
input.endpointUrlPolicy,
|
|
322
|
+
input.fetch
|
|
323
|
+
),
|
|
306
324
|
additionalParameters
|
|
307
325
|
}
|
|
308
326
|
);
|
|
@@ -336,4 +354,4 @@ export {
|
|
|
336
354
|
refreshAccessToken,
|
|
337
355
|
shouldRefreshToken
|
|
338
356
|
};
|
|
339
|
-
//# sourceMappingURL=chunk-
|
|
357
|
+
//# sourceMappingURL=chunk-BBLMWFXN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/oauth-helpers.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// OAuth 2.0 helpers — generic, isomorphic building blocks.\n//\n// Thin wrappers around `oauth4webapi` (stateless; pure Web Crypto +\n// `fetch`, no deps; runs unchanged in Node, CF Workers, and browsers).\n// Each public helper is a single `Effect.tryPromise` call that delegates\n// the RFC work to the library and normalises the failure surface into\n// `OAuth2Error`.\n//\n// What stays hand-rolled:\n// - `OAuth2Error` — our tagged error; we want a stable shape across\n// every token-endpoint call\n// - `shouldRefreshToken` — skew check, trivial\n// - `buildAuthorizationUrl` — the library doesn't expose a raw\n// authorization-URL builder (it prefers PAR); a 30-line manual\n// construction keeps the call sync and lets callers opt out of PAR\n// ---------------------------------------------------------------------------\n\nimport { Data, Effect, Predicate } from \"effect\";\nimport * as oauth from \"oauth4webapi\";\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nexport class OAuth2Error extends Data.TaggedError(\"OAuth2Error\")<{\n readonly message: string;\n /**\n * RFC 6749 §5.2 error code, when the token endpoint returned one\n * (`invalid_grant`, `invalid_client`, `unauthorized_client`, ...).\n * Callers use this to distinguish terminal failures (a refresh token\n * the AS no longer honours → re-auth required) from transient ones.\n */\n readonly error?: string;\n readonly cause?: unknown;\n}> {}\n\n// ---------------------------------------------------------------------------\n// Token response shape (RFC 6749 §5.1)\n// ---------------------------------------------------------------------------\n\nexport type OAuth2TokenResponse = {\n readonly access_token: string;\n readonly token_type?: string;\n readonly refresh_token?: string;\n readonly expires_in?: number;\n readonly scope?: string;\n};\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Refresh tokens this many ms before expiry to avoid mid-request expiration. */\nexport const OAUTH2_REFRESH_SKEW_MS = 60_000;\n\n/** Default token-endpoint timeout. */\nexport const OAUTH2_DEFAULT_TIMEOUT_MS = 20_000;\n\nexport interface OAuthEndpointUrlPolicy {\n readonly allowHttp?: boolean;\n}\n\nconst isLoopbackHttpUrl = (value: string): boolean => {\n if (!URL.canParse(value)) return false;\n const url = new URL(value);\n if (url.protocol !== \"http:\") return false;\n const hostname = url.hostname.toLowerCase();\n return (\n hostname === \"localhost\" ||\n hostname === \"0.0.0.0\" ||\n hostname === \"::1\" ||\n hostname === \"[::1]\" ||\n hostname.startsWith(\"127.\")\n );\n};\n\nexport const isSupportedOAuthEndpointUrl = (\n value: string,\n policy: OAuthEndpointUrlPolicy = {},\n): boolean => {\n if (!URL.canParse(value)) return false;\n const url = new URL(value);\n return (\n url.protocol === \"https:\" ||\n isLoopbackHttpUrl(value) ||\n (url.protocol === \"http:\" && policy.allowHttp === true)\n );\n};\n\nexport const assertSupportedOAuthEndpointUrl = (\n value: string,\n label = \"OAuth endpoint URL\",\n policy: OAuthEndpointUrlPolicy = {},\n): string => {\n if (isSupportedOAuthEndpointUrl(value, policy)) return value;\n // oxlint-disable-next-line executor/no-try-catch-or-throw, executor/no-error-constructor -- boundary: synchronous assertion helper used by URL constructors and Effect.try wrappers\n throw new TypeError(`${label} must use https: or loopback http:`);\n};\n\n// ---------------------------------------------------------------------------\n// PKCE (RFC 7636) — straight delegation to `oauth4webapi`\n// ---------------------------------------------------------------------------\n\nexport const createPkceCodeVerifier = (): string => oauth.generateRandomCodeVerifier();\n\nexport const createPkceCodeChallenge = (verifier: string): Promise<string> =>\n oauth.calculatePKCECodeChallenge(verifier);\n\n/** RFC 6749 `state` — an unguessable correlation token minted by `oauth.start`\n * and redeemed by `oauth.complete`. */\nexport const createOAuthState = (): string => oauth.generateRandomState();\n\n// ---------------------------------------------------------------------------\n// Authorization URL builder\n// ---------------------------------------------------------------------------\n\nexport type BuildAuthorizationUrlInput = {\n readonly authorizationUrl: string;\n readonly clientId: string;\n readonly redirectUrl: string;\n readonly scopes: readonly string[];\n readonly state: string;\n /** Pre-computed base64url S256 challenge (from `createPkceCodeChallenge`). */\n readonly codeChallenge: string;\n /** Separator between scopes. RFC 6749 says space; some providers use comma. */\n readonly scopeSeparator?: string;\n /** RFC 8707 Resource Indicator. MCP Authorization 2025-06-18 §\"Resource\n * Parameter Implementation\" requires clients to send this on every\n * authorization request, regardless of AS support. */\n readonly resource?: string;\n /** Provider-specific extras (e.g. Google's `access_type=offline`). */\n readonly extraParams?: Readonly<Record<string, string>>;\n readonly endpointUrlPolicy?: OAuthEndpointUrlPolicy;\n};\n\n/** Build an RFC 6749 §4.1.1 authorization URL. Sync; pre-computed\n * challenge lets this stay out of the Promise world. */\nexport const buildAuthorizationUrl = (input: BuildAuthorizationUrlInput): string => {\n const url = new URL(\n assertSupportedOAuthEndpointUrl(\n input.authorizationUrl,\n \"Authorization URL\",\n input.endpointUrlPolicy,\n ),\n );\n // Benign default kept by design: a single space is the RFC 6749 scope\n // separator. Callers targeting a legacy comma-separated provider pass\n // `scopeSeparator` explicitly (see the field's JSDoc).\n const separator = input.scopeSeparator ?? \" \";\n url.searchParams.set(\"client_id\", input.clientId);\n url.searchParams.set(\"redirect_uri\", input.redirectUrl);\n url.searchParams.set(\"response_type\", \"code\");\n if (input.scopes.length > 0) {\n url.searchParams.set(\"scope\", input.scopes.join(separator));\n }\n url.searchParams.set(\"state\", input.state);\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n url.searchParams.set(\"code_challenge\", input.codeChallenge);\n if (input.resource) {\n url.searchParams.set(\"resource\", input.resource);\n }\n if (input.extraParams) {\n for (const [k, v] of Object.entries(input.extraParams)) {\n url.searchParams.set(k, v);\n }\n }\n return url.toString();\n};\n\n/** Provider-specific authorize-URL extras that are NOT RFC 6749 params, so the\n * generic flow must add them per-provider (keyed off the authorization host).\n *\n * Google: `access_type=offline` + `prompt=consent` are required to receive (and\n * keep receiving, across reconnects / scope changes) a REFRESH TOKEN — without\n * them Google issues an access-token-only grant that dies in ~1h and a\n * re-consent can silently keep the old scope set. Do not add\n * `include_granted_scopes=true` here: with historical grants on the same Google\n * consent app, Google folds those unrelated scopes into the new consent flow and\n * can fail inside accounts.google.com before returning to our callback. */\nexport const providerAuthorizeExtras = (\n authorizationUrl: string,\n): Readonly<Record<string, string>> => {\n // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: URL() throws on invalid input → no provider extras\n try {\n const host = new URL(authorizationUrl).host.toLowerCase();\n if (host === \"accounts.google.com\") {\n return { access_type: \"offline\", prompt: \"consent\" };\n }\n } catch {\n // Unparseable authorization URL — let buildAuthorizationUrl surface the error.\n }\n return {};\n};\n\n// ---------------------------------------------------------------------------\n// Regional token-endpoint rebind\n//\n// Some authorization servers publish a single static metadata document that\n// advertises one region's token endpoint, but issue authorization codes that\n// are only redeemable at the *regional* host the user's org actually lives on.\n// The region comes back on the callback as a non-standard `domain` (or `site`)\n// query param: Datadog returns `domain=us5.datadoghq.com` while its metadata\n// statically advertises `app.datadoghq.com`. Redeeming the code at the\n// advertised host then fails with `invalid_grant`.\n//\n// `rebindTokenEndpointHostToCallbackDomain` swaps ONLY the hostname of the\n// configured token URL to the callback-supplied host, and ONLY when that host\n// is a sibling subdomain of the configured one (same parent after stripping the\n// leftmost DNS label, e.g. `app.datadoghq.com` and `us5.datadoghq.com` both\n// reduce to `datadoghq.com`). The token request carries the client secret, the\n// code, and the PKCE verifier, so an attacker-influenced `domain` must never be\n// able to point it at an arbitrary origin. Anything that fails the sibling\n// check, fails to parse, or isn't https falls back to the configured URL\n// unchanged.\n// ---------------------------------------------------------------------------\n\nconst hostnameFromCallbackDomain = (callbackDomain: string): string | undefined => {\n const trimmed = callbackDomain.trim();\n if (trimmed.length === 0) return undefined;\n // Datadog sends `domain` as a bare host and `site` as a full origin; accept\n // either by tolerating an optional scheme, then taking only the hostname.\n const candidate = trimmed.includes(\"://\") ? trimmed : `https://${trimmed}`;\n if (!URL.canParse(candidate)) return undefined;\n const url = new URL(candidate);\n // A legitimate regional host carries no port, credentials, or path.\n if (url.port !== \"\" || url.username !== \"\" || url.password !== \"\") return undefined;\n if (url.pathname !== \"/\" && url.pathname !== \"\") return undefined;\n return url.hostname.toLowerCase();\n};\n\n/** Parent domain after stripping the leftmost DNS label, or `undefined` when\n * the host has no sibling space (a single label, or a parent that is a bare\n * TLD). `app.datadoghq.com` -> `datadoghq.com`; `foo.com` -> undefined. */\nconst siblingParentDomainOf = (hostname: string): string | undefined => {\n const labels = hostname.split(\".\");\n if (labels.length < 3) return undefined;\n const parent = labels.slice(1).join(\".\");\n // Require the parent to itself be multi-label so a 2-label configured host\n // can never rebind across an entire TLD (e.g. foo.com -> bar.com).\n return parent.includes(\".\") ? parent : undefined;\n};\n\nexport const rebindTokenEndpointHostToCallbackDomain = (\n configuredTokenUrl: string,\n callbackDomain: string | null | undefined,\n): string => {\n if (!callbackDomain) return configuredTokenUrl;\n if (!URL.canParse(configuredTokenUrl)) return configuredTokenUrl;\n const configured = new URL(configuredTokenUrl);\n if (configured.protocol !== \"https:\") return configuredTokenUrl;\n const targetHost = hostnameFromCallbackDomain(callbackDomain);\n if (!targetHost) return configuredTokenUrl;\n const configuredHost = configured.hostname.toLowerCase();\n if (targetHost === configuredHost) return configuredTokenUrl;\n const configuredParent = siblingParentDomainOf(configuredHost);\n const targetParent = siblingParentDomainOf(targetHost);\n if (!configuredParent || !targetParent || configuredParent !== targetParent) {\n return configuredTokenUrl;\n }\n const rebound = new URL(configuredTokenUrl);\n rebound.hostname = targetHost;\n return rebound.toString();\n};\n\n// ---------------------------------------------------------------------------\n// Error mapping — `oauth4webapi`'s `process*Response` failure shapes are\n// either a WWW-Authenticate challenge or an RFC 6749 §5.2 error body,\n// both exposed via `.error` / `.error_description`. Probing the envelope\n// preserves RFC 6749 error-code semantics (e.g., mapping `invalid_grant`\n// to reauth-required) across wrappers.\n// ---------------------------------------------------------------------------\n\nconst isOAuth2Error = Predicate.isTagged(\"OAuth2Error\") as (cause: unknown) => cause is OAuth2Error;\n\nconst responseFromOAuthErrorCause = (cause: unknown): Response | undefined => {\n if (cause instanceof Response) return cause;\n if (typeof cause !== \"object\" || cause === null) return undefined;\n const envelope = cause as {\n readonly cause?: unknown;\n readonly response?: unknown;\n };\n if (envelope.response instanceof Response) return envelope.response;\n if (envelope.cause instanceof Response) return envelope.cause;\n return undefined;\n};\n\nconst redactTokenEndpointBody = (body: string): string =>\n body\n .replaceAll(\n /(\"(?:access_token|refresh_token|id_token|client_secret)\"\\s*:\\s*\")[^\"]*(\")/gi,\n \"$1[redacted]$2\",\n )\n .replaceAll(\n /((?:access_token|refresh_token|id_token|client_secret|code)=)[^&\\s]*/gi,\n \"$1[redacted]\",\n );\n\nconst tokenEndpointHttpSummary = async (response: Response): Promise<string> => {\n const status = `HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : \"\"}`;\n const contentType = response.headers.get(\"content-type\");\n const url = response.url ? ` from ${response.url}` : \"\";\n const parts = [`${status}${url}`];\n if (contentType) parts.push(`content-type ${contentType}`);\n const preview = await bodyPreviewFromResponse(response);\n if (preview) parts.push(`body: ${preview}`);\n return parts.join(\"; \");\n};\n\nconst bodyPreviewFromResponse = async (response: Response): Promise<string | undefined> => {\n const text = await Promise.resolve()\n .then(() => response.clone().text())\n .then(\n (value) => value.trim(),\n () => \"\",\n );\n if (!text) return undefined;\n const redacted = redactTokenEndpointBody(text.replaceAll(/\\s+/g, \" \"));\n return redacted.length > 500 ? `${redacted.slice(0, 500)}...` : redacted;\n};\n\nconst toOAuth2Error = (cause: unknown): OAuth2Error => {\n if (isOAuth2Error(cause)) return cause;\n if (typeof cause === \"object\" && cause !== null) {\n const c = cause as {\n error?: unknown;\n error_description?: unknown;\n message?: unknown;\n };\n const code = typeof c.error === \"string\" ? c.error : undefined;\n const description =\n typeof c.error_description === \"string\"\n ? c.error_description\n : typeof c.message === \"string\"\n ? c.message\n : undefined;\n return new OAuth2Error({\n message: `OAuth token exchange failed: ${description ?? code ?? \"unknown error\"}`,\n error: code,\n cause,\n });\n }\n return new OAuth2Error({\n message: \"OAuth token exchange failed\",\n cause,\n });\n};\n\nconst toOAuth2ErrorWithHttpSummary = (cause: unknown): Effect.Effect<OAuth2Error> => {\n if (isOAuth2Error(cause)) return Effect.succeed(cause);\n const base = toOAuth2Error(cause);\n const response = responseFromOAuthErrorCause(cause);\n if (!response) return Effect.succeed(base);\n return Effect.promise(() => tokenEndpointHttpSummary(response)).pipe(\n Effect.map(\n (summary) =>\n new OAuth2Error({\n message: `${base.message} (${summary})`,\n error: base.error,\n cause,\n }),\n ),\n );\n};\n\nconst failOAuth2WithHttpSummary = (cause: unknown): Effect.Effect<never, OAuth2Error> =>\n toOAuth2ErrorWithHttpSummary(cause).pipe(Effect.flatMap((error) => Effect.fail(error)));\n\n// ---------------------------------------------------------------------------\n// oauth4webapi adapter helpers\n// ---------------------------------------------------------------------------\n\nexport type ClientAuthMethod = \"body\" | \"basic\";\n\n/**\n * The token-endpoint client-auth transport used when a caller doesn't specify\n * one. `\"body\"` is `client_secret_post` (the secret in the form body) — the\n * method our DCR registers (`token_endpoint_auth_method: client_secret_post`)\n * and the one every confidential client in the v2 model uses. EXPLICIT and\n * documented rather than a hidden inline `?? \"body\"`: callers that need\n * `client_secret_basic` pass `clientAuth: \"basic\"`. For PUBLIC clients (no\n * secret) the method is irrelevant — `pickClientAuth` returns `None()`.\n */\nexport const DEFAULT_CLIENT_AUTH_METHOD: ClientAuthMethod = \"body\";\n\nconst asFromTokenUrl = (\n tokenUrl: string,\n endpointUrlPolicy: OAuthEndpointUrlPolicy = {},\n): oauth.AuthorizationServer => {\n assertSupportedOAuthEndpointUrl(tokenUrl, \"Token URL\", endpointUrlPolicy);\n const url = new URL(tokenUrl);\n return {\n issuer: `${url.protocol}//${url.host}`,\n token_endpoint: tokenUrl,\n };\n};\n\nconst asFromTokenUrlAndIssuer = (\n tokenUrl: string,\n issuerUrl: string | null | undefined,\n options: {\n readonly idTokenSigningAlgValuesSupported?: readonly string[];\n readonly endpointUrlPolicy?: OAuthEndpointUrlPolicy;\n } = {},\n): oauth.AuthorizationServer => {\n const as = asFromTokenUrl(tokenUrl, options.endpointUrlPolicy);\n const withIssuer = issuerUrl ? { ...as, issuer: issuerUrl } : as;\n return options.idTokenSigningAlgValuesSupported\n ? {\n ...withIssuer,\n id_token_signing_alg_values_supported: [...options.idTokenSigningAlgValuesSupported],\n }\n : withIssuer;\n};\n\nconst oauth4webapiRequestOptions = (\n targetUrl: string,\n timeoutMs: number | undefined,\n endpointUrlPolicy: OAuthEndpointUrlPolicy = {},\n customFetch?: typeof globalThis.fetch,\n): Record<string, unknown> => {\n const options: Record<string, unknown> = {\n signal: AbortSignal.timeout(timeoutMs ?? OAUTH2_DEFAULT_TIMEOUT_MS),\n };\n if (customFetch) {\n (options as { [oauth.customFetch]?: typeof globalThis.fetch })[oauth.customFetch] = customFetch;\n }\n if (\n isLoopbackHttpUrl(targetUrl) ||\n (URL.canParse(targetUrl) &&\n new URL(targetUrl).protocol === \"http:\" &&\n endpointUrlPolicy.allowHttp === true)\n ) {\n (options as { [oauth.allowInsecureRequests]?: boolean })[oauth.allowInsecureRequests] = true;\n }\n return options;\n};\n\n// Select the token-endpoint client authentication. The secret's presence is the\n// EXPLICIT public-vs-confidential discriminator in the v2 model: a registered\n// client either has a secret (confidential — authenticate it) or has none\n// (public PKCE — `None()`, RFC 7636). This is not a silent guess: `loadClient`\n// persists a non-empty secret for confidential clients and null/\"\" for public\n// ones, so an absent secret here unambiguously means \"public client\". The\n// `method` only chooses HOW a present secret is sent (post vs basic).\nconst pickClientAuth = (\n clientSecret: string | null | undefined,\n method: ClientAuthMethod,\n): oauth.ClientAuth => {\n if (!clientSecret) return oauth.None();\n return method === \"basic\"\n ? oauth.ClientSecretBasic(clientSecret)\n : oauth.ClientSecretPost(clientSecret);\n};\n\nconst tokenResponseFrom = (r: oauth.TokenEndpointResponse): OAuth2TokenResponse => ({\n access_token: r.access_token,\n token_type: r.token_type,\n refresh_token: r.refresh_token,\n expires_in: typeof r.expires_in === \"number\" ? r.expires_in : undefined,\n scope: r.scope,\n});\n\n// MCP source connections are pure OAuth 2.0 — we never request `openid` and\n// never consume `id_token`. Some providers (PostHog, etc.) front an OIDC\n// backend and emit an `id_token` anyway; oauth4webapi then strict-validates\n// its claims against the AS metadata and rejects mismatches we don't care\n// about. Strip the field before delegation.\nconst stripIdToken = async (response: Response): Promise<Response> => {\n const body = await response\n .clone()\n .json()\n .then(\n (value: unknown) => value,\n () => null,\n );\n if (!body || typeof body !== \"object\" || !(\"id_token\" in (body as Record<string, unknown>))) {\n return response;\n }\n const { id_token: _ignored, ...rest } = body as Record<string, unknown>;\n return new Response(JSON.stringify(rest), {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n });\n};\n\nconst processTokenEndpointResponse = async (\n as: oauth.AuthorizationServer,\n client: oauth.Client,\n response: Response,\n): Promise<OAuth2TokenResponse> =>\n tokenResponseFrom(\n await oauth.processGenericTokenEndpointResponse(as, client, await stripIdToken(response)),\n );\n\n// ---------------------------------------------------------------------------\n// Exchange authorization code → tokens\n// ---------------------------------------------------------------------------\n\nexport type ExchangeAuthorizationCodeInput = {\n readonly tokenUrl: string;\n readonly issuerUrl?: string | null;\n readonly clientId: string;\n readonly clientSecret?: string | null;\n readonly redirectUrl: string;\n readonly codeVerifier: string;\n readonly code: string;\n readonly clientAuth?: ClientAuthMethod;\n readonly idTokenSigningAlgValuesSupported?: readonly string[];\n /** RFC 8707 Resource Indicator. MCP Auth spec MUST-requires this on\n * the token request when the client knows the resource it intends\n * to call. */\n readonly resource?: string;\n readonly timeoutMs?: number;\n readonly endpointUrlPolicy?: OAuthEndpointUrlPolicy;\n readonly fetch?: typeof globalThis.fetch;\n};\n\nexport const exchangeAuthorizationCode = (\n input: ExchangeAuthorizationCodeInput,\n): Effect.Effect<OAuth2TokenResponse, OAuth2Error> =>\n Effect.tryPromise({\n try: async () => {\n const as = asFromTokenUrlAndIssuer(input.tokenUrl, input.issuerUrl, {\n idTokenSigningAlgValuesSupported: input.idTokenSigningAlgValuesSupported,\n endpointUrlPolicy: input.endpointUrlPolicy,\n });\n const client: oauth.Client = { client_id: input.clientId };\n const clientAuth = pickClientAuth(\n input.clientSecret,\n input.clientAuth ?? DEFAULT_CLIENT_AUTH_METHOD,\n );\n // `authorizationCodeGrantRequest` requires its `callbackParameters`\n // to have been returned from `validateAuthResponse`. Our public API\n // takes the `code` directly (the UI already validated `state` by\n // looking up the session), so skip the library's state-validation\n // rail and go through the generic grant request instead.\n const params = new URLSearchParams({\n code: input.code,\n redirect_uri: input.redirectUrl,\n code_verifier: input.codeVerifier,\n });\n if (input.resource) {\n params.set(\"resource\", input.resource);\n }\n const response = await oauth.genericTokenEndpointRequest(\n as,\n client,\n clientAuth,\n \"authorization_code\",\n params,\n oauth4webapiRequestOptions(\n input.tokenUrl,\n input.timeoutMs,\n input.endpointUrlPolicy,\n input.fetch,\n ),\n );\n return await processTokenEndpointResponse(as, client, response);\n },\n catch: (cause) => cause,\n }).pipe(Effect.catch(failOAuth2WithHttpSummary));\n\n// ---------------------------------------------------------------------------\n// Exchange client credentials → tokens (RFC 6749 §4.4)\n// ---------------------------------------------------------------------------\n\nexport type ExchangeClientCredentialsInput = {\n readonly tokenUrl: string;\n readonly clientId: string;\n readonly clientSecret: string;\n readonly scopes?: readonly string[];\n readonly scopeSeparator?: string;\n readonly clientAuth?: ClientAuthMethod;\n /** RFC 8707 Resource Indicator. MCP Authorization 2025-06-18 requires this\n * on token requests when the client knows the protected resource. */\n readonly resource?: string;\n readonly timeoutMs?: number;\n readonly endpointUrlPolicy?: OAuthEndpointUrlPolicy;\n readonly fetch?: typeof globalThis.fetch;\n};\n\nexport const exchangeClientCredentials = (\n input: ExchangeClientCredentialsInput,\n): Effect.Effect<OAuth2TokenResponse, OAuth2Error> =>\n Effect.tryPromise({\n try: async () => {\n const as = asFromTokenUrl(input.tokenUrl, input.endpointUrlPolicy);\n const client: oauth.Client = { client_id: input.clientId };\n const clientAuth = pickClientAuth(\n input.clientSecret,\n input.clientAuth ?? DEFAULT_CLIENT_AUTH_METHOD,\n );\n const params = new URLSearchParams();\n if (input.scopes && input.scopes.length > 0) {\n params.set(\"scope\", input.scopes.join(input.scopeSeparator ?? \" \"));\n }\n if (input.resource) {\n params.set(\"resource\", input.resource);\n }\n const response = await oauth.clientCredentialsGrantRequest(\n as,\n client,\n clientAuth,\n params,\n oauth4webapiRequestOptions(\n input.tokenUrl,\n input.timeoutMs,\n input.endpointUrlPolicy,\n input.fetch,\n ),\n );\n const result = await oauth.processClientCredentialsResponse(as, client, response);\n return tokenResponseFrom(result);\n },\n catch: (cause) => cause,\n }).pipe(Effect.catch(failOAuth2WithHttpSummary));\n\n// ---------------------------------------------------------------------------\n// Refresh access token\n// ---------------------------------------------------------------------------\n\nexport type RefreshAccessTokenInput = {\n readonly tokenUrl: string;\n readonly issuerUrl?: string | null;\n readonly clientId: string;\n readonly clientSecret?: string | null;\n readonly refreshToken: string;\n readonly scopes?: readonly string[];\n readonly scopeSeparator?: string;\n readonly clientAuth?: ClientAuthMethod;\n readonly idTokenSigningAlgValuesSupported?: readonly string[];\n /** RFC 8707 Resource Indicator — MCP spec MUST-requires this on\n * refresh requests so the new access token's audience is bound to\n * the same resource. */\n readonly resource?: string;\n readonly timeoutMs?: number;\n readonly endpointUrlPolicy?: OAuthEndpointUrlPolicy;\n readonly fetch?: typeof globalThis.fetch;\n};\n\nexport const refreshAccessToken = (\n input: RefreshAccessTokenInput,\n): Effect.Effect<OAuth2TokenResponse, OAuth2Error> =>\n Effect.tryPromise({\n try: async () => {\n const as = asFromTokenUrlAndIssuer(input.tokenUrl, input.issuerUrl, {\n idTokenSigningAlgValuesSupported: input.idTokenSigningAlgValuesSupported,\n endpointUrlPolicy: input.endpointUrlPolicy,\n });\n const client: oauth.Client = { client_id: input.clientId };\n const clientAuth = pickClientAuth(\n input.clientSecret,\n input.clientAuth ?? DEFAULT_CLIENT_AUTH_METHOD,\n );\n const extraParams = new URLSearchParams();\n if (input.scopes && input.scopes.length > 0) {\n extraParams.set(\"scope\", input.scopes.join(input.scopeSeparator ?? \" \"));\n }\n if (input.resource) {\n extraParams.set(\"resource\", input.resource);\n }\n const additionalParameters =\n Array.from(extraParams.keys()).length > 0 ? extraParams : undefined;\n const response = await oauth.refreshTokenGrantRequest(\n as,\n client,\n clientAuth,\n input.refreshToken,\n {\n ...oauth4webapiRequestOptions(\n input.tokenUrl,\n input.timeoutMs,\n input.endpointUrlPolicy,\n input.fetch,\n ),\n additionalParameters,\n },\n );\n const result = await oauth.processRefreshTokenResponse(\n as,\n client,\n await stripIdToken(response),\n );\n return tokenResponseFrom(result);\n },\n catch: (cause) => cause,\n }).pipe(Effect.catch(failOAuth2WithHttpSummary));\n\n// ---------------------------------------------------------------------------\n// Refresh-needed predicate\n// ---------------------------------------------------------------------------\n\nexport const shouldRefreshToken = (input: {\n readonly expiresAt: number | null;\n readonly now?: number;\n readonly skewMs?: number;\n}): boolean => {\n if (input.expiresAt === null) return false;\n const now = input.now ?? Date.now();\n const skew = input.skewMs ?? OAUTH2_REFRESH_SKEW_MS;\n return input.expiresAt <= now + skew;\n};\n"],"mappings":"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","names":["customFetch"]}
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
rebindTokenEndpointHostToCallbackDomain,
|
|
39
39
|
refreshAccessToken,
|
|
40
40
|
shouldRefreshToken
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-BBLMWFXN.js";
|
|
42
42
|
import {
|
|
43
43
|
AuthTemplateSlug,
|
|
44
44
|
ConnectionAddress,
|
|
@@ -3167,7 +3167,14 @@ var ConnectionCreateInput = Schema2.Struct({
|
|
|
3167
3167
|
}).check(
|
|
3168
3168
|
Schema2.makeFilter((payload) => {
|
|
3169
3169
|
const originCount = (payload.from === void 0 ? 0 : 1) + (payload.inputs === void 0 ? 0 : 1);
|
|
3170
|
-
|
|
3170
|
+
const isNoAuth = String(payload.template) === String(NO_AUTH_TEMPLATE);
|
|
3171
|
+
if (isNoAuth) {
|
|
3172
|
+
if (originCount > 0) {
|
|
3173
|
+
return 'A no-auth connection (template "none") takes no provider credential origin';
|
|
3174
|
+
}
|
|
3175
|
+
} else if (originCount !== 1) {
|
|
3176
|
+
return "Expected exactly one provider credential origin";
|
|
3177
|
+
}
|
|
3171
3178
|
if (payload.inputs !== void 0 && Object.keys(payload.inputs).length === 0) {
|
|
3172
3179
|
return "Expected at least one provider credential input";
|
|
3173
3180
|
}
|
|
@@ -3251,9 +3258,23 @@ var OAuthCreateClientInput = Schema2.Struct({
|
|
|
3251
3258
|
tokenUrl: Schema2.String,
|
|
3252
3259
|
grant: OAuthGrantSchema,
|
|
3253
3260
|
clientId: Schema2.String,
|
|
3254
|
-
clientSecret: Schema2.String,
|
|
3255
3261
|
resource: Schema2.optional(Schema2.NullOr(Schema2.String))
|
|
3256
3262
|
});
|
|
3263
|
+
var OAuthCreateClientHandoffInput = Schema2.Struct({
|
|
3264
|
+
integration: Schema2.String,
|
|
3265
|
+
owner: Schema2.optional(OwnerSchema),
|
|
3266
|
+
slug: Schema2.optional(Schema2.String),
|
|
3267
|
+
grant: Schema2.optional(OAuthGrantSchema),
|
|
3268
|
+
clientId: Schema2.optional(Schema2.String),
|
|
3269
|
+
authorizationUrl: Schema2.optional(Schema2.String),
|
|
3270
|
+
tokenUrl: Schema2.optional(Schema2.String),
|
|
3271
|
+
resource: Schema2.optional(Schema2.NullOr(Schema2.String)),
|
|
3272
|
+
label: Schema2.optional(Schema2.String)
|
|
3273
|
+
});
|
|
3274
|
+
var OAuthCreateClientHandoffOutput = Schema2.Struct({
|
|
3275
|
+
url: Schema2.String,
|
|
3276
|
+
instructions: Schema2.String
|
|
3277
|
+
});
|
|
3257
3278
|
var OAuthClientOutputRef = Schema2.Struct({
|
|
3258
3279
|
client: Schema2.String
|
|
3259
3280
|
});
|
|
@@ -3332,6 +3353,8 @@ var PolicyUpdateInputStd = schemaToStandard(PolicyUpdateInput);
|
|
|
3332
3353
|
var PolicyRemoveInputStd = schemaToStandard(PolicyRemoveInput);
|
|
3333
3354
|
var OAuthClientsListOutputStd = schemaToStandard(OAuthClientsListOutput);
|
|
3334
3355
|
var OAuthCreateClientInputStd = schemaToStandard(OAuthCreateClientInput);
|
|
3356
|
+
var OAuthCreateClientHandoffInputStd = schemaToStandard(OAuthCreateClientHandoffInput);
|
|
3357
|
+
var OAuthCreateClientHandoffOutputStd = schemaToStandard(OAuthCreateClientHandoffOutput);
|
|
3335
3358
|
var OAuthClientOutputRefStd = schemaToStandard(OAuthClientOutputRef);
|
|
3336
3359
|
var OAuthRegisterDynamicInputStd = schemaToStandard(OAuthRegisterDynamicInput);
|
|
3337
3360
|
var OAuthRemoveClientInputStd = schemaToStandard(OAuthRemoveClientInput);
|
|
@@ -3417,12 +3440,28 @@ var createConnectionInputFromTool = (input) => {
|
|
|
3417
3440
|
)
|
|
3418
3441
|
};
|
|
3419
3442
|
};
|
|
3420
|
-
var connectionCreateHandoffUrl = (webBaseUrl, input) => {
|
|
3443
|
+
var connectionCreateHandoffUrl = (webBaseUrl, orgSlug, input) => {
|
|
3421
3444
|
const search = new URLSearchParams({ addAccount: "1" });
|
|
3422
3445
|
if (input.owner !== void 0) search.set("owner", input.owner);
|
|
3423
3446
|
if (input.template !== void 0) search.set("template", input.template);
|
|
3424
3447
|
if (input.label !== void 0) search.set("label", input.label);
|
|
3425
|
-
const
|
|
3448
|
+
const orgPrefix = orgSlug !== void 0 && orgSlug.length > 0 ? `/${orgSlug}` : "";
|
|
3449
|
+
const path = `${orgPrefix}/integrations/${encodeURIComponent(input.integration)}?${search.toString()}`;
|
|
3450
|
+
if (webBaseUrl === void 0 || webBaseUrl.length === 0) return path;
|
|
3451
|
+
return new URL(path, webBaseUrl.endsWith("/") ? webBaseUrl : `${webBaseUrl}/`).toString();
|
|
3452
|
+
};
|
|
3453
|
+
var oauthClientCreateHandoffUrl = (webBaseUrl, orgSlug, input) => {
|
|
3454
|
+
const search = new URLSearchParams({ addAccount: "1", oauthClient: "1" });
|
|
3455
|
+
if (input.owner !== void 0) search.set("owner", input.owner);
|
|
3456
|
+
if (input.slug !== void 0) search.set("clientSlug", input.slug);
|
|
3457
|
+
if (input.grant !== void 0) search.set("grant", input.grant);
|
|
3458
|
+
if (input.clientId !== void 0) search.set("clientId", input.clientId);
|
|
3459
|
+
if (input.authorizationUrl !== void 0) search.set("authorizationUrl", input.authorizationUrl);
|
|
3460
|
+
if (input.tokenUrl !== void 0) search.set("tokenUrl", input.tokenUrl);
|
|
3461
|
+
if (input.resource != null && input.resource.length > 0) search.set("resource", input.resource);
|
|
3462
|
+
if (input.label !== void 0) search.set("label", input.label);
|
|
3463
|
+
const orgPrefix = orgSlug !== void 0 && orgSlug.length > 0 ? `/${orgSlug}` : "";
|
|
3464
|
+
const path = `${orgPrefix}/integrations/${encodeURIComponent(input.integration)}?${search.toString()}`;
|
|
3426
3465
|
if (webBaseUrl === void 0 || webBaseUrl.length === 0) return path;
|
|
3427
3466
|
return new URL(path, webBaseUrl.endsWith("/") ? webBaseUrl : `${webBaseUrl}/`).toString();
|
|
3428
3467
|
};
|
|
@@ -3485,9 +3524,16 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
|
|
|
3485
3524
|
}),
|
|
3486
3525
|
tool({
|
|
3487
3526
|
name: "connections.create",
|
|
3488
|
-
description:
|
|
3527
|
+
description: 'Low-level create or replace for a saved connection from provider item references. For a no-auth integration (public MCP server, public REST API), pass `template: "none"` with no `from`/`inputs` to wire it up directly. For normal API keys/tokens, use `connections.createHandoff` so the user enters the credential in the web UI. OAuth credentials should use `oauth.start`.',
|
|
3489
3528
|
inputSchema: ConnectionCreateInputStd,
|
|
3490
3529
|
outputSchema: ConnectionOutputStd,
|
|
3530
|
+
// Creating a connection binds a credential reference and roots a new
|
|
3531
|
+
// tool catalog: every tool that connection produces then becomes
|
|
3532
|
+
// callable. Even the no-auth (`template: "none"`) path pulls tools
|
|
3533
|
+
// from an arbitrary endpoint. Prompt-injected code could silently
|
|
3534
|
+
// wire an attacker-chosen integration or credential, so this is
|
|
3535
|
+
// approval-gated (the v1 `sources.configure` carried the same guard).
|
|
3536
|
+
annotations: { requiresApproval: true },
|
|
3491
3537
|
execute: (input, { ctx }) => Effect5.map(
|
|
3492
3538
|
ctx.connections.create(createConnectionInputFromTool(input)),
|
|
3493
3539
|
connectionToOutput
|
|
@@ -3499,7 +3545,7 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
|
|
|
3499
3545
|
inputSchema: ConnectionCreateHandoffInputStd,
|
|
3500
3546
|
outputSchema: ConnectionCreateHandoffOutputStd,
|
|
3501
3547
|
execute: (input) => {
|
|
3502
|
-
const url = connectionCreateHandoffUrl(options.webBaseUrl, input);
|
|
3548
|
+
const url = connectionCreateHandoffUrl(options.webBaseUrl, options.orgSlug, input);
|
|
3503
3549
|
return Effect5.succeed({
|
|
3504
3550
|
url,
|
|
3505
3551
|
instructions: "Ask the user to open this URL and add the account in the Executor web UI. Do not ask them to paste the credential value into chat. After they finish, call connections.list for the integration to discover the created connection."
|
|
@@ -3511,6 +3557,10 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
|
|
|
3511
3557
|
description: "Remove a saved connection and its produced tools by owner, integration, and connection name.",
|
|
3512
3558
|
inputSchema: ConnectionRefInputStd,
|
|
3513
3559
|
outputSchema: RemovedOutputStd,
|
|
3560
|
+
// Deleting a connection drops it and every tool it produced, which
|
|
3561
|
+
// prompt-injected code could use to disrupt an integration or force a
|
|
3562
|
+
// re-add flow. Approval-gated, matching v1 `sources.remove`.
|
|
3563
|
+
annotations: { requiresApproval: true },
|
|
3514
3564
|
execute: (input, { ctx }) => Effect5.map(ctx.connections.remove(connectionRefFromInput(input)), () => ({
|
|
3515
3565
|
removed: true
|
|
3516
3566
|
}))
|
|
@@ -3520,6 +3570,11 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
|
|
|
3520
3570
|
description: "Re-run an integration's tool production for a saved connection, replacing that connection's persisted tools.",
|
|
3521
3571
|
inputSchema: ConnectionRefInputStd,
|
|
3522
3572
|
outputSchema: ConnectionsRefreshOutputStd,
|
|
3573
|
+
// Refresh replaces a connection's persisted tool set; for a mutable
|
|
3574
|
+
// upstream (an MCP server whose catalog can change) this can swap in
|
|
3575
|
+
// different tools without confirmation. Approval-gated, matching v1
|
|
3576
|
+
// `sources.refresh`.
|
|
3577
|
+
annotations: { requiresApproval: true },
|
|
3523
3578
|
execute: (input, { ctx }) => Effect5.map(ctx.connections.refresh(connectionRefFromInput(input)), (tools) => ({
|
|
3524
3579
|
tools: tools.map(toolToOutput)
|
|
3525
3580
|
}))
|
|
@@ -3563,9 +3618,20 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
|
|
|
3563
3618
|
}),
|
|
3564
3619
|
tool({
|
|
3565
3620
|
name: "oauth.clients.create",
|
|
3566
|
-
description: "Register or replace an owner-scoped OAuth client
|
|
3621
|
+
description: "Register or replace an owner-scoped OAuth client WITHOUT a client secret: a PUBLIC client (PKCE / authorization_code) or a discovery-prefill placeholder. To register a CONFIDENTIAL client that has a secret, call `oauth.clients.createHandoff` instead so the human enters the secret in the web UI; never pass a client secret through this tool.",
|
|
3567
3622
|
inputSchema: OAuthCreateClientInputStd,
|
|
3568
3623
|
outputSchema: OAuthClientOutputRefStd,
|
|
3624
|
+
// This persists an OAuth client and REPLACES on slug collision. It
|
|
3625
|
+
// takes NO client secret: a secret would have to travel through the
|
|
3626
|
+
// agent's context window, so a confidential app is registered by the
|
|
3627
|
+
// human via `oauth.clients.createHandoff`. An empty secret registers a
|
|
3628
|
+
// PUBLIC client. The remaining risk is the write itself: prompt-injected
|
|
3629
|
+
// code could register a client with an attacker-controlled
|
|
3630
|
+
// authorizationUrl/tokenUrl, then drive `oauth.start` to mint a
|
|
3631
|
+
// connection and route the user's tokens to the attacker. The
|
|
3632
|
+
// highest-value gate here; matches v1 `sources.bindings.set`, which
|
|
3633
|
+
// guarded credential writes.
|
|
3634
|
+
annotations: { requiresApproval: true },
|
|
3569
3635
|
execute: (input, { ctx }) => Effect5.map(
|
|
3570
3636
|
ctx.oauth.createClient({
|
|
3571
3637
|
owner: input.owner,
|
|
@@ -3574,17 +3640,40 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
|
|
|
3574
3640
|
tokenUrl: input.tokenUrl,
|
|
3575
3641
|
grant: input.grant,
|
|
3576
3642
|
clientId: input.clientId,
|
|
3577
|
-
|
|
3643
|
+
// No secret crosses the agent boundary; an empty secret registers
|
|
3644
|
+
// a public client. Confidential clients go through
|
|
3645
|
+
// `oauth.clients.createHandoff`.
|
|
3646
|
+
clientSecret: "",
|
|
3578
3647
|
resource: input.resource ?? null
|
|
3579
3648
|
}),
|
|
3580
3649
|
(client) => ({ client: String(client) })
|
|
3581
3650
|
)
|
|
3582
3651
|
}),
|
|
3652
|
+
tool({
|
|
3653
|
+
name: "oauth.clients.createHandoff",
|
|
3654
|
+
description: "Return a browser URL that opens the Register-OAuth-app form for one integration, pre-filled with the non-secret fields (client id, endpoints, grant). Use this for any CONFIDENTIAL OAuth app: the user types the client secret directly in the web UI instead of sending it through the agent. After they register the app, call `oauth.clients.list` to discover its owner and slug, then `oauth.start`.",
|
|
3655
|
+
inputSchema: OAuthCreateClientHandoffInputStd,
|
|
3656
|
+
outputSchema: OAuthCreateClientHandoffOutputStd,
|
|
3657
|
+
// Pure URL builder: no DB write, no token, no secret. This is the SAFE
|
|
3658
|
+
// path (it routes the secret to the human in the browser), so it is
|
|
3659
|
+
// deliberately NOT approval-gated, mirroring `connections.createHandoff`.
|
|
3660
|
+
execute: (input) => {
|
|
3661
|
+
const url = oauthClientCreateHandoffUrl(options.webBaseUrl, options.orgSlug, input);
|
|
3662
|
+
return Effect5.succeed({
|
|
3663
|
+
url,
|
|
3664
|
+
instructions: "Ask the user to open this URL and register the OAuth app in the Executor web UI, entering the client secret there. Do not ask them to paste the client secret into chat. After they finish, call oauth.clients.list to find the registered client (owner + slug), then oauth.start."
|
|
3665
|
+
});
|
|
3666
|
+
}
|
|
3667
|
+
}),
|
|
3583
3668
|
tool({
|
|
3584
3669
|
name: "oauth.clients.registerDynamic",
|
|
3585
3670
|
description: "Register an OAuth client through RFC 7591 Dynamic Client Registration and save the minted client for later `oauth.start` calls.",
|
|
3586
3671
|
inputSchema: OAuthRegisterDynamicInputStd,
|
|
3587
3672
|
outputSchema: OAuthClientOutputRefStd,
|
|
3673
|
+
// Same risk class as `oauth.clients.create`: registers a client at a
|
|
3674
|
+
// caller-supplied endpoint and persists the minted credentials for
|
|
3675
|
+
// later `oauth.start` abuse. Approval-gated. See `oauth.clients.create`.
|
|
3676
|
+
annotations: { requiresApproval: true },
|
|
3588
3677
|
execute: (input, { ctx }) => Effect5.map(
|
|
3589
3678
|
ctx.oauth.registerDynamicClient({
|
|
3590
3679
|
owner: input.owner,
|
|
@@ -3607,6 +3696,11 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
|
|
|
3607
3696
|
description: "Remove an owner-scoped OAuth client by owner and slug. Existing connections are not cascaded.",
|
|
3608
3697
|
inputSchema: OAuthRemoveClientInputStd,
|
|
3609
3698
|
outputSchema: RemovedOutputStd,
|
|
3699
|
+
// Removing a client breaks token refresh for every connection that
|
|
3700
|
+
// depends on it (a silent DoS) and can force re-auth through an
|
|
3701
|
+
// attacker-supplied replacement. Approval-gated, matching v1
|
|
3702
|
+
// `sources.bindings.remove`.
|
|
3703
|
+
annotations: { requiresApproval: true },
|
|
3610
3704
|
execute: (input, { ctx }) => Effect5.map(
|
|
3611
3705
|
ctx.oauth.removeClient(input.owner, OAuthClientSlug.make(input.slug)),
|
|
3612
3706
|
() => ({ removed: true })
|
|
@@ -3631,6 +3725,14 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
|
|
|
3631
3725
|
description: "Start OAuth through a registered client to mint a connection for an integration. `client_credentials` clients return `connected`; authorization-code clients return an authorization URL and state.",
|
|
3632
3726
|
inputSchema: OAuthStartInputStd,
|
|
3633
3727
|
outputSchema: OAuthStartOutputStd,
|
|
3728
|
+
// This is the materialization step that turns a registered client
|
|
3729
|
+
// into a live connection. For `client_credentials` it completes
|
|
3730
|
+
// synchronously (status `connected`) with no browser step, so a
|
|
3731
|
+
// prompt-injected call against an attacker-registered client mints a
|
|
3732
|
+
// credentialed connection with no human in the loop. The
|
|
3733
|
+
// authorization-code path already returns a URL the user must visit,
|
|
3734
|
+
// but one gate on the whole tool covers the silent path cleanly.
|
|
3735
|
+
annotations: { requiresApproval: true },
|
|
3634
3736
|
execute: (input, { ctx }) => Effect5.map(
|
|
3635
3737
|
ctx.oauth.start({
|
|
3636
3738
|
client: OAuthClientSlug.make(input.client),
|
|
@@ -3678,6 +3780,11 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
|
|
|
3678
3780
|
description: "Create a tool policy. `pattern` matches a tool address tail (`integration.connection.tool`, `integration.*`, `*`); `action` is approve/require_approval/block. `owner` is org (workspace guardrail) or user (personal).",
|
|
3679
3781
|
inputSchema: PolicyCreateInputStd,
|
|
3680
3782
|
outputSchema: PolicyOutputStd,
|
|
3783
|
+
// A policy decides which tools run without confirmation, so creating
|
|
3784
|
+
// one can silence every other approval gate (e.g. `approve *`). It
|
|
3785
|
+
// must itself require approval, otherwise prompt-injected code could
|
|
3786
|
+
// disable approvals by writing its own bypass policy.
|
|
3787
|
+
annotations: { requiresApproval: true },
|
|
3681
3788
|
execute: (input, { ctx }) => Effect5.map(
|
|
3682
3789
|
ctx.core.policies.create({
|
|
3683
3790
|
owner: input.owner,
|
|
@@ -3698,6 +3805,10 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
|
|
|
3698
3805
|
description: "Update a tool policy's pattern and/or action by id + owner.",
|
|
3699
3806
|
inputSchema: PolicyUpdateInputStd,
|
|
3700
3807
|
outputSchema: PolicyOutputStd,
|
|
3808
|
+
// Editing a policy can broaden a pattern or flip an action to
|
|
3809
|
+
// `approve`, weakening an approval gate just as creation can, so it
|
|
3810
|
+
// requires approval too. See `policies.create`.
|
|
3811
|
+
annotations: { requiresApproval: true },
|
|
3701
3812
|
execute: (input, { ctx }) => Effect5.map(
|
|
3702
3813
|
ctx.core.policies.update({
|
|
3703
3814
|
id: input.id,
|
|
@@ -3719,6 +3830,10 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
|
|
|
3719
3830
|
description: "Remove a tool policy by id + owner.",
|
|
3720
3831
|
inputSchema: PolicyRemoveInputStd,
|
|
3721
3832
|
outputSchema: RemovedOutputStd,
|
|
3833
|
+
// Removing a policy can drop a `block` or `require_approval`
|
|
3834
|
+
// guardrail, so deletion is also approval-gated. See
|
|
3835
|
+
// `policies.create`.
|
|
3836
|
+
annotations: { requiresApproval: true },
|
|
3722
3837
|
execute: (input, { ctx }) => Effect5.map(
|
|
3723
3838
|
ctx.core.policies.remove({
|
|
3724
3839
|
id: input.id,
|
|
@@ -4161,6 +4276,7 @@ var validateClientEndpoints = (input, endpointUrlPolicy) => Effect7.gen(function
|
|
|
4161
4276
|
});
|
|
4162
4277
|
var makeOAuthService = (deps) => {
|
|
4163
4278
|
const httpClientLayer = deps.httpClientLayer ?? FetchHttpClient2.layer;
|
|
4279
|
+
const fetch = deps.fetch;
|
|
4164
4280
|
const redirectUri = deps.redirectUri;
|
|
4165
4281
|
const createClient = (input) => Effect7.gen(function* () {
|
|
4166
4282
|
yield* validateClientEndpoints(input, deps.endpointUrlPolicy);
|
|
@@ -4365,7 +4481,8 @@ var makeOAuthService = (deps) => {
|
|
|
4365
4481
|
clientSecret: client.clientSecret,
|
|
4366
4482
|
scopes: requestedScopes,
|
|
4367
4483
|
resource: client.resource ?? void 0,
|
|
4368
|
-
endpointUrlPolicy: deps.endpointUrlPolicy
|
|
4484
|
+
endpointUrlPolicy: deps.endpointUrlPolicy,
|
|
4485
|
+
fetch
|
|
4369
4486
|
}).pipe(
|
|
4370
4487
|
Effect7.mapError(
|
|
4371
4488
|
(cause) => new OAuthStartError({
|
|
@@ -4506,7 +4623,8 @@ var makeOAuthService = (deps) => {
|
|
|
4506
4623
|
codeVerifier: session.pkceVerifier,
|
|
4507
4624
|
code: input.code,
|
|
4508
4625
|
resource: client.resource ?? void 0,
|
|
4509
|
-
endpointUrlPolicy: deps.endpointUrlPolicy
|
|
4626
|
+
endpointUrlPolicy: deps.endpointUrlPolicy,
|
|
4627
|
+
fetch
|
|
4510
4628
|
}).pipe(
|
|
4511
4629
|
Effect7.mapError(
|
|
4512
4630
|
(cause) => new OAuthCompleteError({
|
|
@@ -5178,6 +5296,7 @@ var createExecutor = (config) => Effect8.gen(function* () {
|
|
|
5178
5296
|
const plugins = config.coreTools ? [
|
|
5179
5297
|
coreToolsPlugin({
|
|
5180
5298
|
webBaseUrl: config.coreTools.webBaseUrl,
|
|
5299
|
+
orgSlug: config.coreTools.orgSlug,
|
|
5181
5300
|
includeProviders: config.coreTools.includeProviders
|
|
5182
5301
|
}),
|
|
5183
5302
|
...userPlugins
|
|
@@ -5306,7 +5425,8 @@ var createExecutor = (config) => Effect8.gen(function* () {
|
|
|
5306
5425
|
clientSecret,
|
|
5307
5426
|
scopes: grantedScopes,
|
|
5308
5427
|
resource: clientRow.resource ? String(clientRow.resource) : void 0,
|
|
5309
|
-
endpointUrlPolicy: config.oauthEndpointUrlPolicy
|
|
5428
|
+
endpointUrlPolicy: config.oauthEndpointUrlPolicy,
|
|
5429
|
+
fetch: config.fetch
|
|
5310
5430
|
}).pipe(
|
|
5311
5431
|
// A client_credentials failure is never a rotated-refresh-token
|
|
5312
5432
|
// problem, so do NOT map invalid_grant → reauth. Surface as a
|
|
@@ -5336,7 +5456,8 @@ var createExecutor = (config) => Effect8.gen(function* () {
|
|
|
5336
5456
|
// RFC 8707: keep the re-minted token bound to the same resource
|
|
5337
5457
|
// (MCP servers require this on refresh).
|
|
5338
5458
|
resource: clientRow.resource ? String(clientRow.resource) : void 0,
|
|
5339
|
-
endpointUrlPolicy: config.oauthEndpointUrlPolicy
|
|
5459
|
+
endpointUrlPolicy: config.oauthEndpointUrlPolicy,
|
|
5460
|
+
fetch: config.fetch
|
|
5340
5461
|
}).pipe(
|
|
5341
5462
|
Effect8.mapError(
|
|
5342
5463
|
(cause) => cause.error === "invalid_grant" ? reauth(
|
|
@@ -5620,6 +5741,7 @@ var createExecutor = (config) => Effect8.gen(function* () {
|
|
|
5620
5741
|
const result = yield* runtime.plugin.resolveTools({
|
|
5621
5742
|
integration: rowToIntegration(integrationRow),
|
|
5622
5743
|
config: decodeJsonColumn(integrationRow.config),
|
|
5744
|
+
httpClientLayer: runtime.ctx.httpClientLayer,
|
|
5623
5745
|
connection: ref,
|
|
5624
5746
|
template: existingRow ? AuthTemplateSlug.make(existingRow.template) : null,
|
|
5625
5747
|
storage: runtime.storage,
|
|
@@ -6516,6 +6638,7 @@ ${approvalArgumentPreview(args)}`,
|
|
|
6516
6638
|
})
|
|
6517
6639
|
),
|
|
6518
6640
|
httpClientLayer: config.httpClientLayer,
|
|
6641
|
+
fetch: config.fetch,
|
|
6519
6642
|
endpointUrlPolicy: config.oauthEndpointUrlPolicy,
|
|
6520
6643
|
// EXPLICIT — no localhost default. When a caller omits `redirectUri` the
|
|
6521
6644
|
// OAuth service receives `null` and redirect-requiring flows fail loudly
|
|
@@ -6739,4 +6862,4 @@ export {
|
|
|
6739
6862
|
collectTables,
|
|
6740
6863
|
createExecutor
|
|
6741
6864
|
};
|
|
6742
|
-
//# sourceMappingURL=chunk-
|
|
6865
|
+
//# sourceMappingURL=chunk-MYRPYTSV.js.map
|