History

Remove the @executor/auth-oauth2 package and its references throughout the project, including dependencies and related files, to streamline the authentication process.

Rhys Sullivan committed 7 months ago

Commit 4f7dbcc1 parent 05af42a12 files · +35 −295Browse files

Unified · Split

12 files
  1. bun.lock +0 −17
  2. packages/auth/oauth2/package.json +0 −22
  3. packages/auth/oauth2/tsconfig.json +0 −19
  4. packages/platform/sdk/package.json +0 −2
  5. packages/platform/sdk/src/runtime/auth/mcp-oauth.ts +0 −1
  6. packages/platform/sdk/src/runtime/auth/oauth-loopback.test.ts +0 −110
  7. packages/platform/sdk/src/runtime/auth/oauth-loopback.ts +0 −106
  8. packages/platform/sdk/src/runtime/auth/oauth2-pkce.ts +0 −1
  9. packages/sources/core/package.json +0 −1
  10. plugins/google-discovery/sdk/index.ts +6 −6
  11. plugins/google-discovery/sdk/oauth2.ts +29 −9
  12. plugins/google-discovery/sdk/package.json +0 −1
bun.lock+0−17
@@ -135,17 +135,6 @@ · 134 unmodified lines
135 "bun-types": "catalog:",135 "bun-types": "catalog:",
136 },136 },
137 },137 },
138 "packages/auth/oauth2": {
139 "name": "@executor/auth-oauth2",
140 "version": "0.0.0",
141 "dependencies": {
142 "effect": "catalog:",
143 },
144 "devDependencies": {
145 "@types/node": "catalog:",
146 "bun-types": "catalog:",
147 },
148 },
149 "packages/clients/react": {138 "packages/clients/react": {
150 "name": "@executor/react",139 "name": "@executor/react",
151 "version": "0.0.0",140 "version": "0.0.0",
@@ -351,8 +340,6 @@ · 199 unmodified lines
351 "dependencies": {340 "dependencies": {
352 "@effect/platform": "catalog:",341 "@effect/platform": "catalog:",
353 "@effect/platform-node": "catalog:",342 "@effect/platform-node": "catalog:",
354 "@executor/auth-mcp-oauth": "workspace:*",
355 "@executor/auth-oauth2": "workspace:*",
356 "@executor/codemode-core": "workspace:*",343 "@executor/codemode-core": "workspace:*",
357 "@executor/ir": "workspace:*",344 "@executor/ir": "workspace:*",
358 "@executor/runtime-deno-subprocess": "workspace:*",345 "@executor/runtime-deno-subprocess": "workspace:*",
@@ -436,7 +423,6 @@ · 77 unmodified lines
436 "name": "@executor/source-core",423 "name": "@executor/source-core",
437 "version": "0.0.0",424 "version": "0.0.0",
438 "dependencies": {425 "dependencies": {
439 "@executor/auth-oauth2": "workspace:*",
440 "@executor/codemode-core": "workspace:*",426 "@executor/codemode-core": "workspace:*",
441 "@executor/ir": "workspace:*",427 "@executor/ir": "workspace:*",
442 "@modelcontextprotocol/sdk": "^1.26.0",428 "@modelcontextprotocol/sdk": "^1.26.0",
@@ -482,7 +468,6 @@ · 39 unmodified lines
482 "version": "0.0.0",468 "version": "0.0.0",
483 "dependencies": {469 "dependencies": {
484 "@effect/platform": "catalog:",470 "@effect/platform": "catalog:",
485 "@executor/auth-oauth2": "workspace:*",
486 "@executor/codemode-core": "workspace:*",471 "@executor/codemode-core": "workspace:*",
487 "@executor/ir": "workspace:*",472 "@executor/ir": "workspace:*",
488 "@executor/platform-sdk": "workspace:*",473 "@executor/platform-sdk": "workspace:*",
@@ -900,8 +885,6 @@ · 411 unmodified lines
900 885
901 "@executor/auth-mcp-oauth": ["@executor/auth-mcp-oauth@workspace:packages/auth/mcp-oauth"],886 "@executor/auth-mcp-oauth": ["@executor/auth-mcp-oauth@workspace:packages/auth/mcp-oauth"],
902 887
903 "@executor/auth-oauth2": ["@executor/auth-oauth2@workspace:packages/auth/oauth2"],
904
905 "@executor/codemode-core": ["@executor/codemode-core@workspace:packages/kernel/core"],888 "@executor/codemode-core": ["@executor/codemode-core@workspace:packages/kernel/core"],
906 889
907 "@executor/docs": ["@executor/docs@workspace:apps/docs"],890 "@executor/docs": ["@executor/docs@workspace:apps/docs"],
packages/auth/oauth2/package.json+0−22
@@ -1,22 +0,0 @@
1{
2 "name": "@executor/auth-oauth2",
3 "private": true,
4 "type": "module",
5 "exports": {
6 ".": "./src/index.ts"
7 },
8 "files": [
9 "src"
10 ],
11 "scripts": {
12 "typecheck": "bunx tsc --noEmit -p tsconfig.json"
13 },
14 "dependencies": {
15 "effect": "catalog:"
16 },
17 "devDependencies": {
18 "@types/node": "catalog:",
19 "bun-types": "catalog:"
20 },
21 "version": "0.0.0"
22}
packages/auth/oauth2/tsconfig.json+0−19
@@ -1,19 +0,0 @@
1{
2 "compilerOptions": {
3 "target": "ES2022",
4 "module": "ESNext",
5 "moduleResolution": "Bundler",
6 "strict": true,
7 "skipLibCheck": true,
8 "noEmit": true,
9 "lib": ["ES2022"],
10 "types": ["bun-types", "node"]
11 },
12 "include": [
13 "src/**/*.ts",
14 "src/**/*.d.ts"
15 ],
16 "exclude": [
17 "src/**/*.test.ts"
18 ]
19}
packages/platform/sdk/package.json+0−2
@@ -27,8 +27,6 @@ · 26 unmodified lines
27 "dependencies": {27 "dependencies": {
28 "@effect/platform": "catalog:",28 "@effect/platform": "catalog:",
29 "@effect/platform-node": "catalog:",29 "@effect/platform-node": "catalog:",
30 "@executor/auth-mcp-oauth": "workspace:*",
31 "@executor/auth-oauth2": "workspace:*",
32 "@executor/codemode-core": "workspace:*",30 "@executor/codemode-core": "workspace:*",
33 "@executor/ir": "workspace:*",31 "@executor/ir": "workspace:*",
34 "@executor/runtime-deno-subprocess": "workspace:*",32 "@executor/runtime-deno-subprocess": "workspace:*",
packages/platform/sdk/src/runtime/auth/mcp-oauth.ts+0−1
@@ -1 +0,0 @@
1export * from "@executor/auth-mcp-oauth";
packages/platform/sdk/src/runtime/auth/oauth-loopback.test.ts+0−110
@@ -1,110 +0,0 @@
1import {
2 createServer,
3} from "node:http";
4
5import {
6 describe,
7 expect,
8 it,
9} from "@effect/vitest";
10import * as Effect from "effect/Effect";
11
12import {
13 startOauthLoopbackRedirectServer,
14} from "./oauth-loopback";
15
16const toError = (cause: unknown): Error =>
17 cause instanceof Error ? cause : new Error(String(cause));
18
19const makeCompletionServer = Effect.acquireRelease(
20 Effect.tryPromise({
21 try: () =>
22 new Promise<{
23 completionUrl: string;
24 requests: string[];
25 server: ReturnType<typeof createServer>;
26 }>((resolve, reject) => {
27 const requests: string[] = [];
28 const server = createServer((request, response) => {
29 requests.push(request.url ?? "/");
30 response.statusCode = 200;
31 response.end("ok");
32 });
33
34 server.listen(0, "127.0.0.1", (error?: Error) => {
35 if (error) {
36 reject(error);
37 return;
38 }
39
40 const address = server.address();
41 if (!address || typeof address === "string") {
42 reject(new Error("Failed to resolve completion server address"));
43 return;
44 }
45
46 resolve({
47 completionUrl: `http://127.0.0.1:${address.port}/oauth/complete?sessionId=test-session`,
48 requests,
49 server,
50 });
51 });
52 }),
53 catch: toError,
54 }),
55 ({ server }) =>
56 Effect.tryPromise({
57 try: () =>
58 new Promise<void>((resolve, reject) => {
59 server.close((error) => {
60 if (error) {
61 reject(error);
62 return;
63 }
64 resolve();
65 });
66 }),
67 catch: toError,
68 }),
69);
70
71const withCompletionServer = <T>(
72 handler: (input: {
73 completionUrl: string;
74 requests: string[];
75 }) => Effect.Effect<T, Error, never>,
76) => Effect.flatMap(makeCompletionServer, handler);
77
78describe("oauth-loopback", () => {
79 it.scoped(
80 "redirects loopback callbacks to the app completion URL with query params intact",
81 () =>
82 withCompletionServer(({ completionUrl, requests }) =>
83 Effect.gen(function* () {
84 const receiver = yield* Effect.acquireRelease(
85 startOauthLoopbackRedirectServer({
86 completionUrl,
87 }),
88 (server) => server.close,
89 );
90
91 const response = yield* Effect.tryPromise({
92 try: () =>
93 fetch(
94 `${receiver.redirectUri}?state=oauth-state&code=oauth-code`,
95 {
96 redirect: "follow",
97 signal: AbortSignal.timeout(10_000),
98 },
99 ),
100 catch: toError,
101 });
102
103 expect(response.ok).toBe(true);
104 expect(requests).toEqual([
105 "/oauth/complete?sessionId=test-session&state=oauth-state&code=oauth-code",
106 ]);
107 }),
108 ),
109 );
110});
packages/platform/sdk/src/runtime/auth/oauth-loopback.ts+0−106
@@ -1,106 +0,0 @@
1import {
2 createServer,
3} from "node:http";
4
5import * as Effect from "effect/Effect";
6
7const DEFAULT_LOOPBACK_TIMEOUT_MS = 10 * 60_000;
8
9export type OauthLoopbackRedirectServer = {
10 redirectUri: string;
11 close: Effect.Effect<void, Error, never>;
12};
13
14const closeServer = (
15 server: ReturnType<typeof createServer>,
16): Promise<void> =>
17 new Promise<void>((resolve, reject) => {
18 server.close((error) => {
19 if (error) {
20 reject(error);
21 return;
22 }
23 resolve();
24 });
25 });
26
27export const startOauthLoopbackRedirectServer = (input: {
28 completionUrl: string;
29 publicHost?: "localhost" | "127.0.0.1";
30 listenHost?: "127.0.0.1";
31 timeoutMs?: number;
32}): Effect.Effect<OauthLoopbackRedirectServer, Error, never> =>
33 Effect.tryPromise({
34 try: () =>
35 new Promise<OauthLoopbackRedirectServer>((resolve, reject) => {
36 const publicHost = input.publicHost ?? "127.0.0.1";
37 const listenHost = input.listenHost ?? "127.0.0.1";
38 const completionUrl = new URL(input.completionUrl);
39 const server = createServer((request, response) => {
40 try {
41 const requestUrl = new URL(
42 request.url ?? "/",
43 `http://${publicHost}`,
44 );
45 const redirectUrl = new URL(completionUrl.toString());
46
47 for (const [key, value] of requestUrl.searchParams.entries()) {
48 redirectUrl.searchParams.set(key, value);
49 }
50
51 response.statusCode = 302;
52 response.setHeader("cache-control", "no-store");
53 response.setHeader("location", redirectUrl.toString());
54 response.end();
55 } catch (cause) {
56 const error = cause instanceof Error ? cause : new Error(String(cause));
57 response.statusCode = 500;
58 response.setHeader("content-type", "text/plain; charset=utf-8");
59 response.end(`OAuth redirect failed: ${error.message}`);
60 } finally {
61 void closeServer(server).catch(() => {});
62 }
63 });
64
65 let timeout: NodeJS.Timeout | null = null;
66 const cleanup = (): Promise<void> => {
67 if (timeout !== null) {
68 clearTimeout(timeout);
69 timeout = null;
70 }
71 return closeServer(server).catch(() => undefined);
72 };
73
74 server.once("error", (cause) => {
75 reject(
76 cause instanceof Error ? cause : new Error(String(cause)),
77 );
78 });
79
80 server.listen(0, listenHost, () => {
81 const address = server.address();
82 if (!address || typeof address === "string") {
83 reject(new Error("Failed to resolve OAuth loopback port"));
84 return;
85 }
86
87 timeout = setTimeout(() => {
88 void cleanup();
89 }, input.timeoutMs ?? DEFAULT_LOOPBACK_TIMEOUT_MS);
90 if (typeof timeout.unref === "function") {
91 timeout.unref();
92 }
93
94 resolve({
95 redirectUri: `http://${publicHost}:${address.port}`,
96 close: Effect.tryPromise({
97 try: cleanup,
98 catch: (cause) =>
99 cause instanceof Error ? cause : new Error(String(cause)),
100 }),
101 });
102 });
103 }),
104 catch: (cause) =>
105 cause instanceof Error ? cause : new Error(String(cause)),
106 });
packages/platform/sdk/src/runtime/auth/oauth2-pkce.ts+0−1
@@ -1 +0,0 @@
1export * from "@executor/auth-oauth2";
packages/sources/core/package.json+0−1
@@ -12,7 +12,6 @@ · 11 unmodified lines
12 "typecheck": "bunx tsc --noEmit -p tsconfig.json"12 "typecheck": "bunx tsc --noEmit -p tsconfig.json"
13 },13 },
14 "dependencies": {14 "dependencies": {
15 "@executor/auth-oauth2": "workspace:*",
16 "@executor/codemode-core": "workspace:*",15 "@executor/codemode-core": "workspace:*",
17 "@executor/ir": "workspace:*",16 "@executor/ir": "workspace:*",
18 "@modelcontextprotocol/sdk": "^1.26.0",17 "@modelcontextprotocol/sdk": "^1.26.0",
plugins/google-discovery/sdk/index.ts+6−6
@@ -1,12 +1,6 @@
1import * as Effect from "effect/Effect";1import * as Effect from "effect/Effect";
2import * as Schema from "effect/Schema";2import * as Schema from "effect/Schema";
3 3
4import {
5 buildOAuth2AuthorizationUrl,
6 createPkceCodeVerifier,
7 exchangeOAuth2AuthorizationCode,
8 refreshOAuth2AccessToken,
9} from "@executor/auth-oauth2";
10import {4import {
11 createCatalogImportMetadata,5 createCatalogImportMetadata,
12 createSourceCatalogSyncResult,6 createSourceCatalogSyncResult,
@@ -58,6 +52,12 @@ import { · 45 unmodified lines
58 type GoogleDiscoveryToolManifest,52 type GoogleDiscoveryToolManifest,
59 type GoogleDiscoveryToolProviderData,53 type GoogleDiscoveryToolProviderData,
60} from "./types";54} from "./types";
55import {
56 buildOAuth2AuthorizationUrl,
57 createPkceCodeVerifier,
58 exchangeOAuth2AuthorizationCode,
59 refreshOAuth2AccessToken,
60} from "./oauth2";
61 61
62const decodeStoredSourceData = Schema.decodeUnknownSync(62const decodeStoredSourceData = Schema.decodeUnknownSync(
63 GoogleDiscoveryStoredSourceDataSchema,63 GoogleDiscoveryStoredSourceDataSchema,
plugins/google-discovery/sdk/oauth2.tsrenamed from packages/auth/oauth2/src/index.ts+29−9
@@ -42,7 +42,10 @@ export const buildOAuth2AuthorizationUrl = (input: { · 41 unmodified lines
42 url.searchParams.set("scope", input.scopes.join(" "));42 url.searchParams.set("scope", input.scopes.join(" "));
43 url.searchParams.set("state", input.state);43 url.searchParams.set("state", input.state);
44 url.searchParams.set("code_challenge_method", "S256");44 url.searchParams.set("code_challenge_method", "S256");
45 url.searchParams.set("code_challenge", createPkceCodeChallenge(input.codeVerifier));45 url.searchParams.set(
46 "code_challenge",
47 createPkceCodeChallenge(input.codeVerifier),
48 );
46 49
47 for (const [key, value] of Object.entries(input.extraParams ?? {})) {50 for (const [key, value] of Object.entries(input.extraParams ?? {})) {
48 url.searchParams.set(key, value);51 url.searchParams.set(key, value);
@@ -51,17 +54,23 @@ export const buildOAuth2AuthorizationUrl = (input: { · 2 unmodified lines
51 return url.toString();54 return url.toString();
52};55};
53 56
54const parseOAuth2TokenResponse = async (response: Response): Promise<OAuth2TokenResponse> => {57const parseOAuth2TokenResponse = async (
58 response: Response,
59): Promise<OAuth2TokenResponse> => {
55 const rawText = await response.text();60 const rawText = await response.text();
56 let parsed: unknown;61 let parsed: unknown;
57 try {62 try {
58 parsed = JSON.parse(rawText);63 parsed = JSON.parse(rawText);
59 } catch {64 } catch {
60 throw new Error(`OAuth token endpoint returned non-JSON response (${response.status})`);65 throw new Error(
66 `OAuth token endpoint returned non-JSON response (${response.status})`,
67 );
61 }68 }
62 69
63 if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {70 if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
64 throw new Error(`OAuth token endpoint returned invalid JSON payload (${response.status})`);71 throw new Error(
72 `OAuth token endpoint returned invalid JSON payload (${response.status})`,
73 );
65 }74 }
66 75
67 const record = parsed as Record<string, unknown>;76 const record = parsed as Record<string, unknown>;
@@ -72,7 +81,8 @@ const parseOAuth2TokenResponse = async (response: Response): Promise<OAuth2Token · 4 unmodified lines
72 81
73 if (!response.ok) {82 if (!response.ok) {
74 const description =83 const description =
75 typeof record.error_description === "string" && record.error_description.length > 084 typeof record.error_description === "string"
85 && record.error_description.length > 0
76 ? record.error_description86 ? record.error_description
77 : typeof record.error === "string" && record.error.length > 087 : typeof record.error === "string" && record.error.length > 0
78 ? record.error88 ? record.error
@@ -86,8 +96,12 @@ const parseOAuth2TokenResponse = async (response: Response): Promise<OAuth2Token · 7 unmodified lines
86 96
87 return {97 return {
88 access_token: accessToken,98 access_token: accessToken,
89 token_type: typeof record.token_type === "string" ? record.token_type : undefined,99 token_type:
90 refresh_token: typeof record.refresh_token === "string" ? record.refresh_token : undefined,100 typeof record.token_type === "string" ? record.token_type : undefined,
101 refresh_token:
102 typeof record.refresh_token === "string"
103 ? record.refresh_token
104 : undefined,
91 expires_in: typeof record.expires_in === "number"105 expires_in: typeof record.expires_in === "number"
92 ? record.expires_in106 ? record.expires_in
93 : typeof record.expires_in === "string"107 : typeof record.expires_in === "string"
@@ -137,7 +151,10 @@ export const exchangeOAuth2AuthorizationCode = (input: { · 43 unmodified lines
137 code: input.code,151 code: input.code,
138 });152 });
139 153
140 if (input.clientAuthentication === "client_secret_post" && input.clientSecret) {154 if (
155 input.clientAuthentication === "client_secret_post"
156 && input.clientSecret
157 ) {
141 body.set("client_secret", input.clientSecret);158 body.set("client_secret", input.clientSecret);
142 }159 }
143 160
@@ -162,7 +179,10 @@ export const refreshOAuth2AccessToken = (input: { · 18 unmodified lines
162 refresh_token: input.refreshToken,179 refresh_token: input.refreshToken,
163 });180 });
164 181
165 if (input.clientAuthentication === "client_secret_post" && input.clientSecret) {182 if (
183 input.clientAuthentication === "client_secret_post"
184 && input.clientSecret
185 ) {
166 body.set("client_secret", input.clientSecret);186 body.set("client_secret", input.clientSecret);
167 }187 }
168 188
plugins/google-discovery/sdk/package.json+0−1
@@ -13,7 +13,6 @@ · 12 unmodified lines
13 },13 },
14 "dependencies": {14 "dependencies": {
15 "@effect/platform": "catalog:",15 "@effect/platform": "catalog:",
16 "@executor/auth-oauth2": "workspace:*",
17 "@executor/codemode-core": "workspace:*",16 "@executor/codemode-core": "workspace:*",
18 "@executor/ir": "workspace:*",17 "@executor/ir": "workspace:*",
19 "@executor/platform-sdk": "workspace:*",18 "@executor/platform-sdk": "workspace:*",