Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346import { getAgentByName } from "agents";import { loadCustomerConfig, type InstallationConfig,} from "../configuration/customer";import type { Secret } from "../configuration/secrets";import { decode, hash, random, opaque, verifyAssertion, LOGIN_PURPOSE, HEALTH_PURPOSE, type BridgeClaims,} from "../shared/bridge";import { createOwnerSession, authorizeRuntimeRequest } from "./session";import { PERSONAL_PATH } from "./runtime-path";import type { Env } from "./personal-agent";const CHALLENGE_COOKIE = "__Host-flarebot-login";const cookie = (value: string, maxAge: number) => `${CHALLENGE_COOKIE}=${value}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=${maxAge}`;function response( body: BodyInit | null, status: number, headers: Record<string, string> = {},) { return new Response(body, { status, headers: { "Cache-Control": "no-store", "Referrer-Policy": "no-referrer", "Content-Security-Policy": "default-src 'none'; frame-ancestors 'none'; base-uri 'none'", ...headers, }, });}function challengeCookie(request: Request) { const values = (request.headers.get("Cookie") ?? "") .split(";") .map((value) => value.trim()) .filter((value) => value.startsWith(`${CHALLENGE_COOKIE}=`)); const value = values[0]?.slice(CHALLENGE_COOKIE.length + 1); return values.length === 1 && opaque(value) ? value : null;}function navigation(request: Request) { return ( request.method === "GET" && !request.headers.has("Upgrade") && (!request.headers.has("Sec-Fetch-Mode") || request.headers.get("Sec-Fetch-Mode") === "navigate") );}function loopback(origin: string) { return ["localhost", "127.0.0.1", "[::1]"].includes(new URL(origin).hostname);}export async function handleCustomerBridge( request: Request, env: Env, installation: InstallationConfig, secret: Secret, network: typeof fetch = fetch,): Promise<Response | null> { const url = new URL(request.url); if ( ![ "/auth/login", "/auth/callback", "/auth/bootstrap-health", "/auth/provider-enabled", "/auth/domain-configuration", "/auth/domain-health", ].includes(url.pathname) ) return null; try { if (request.url.length > 2048) throw new Error("Bridge unavailable"); const personal = await getAgentByName(env.PersonalAgent, "personal"); if (url.pathname === "/auth/domain-configuration") { if ( url.origin !== loadCustomerConfig(env).effectiveInstallation.runtimeOrigin || request.method !== "POST" || url.search || request.headers.has("Origin") || request.headers.has("Cookie") || request.headers.has("Sec-Fetch-Site") ) throw new Error("Invalid domain configuration"); const assertion = request.headers .get("Authorization") ?.match(/^Bearer ([A-Za-z0-9_.-]{1,4096})$/)?.[1]; if (!assertion) throw new Error("Invalid domain configuration"); return response( JSON.stringify(await personal.configureDomain(assertion)), 200, { "Content-Type": "application/json" }, ); } if (url.pathname === "/auth/domain-health") { if ( request.method !== "POST" || url.search || request.headers.has("Origin") || request.headers.has("Cookie") || request.headers.has("Sec-Fetch-Site") || (await personal.configuredDomainOrigin()) !== url.origin ) throw new Error("Invalid domain health"); const assertion = request.headers .get("Authorization") ?.match(/^Bearer ([A-Za-z0-9_.-]{1,4096})$/)?.[1]; if (!assertion) throw new Error("Invalid domain health"); return response( JSON.stringify(await personal.domainHealth(assertion, url.origin)), 200, { "Content-Type": "application/json" }, ); } if (url.origin !== installation.runtimeOrigin) throw new Error("Bridge unavailable"); if ( env.FLAREBOT_ENV === "development" && loopback(installation.runtimeOrigin) && url.pathname === "/auth/login" && navigation(request) && !url.search ) return response(null, 303, { Location: "/", "Set-Cookie": await createOwnerSession(secret, installation), }); if (!installation.bridge) throw new Error("Bridge unavailable"); if ( url.pathname === "/auth/provider-enabled" && request.method === "POST" && !url.search ) { if ( request.headers.has("Origin") || request.headers.has("Cookie") || request.headers.has("Sec-Fetch-Site") ) throw new Error("Invalid provider receipt"); const assertion = request.headers .get("Authorization") ?.match(/^Bearer ([A-Za-z0-9_.-]{1,4096})$/)?.[1]; if (!assertion) throw new Error("Invalid provider receipt"); const enabled = await personal.enableOpenRouter(assertion); return response(JSON.stringify(enabled), 200, { "Content-Type": "application/json", }); } if (url.pathname === "/auth/login" && navigation(request) && !url.search) { const state = random(); const binding = random(); const verifier = random(); const challenge = await hash(verifier); await personal.createLoginChallenge({ state, bindingHash: await hash(`${binding}.${installation.runtimeOrigin}`), verifier, challenge, // Leave room for independent Worker and Durable Object request clocks. // The store still enforces the absolute ten-minute upper bound. expiresAt: Date.now() + 540_000, }); const destination = new URL( "/auth/bridge", installation.controlPlaneOrigin, ); destination.search = new URLSearchParams({ installationId: installation.installationId, state, challenge, audience: installation.runtimeOrigin, }).toString(); return response(null, 303, { Location: destination.href, "Set-Cookie": cookie(binding, 600), }); } if (url.pathname === "/auth/callback" && navigation(request)) { if ( [...url.searchParams.keys()].length !== 2 || ["code", "state"].some( (key) => url.searchParams.getAll(key).length !== 1, ) ) throw new Error("Invalid callback"); const code = url.searchParams.get("code"); const state = url.searchParams.get("state"); const binding = challengeCookie(request); if (!opaque(code) || !opaque(state) || !binding) throw new Error("Invalid callback"); const challenge = await personal.claimLoginChallenge( state, await hash(`${binding}.${installation.runtimeOrigin}`), ); if (!challenge) throw new Error("Invalid challenge"); const exchanged = await network( new Request( new URL("/auth/bridge/exchange", installation.controlPlaneOrigin), { method: "POST", redirect: "manual", signal: AbortSignal.timeout(10_000), headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ code, verifier: challenge.verifier, installationId: installation.installationId, audience: installation.runtimeOrigin, state, }), }, ), ); if (!exchanged.ok) throw new Error("Exchange denied"); const body = await boundedText(exchanged, 5000); const result = JSON.parse(body); if ( Object.keys(result).join(",") !== "assertion" || typeof result.assertion !== "string" ) throw new Error("Invalid exchange"); await verifyAssertion(result.assertion, installation.bridge, { iss: installation.controlPlaneOrigin, aud: installation.runtimeOrigin, sub: installation.ownerSubject, installationId: installation.installationId, purpose: LOGIN_PURPOSE, state, challenge: challenge.challenge, }); const resultResponse = response(null, 303, { Location: "/", "Set-Cookie": cookie("", 0), }); resultResponse.headers.append( "Set-Cookie", await createOwnerSession(secret, installation), ); return resultResponse; } if ( url.pathname === "/auth/bootstrap-health" && request.method === "POST" && !url.search ) { if (request.headers.has("Origin") || request.headers.has("Cookie")) throw new Error("Invalid health request"); const token = request.headers .get("Authorization") ?.match(/^Bearer ([A-Za-z0-9_.-]{1,4096})$/)?.[1]; const release = installation.release; if (!token || !release) throw new Error("Invalid health request"); const untrusted = JSON.parse( new TextDecoder().decode(decode(token.split(".")[1])), ) as BridgeClaims; if (!opaque(untrusted.state) || !opaque(untrusted.challenge)) throw new Error("Invalid health request"); const claims = await verifyAssertion(token, installation.bridge, { iss: installation.controlPlaneOrigin, aud: installation.runtimeOrigin, sub: installation.ownerSubject, installationId: installation.installationId, purpose: HEALTH_PURPOSE, state: untrusted.state, challenge: untrusted.challenge, operationId: release.operationId, artifactDigest: release.artifactDigest, version: release.version, }); if (!env.AI || !env.BROWSER || !env.LOADER || !env.Sandbox || !env.ASSETS) throw new Error("Missing native bindings"); const asset = await env.ASSETS.fetch( new Request( new URL("/flarebot-health.txt", installation.runtimeOrigin), ), ); if ( !asset.ok || (await boundedText(asset, 128)) !== "flarebot-assets-v1\n" ) throw new Error("Assets unavailable"); const denied = await authorizeRuntimeRequest( new Request(new URL(PERSONAL_PATH, installation.runtimeOrigin)), secret, installation, ); const socketDenied = await authorizeRuntimeRequest( new Request(new URL(PERSONAL_PATH, installation.runtimeOrigin), { headers: { Upgrade: "websocket", Origin: installation.runtimeOrigin }, }), secret, installation, ); if (denied?.status !== 401 || socketDenied?.status !== 401) throw new Error("Authentication unavailable"); const personal = await getAgentByName(env.PersonalAgent, "personal"); const readiness = await personal.bootstrapHealth(claims); return response( JSON.stringify({ installationId: installation.installationId, ...release, state: claims.state, challenge: claims.challenge, ...readiness, bindings: "present", assets: "ready", authentication: "required", }), 200, { "Content-Type": "application/json" }, ); } throw new Error("Invalid bridge request"); } catch { return response( "Owner authentication or installation verification failed.", 403, url.pathname === "/auth/callback" ? { "Set-Cookie": cookie("", 0) } : {}, ); }}async function boundedText(response: Response, limit: number) { const reader = response.body?.getReader(); if (!reader) throw new Error("Missing response"); const decoder = new TextDecoder(); let text = ""; let size = 0; while (true) { const chunk = await reader.read(); if (chunk.done) break; size += chunk.value.byteLength; if (size > limit) { await reader.cancel(); throw new Error("Response too large"); } text += decoder.decode(chunk.value, { stream: true }); } return text + decoder.decode();}