| 1 | // @effect-diagnostics nodeBuiltinImport:off - node:http sends the one-shot loopback callback with no proxy, redirect handling, or response logging. |
| 2 | import * as NodeHttp from "node:http"; |
| 3 | |
| 4 | import { ProviderSetupError, type ProviderInstanceId } from "@t3tools/contracts"; |
| 5 | import * as Effect from "effect/Effect"; |
| 6 | |
| 7 | export interface AntigravityPendingCallback { |
| 8 | readonly redirectUri: string; |
| 9 | readonly state: string; |
| 10 | } |
| 11 | |
| 12 | /** Only the callback advertised by this running ACP process may receive a request. */ |
| 13 | export const validateAntigravityCallbackUrl = Effect.fn("validateAntigravityCallbackUrl")( |
| 14 | function* ( |
| 15 | instanceId: ProviderInstanceId, |
| 16 | pending: AntigravityPendingCallback, |
| 17 | callbackUrl: string, |
| 18 | ) { |
| 19 | const invalid = (detail: string) => |
| 20 | new ProviderSetupError({ instanceId, operation: "complete", detail }); |
| 21 | if (callbackUrl.length > 16_384) { |
| 22 | return yield* invalid("The sign-in response URL is too long."); |
| 23 | } |
| 24 | const callback = yield* Effect.try({ |
| 25 | try: () => new URL(callbackUrl), |
| 26 | catch: () => invalid("Paste the complete redirect URL from the Google sign-in page."), |
| 27 | }); |
| 28 | const expected = new URL(pending.redirectUri); |
| 29 | if ( |
| 30 | callback.protocol !== "http:" || |
| 31 | callback.hostname !== "127.0.0.1" || |
| 32 | callback.origin !== expected.origin || |
| 33 | callback.pathname !== expected.pathname || |
| 34 | callback.username !== "" || |
| 35 | callback.password !== "" || |
| 36 | callback.hash !== "" |
| 37 | ) { |
| 38 | return yield* invalid("This redirect URL does not belong to the current sign-in."); |
| 39 | } |
| 40 | const states = callback.searchParams.getAll("state"); |
| 41 | if (states.length !== 1 || states[0] !== pending.state) { |
| 42 | return yield* invalid("This redirect URL does not belong to the current sign-in."); |
| 43 | } |
| 44 | const codes = callback.searchParams.getAll("code"); |
| 45 | const errors = callback.searchParams.getAll("error"); |
| 46 | if ( |
| 47 | !( |
| 48 | (codes.length === 1 && Boolean(codes[0]) && errors.length === 0) || |
| 49 | (errors.length === 1 && Boolean(errors[0]) && codes.length === 0) |
| 50 | ) |
| 51 | ) { |
| 52 | return yield* invalid("The redirect URL must contain one Google sign-in response."); |
| 53 | } |
| 54 | const issuers = callback.searchParams.getAll("iss"); |
| 55 | if ( |
| 56 | issuers.length > 1 || |
| 57 | (issuers.length === 1 && issuers[0] !== "https://accounts.google.com") |
| 58 | ) { |
| 59 | return yield* invalid("The redirect URL is not a Google sign-in response."); |
| 60 | } |
| 61 | return callback; |
| 62 | }, |
| 63 | ); |
| 64 | |
| 65 | /** Sends one callback, without proxies, redirects, readiness probes, or response logging. */ |
| 66 | export const forwardAntigravityCallback = ( |
| 67 | instanceId: ProviderInstanceId, |
| 68 | callback: URL, |
| 69 | ): Effect.Effect<void, ProviderSetupError> => |
| 70 | Effect.callback<void, ProviderSetupError>((resume) => { |
| 71 | const failed = () => |
| 72 | new ProviderSetupError({ |
| 73 | instanceId, |
| 74 | operation: "complete", |
| 75 | detail: "Could not deliver the sign-in response. Start sign-in again.", |
| 76 | }); |
| 77 | let response: NodeHttp.IncomingMessage | undefined; |
| 78 | const request = NodeHttp.request( |
| 79 | { |
| 80 | protocol: "http:", |
| 81 | hostname: callback.hostname, |
| 82 | port: callback.port, |
| 83 | path: `${callback.pathname}${callback.search}`, |
| 84 | method: "GET", |
| 85 | agent: false, |
| 86 | }, |
| 87 | (incoming) => { |
| 88 | response = incoming; |
| 89 | incoming.once("error", () => resume(Effect.fail(failed()))); |
| 90 | incoming.once("end", () => { |
| 91 | const status = incoming.statusCode ?? 0; |
| 92 | resume(status >= 200 && status < 300 ? Effect.void : Effect.fail(failed())); |
| 93 | }); |
| 94 | incoming.resume(); |
| 95 | }, |
| 96 | ); |
| 97 | request.once("error", () => resume(Effect.fail(failed()))); |
| 98 | request.end(); |
| 99 | return Effect.sync(() => { |
| 100 | request.destroy(); |
| 101 | response?.destroy(); |
| 102 | }); |
| 103 | }).pipe( |
| 104 | Effect.timeoutOrElse({ |
| 105 | duration: "10 seconds", |
| 106 | orElse: () => |
| 107 | Effect.fail( |
| 108 | new ProviderSetupError({ |
| 109 | instanceId, |
| 110 | operation: "complete", |
| 111 | detail: "The sign-in response timed out. Start sign-in again.", |
| 112 | }), |
| 113 | ), |
| 114 | }), |
| 115 | ); |