MCPcopy Create free account
hub / github.com/UsefulSoftwareCo/executor / start

Function start

packages/core/sdk/src/oauth-service.ts:1646–2074  ·  view source on GitHub ↗
(
    input: OAuthStartInput,
  )

Source from the content-addressed store, hash-verified

1644 // start — begin a flow through a client to mint a connection.
1645 // -----------------------------------------------------------------------
1646 const start = (
1647 input: OAuthStartInput,
1648 ): Effect.Effect<ConnectResult, OAuthStartError | OrgWriteDeniedError | StorageFailure> =>
1649 Effect.gen(function* () {
1650 // Gate before any session row or upstream exchange: minting a Workspace
1651 // connection (including a reconnect that would replace its credential)
1652 // is a workspace-level change. Personal connections remain member-owned.
1653 yield* deps.guardOrgWrite(input.owner);
1654 const keys = yield* Effect.try({
1655 try: () => deps.ownedKeys(input.owner),
1656 catch: (cause) =>
1657 new StorageError({
1658 message: "Cannot start OAuth flow for owner without a subject",
1659 cause,
1660 }),
1661 });
1662 // Sharing is one-directional (org → members): a Workspace (org) connection
1663 // cannot be backed by a member's private (user) app. The connection owner
1664 // and the app owner are otherwise independent — a Personal connection
1665 // through a shared Workspace app is the supported cross-owner case.
1666 // First-party apps are deployment-owned, outside the owner lattice
1667 // entirely, so the rule does not apply to them.
1668 const firstPartyFlow = isFirstPartyOAuthClientSlug(String(input.client));
1669 yield* Effect.annotateCurrentSpan({
1670 "executor.oauth.client_first_party": firstPartyFlow,
1671 });
1672 if (!firstPartyFlow && input.owner === "org" && input.clientOwner === "user") {
1673 return yield* new OAuthStartError({
1674 message: "A Workspace connection must use a Workspace app.",
1675 });
1676 }
1677 // Load the app by its EXPLICIT owner (the caller knows it — no derivation).
1678 // The connection is still minted under `input.owner`. Storage visibility
1679 // policy hides apps the actor cannot see, so a wrong owner yields null.
1680 const client = yield* loadClient(input.clientOwner, input.client);
1681 if (!client) {
1682 return yield* new OAuthStartError({
1683 message: `OAuth client not found: ${input.client}`,
1684 });
1685 }
1686
1687 // Normalize the name the same way the mint stores it, so the free-name
1688 // guard below compares against the exact stored form.
1689 const requestedName = connectionIdentifier(String(input.name));
1690 // newConnection: resolve the requested name to a FREE one against the
1691 // stored rows (not a client-side, policy-filtered view), so a second
1692 // untyped connect mints `personalGmail2` instead of silently re-minting
1693 // the first account's row. Reconnects omit the flag and keep targeting
1694 // their existing row. Bounded: a pathological owner with 1000 same-named
1695 // connections fails loudly rather than scanning forever.
1696 let name = requestedName;
1697 if (input.newConnection === true) {
1698 let suffix = 2;
1699 while (
1700 yield* deps.connectionNameTaken({
1701 owner: input.owner,
1702 integration: input.integration,
1703 name,

Callers

nothing calls this directly

Calls 15

connectionIdentifierFunction · 0.90
createPkceCodeVerifierFunction · 0.90
createPkceCodeChallengeFunction · 0.90
createOAuthStateFunction · 0.90
encodeOAuthCallbackStateFunction · 0.90
buildAuthorizationUrlFunction · 0.90
providerAuthorizeExtrasFunction · 0.90
loadClientFunction · 0.85

Tested by

no test coverage detected