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

Function removeClient

packages/core/sdk/src/oauth-service.ts:1165–1241  ·  view source on GitHub ↗
(
    owner: Owner,
    slug: OAuthClientSlug,
  )

Source from the content-addressed store, hash-verified

1163 // op never cascades into connections).
1164 // -----------------------------------------------------------------------
1165 const removeClient = (
1166 owner: Owner,
1167 slug: OAuthClientSlug,
1168 ): Effect.Effect<void, OrgWriteDeniedError | StorageFailure> =>
1169 Effect.gen(function* () {
1170 // Config-declared apps have no row to remove; removing one is an env
1171 // change on the host, not a storage operation. Fail loudly rather than
1172 // returning a success that changed nothing.
1173 if (isFirstPartyOAuthClientSlug(String(slug))) {
1174 return yield* new StorageError({
1175 message: `OAuth client "${String(slug)}" is a first-party app declared in host config; it cannot be removed through this surface.`,
1176 cause: undefined,
1177 });
1178 }
1179 yield* deps.guardOrgWrite(owner);
1180 // "Is there an app at (owner, slug) right now?" — asked twice, for two
1181 // different reasons. Before the delete it says whether this call removes
1182 // anything at all; after the commit it says whether the secret key still
1183 // belongs to the app this call removed.
1184 const findClientRow = deps.fuma.use("oauth_client.findFirst", (db) =>
1185 looseDb(db).findFirst("oauth_client", {
1186 where: (b: any) => b.and(b("owner", "=", owner), b("slug", "=", String(slug))),
1187 }),
1188 );
1189
1190 const removedRow = yield* deps.fuma.transaction(
1191 Effect.gen(function* () {
1192 const existing = yield* findClientRow;
1193 yield* deps.fuma
1194 .use("oauth_client.delete", (db) =>
1195 looseDb(db).deleteMany("oauth_client", {
1196 where: (b: any) => b.and(b("owner", "=", owner), b("slug", "=", String(slug))),
1197 }),
1198 )
1199 .pipe(Effect.asVoid);
1200 return existing;
1201 }),
1202 );
1203 // Nothing matched, so this call removed nothing and owns no secret. The
1204 // idempotent no-op and the cross-subject miss both land here, and both
1205 // used to queue a delete of a key they never had a claim on.
1206 if (!removedRow) return;
1207 // Best-effort: drop the secret from the provider so it isn't orphaned.
1208 //
1209 // Deferred to the outermost commit. This function opens no transaction of
1210 // its own, but a caller can wrap it in one — and `provider.delete` reaches
1211 // a store that does not roll back with it. An abort would then restore the
1212 // client row while its secret stayed destroyed, leaving a client that
1213 // looks configured and can never authenticate again. Orphaning a secret is
1214 // recoverable; deleting one that is still referenced is not, so the
1215 // deletion waits until the row's removal is durable. With no transaction
1216 // active `afterCommit` runs it immediately, which is the behaviour this
1217 // path already had.
1218 const provider = deps.defaultWritableProvider();
1219 const dropSecret = provider?.delete;
1220 if (provider && dropSecret) {
1221 yield* afterCommit(
1222 Effect.gen(function* () {

Callers

nothing calls this directly

Calls 5

afterCommitFunction · 0.90
looseDbFunction · 0.85
callMethod · 0.80
transactionMethod · 0.65

Tested by

no test coverage detected