From 9f834bf6ab91aaf8a05b16d264fb24d1712b21fd Mon Sep 17 00:00:00 2001 From: Heath Stewart Date: Wed, 25 Feb 2026 23:14:44 -0800 Subject: [PATCH] Add cors support Co-Authored-By: @stevedylan.dev --- docs/src/index.ts | 10 +++++++ docs/src/lib/oauth-client.ts | 2 +- docs/src/lib/path-redirect.ts | 51 +++++++++++++++++++++++++++++++++++ docs/src/routes/auth.ts | 4 +-- 4 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 docs/src/lib/path-redirect.ts diff --git a/docs/src/index.ts b/docs/src/index.ts index d6bba0a..2a38133 100644 --- a/docs/src/index.ts +++ b/docs/src/index.ts @@ -1,6 +1,8 @@ import { Hono } from "hono"; +import { cors } from "hono/cors"; import auth from "./routes/auth"; import subscribe from "./routes/subscribe"; +import "./lib/path-redirect"; type Bindings = { ASSETS: Fetcher; @@ -12,6 +14,14 @@ const app = new Hono<{ Bindings: Bindings }>(); app.route("/oauth", auth); app.route("/subscribe", subscribe); +app.use("/subscribe", cors({ + origin: (origin) => origin, + credentials: true, +})); +app.use("/subscribe/*", cors({ + origin: (origin) => origin, + credentials: true, +})); app.get("/api/health", (c) => { return c.json({ status: "ok" }); diff --git a/docs/src/lib/oauth-client.ts b/docs/src/lib/oauth-client.ts index 554e9e4..6e5dae0 100644 --- a/docs/src/lib/oauth-client.ts +++ b/docs/src/lib/oauth-client.ts @@ -19,7 +19,7 @@ export function createOAuthClient(kv: KVNamespace, clientUrl: string) { redirect_uris: [redirectUri], grant_types: ["authorization_code", "refresh_token"], response_types: ["code"], - scope: "atproto transition:generic", + scope: "atproto site.standard.graph.subscription", token_endpoint_auth_method: "none", application_type: "web", dpop_bound_access_tokens: true, diff --git a/docs/src/lib/path-redirect.ts b/docs/src/lib/path-redirect.ts new file mode 100644 index 0000000..cf6a05c --- /dev/null +++ b/docs/src/lib/path-redirect.ts @@ -0,0 +1,51 @@ +// Cloudflare Workers compatibility patches for @atproto libraries. +// +// 1. Workers don't support `redirect: 'error'` — simulate it with 'manual'. +// 2. Workers don't support the standard `cache` option in Request — strip it. + +function sanitizeInit(init?: RequestInit): RequestInit | undefined { + if (!init) return init; + const { cache, redirect, ...rest } = init; + return { + ...rest, + // Workers only support 'follow' and 'manual' + redirect: redirect === "error" ? "manual" : redirect, + // Workers don't support standard cache modes — omit entirely + ...(cache ? {} : {}), + }; +} + +const errorRedirectRequests = new WeakSet(); +const OriginalRequest = globalThis.Request; + +globalThis.Request = class extends OriginalRequest { + constructor( + input: RequestInfo | URL, + init?: RequestInit, + ) { + super(input, sanitizeInit(init)); + if (init?.redirect === "error") { + errorRedirectRequests.add(this); + } + } +} as typeof Request; + +const originalFetch = globalThis.fetch; +globalThis.fetch = (async ( + input: RequestInfo | URL, + init?: RequestInit, +): Promise => { + const cleanInit = sanitizeInit(init); + const response = await originalFetch(input, cleanInit); + + // Simulate redirect: 'error' — throw on 3xx + const wantsRedirectError = + init?.redirect === "error" || + (input instanceof Request && errorRedirectRequests.has(input)); + + if (wantsRedirectError && response.status >= 300 && response.status < 400) { + throw new TypeError("unexpected redirect"); + } + + return response; +}) as typeof fetch; diff --git a/docs/src/routes/auth.ts b/docs/src/routes/auth.ts index 048ada7..6fc4e8d 100644 --- a/docs/src/routes/auth.ts +++ b/docs/src/routes/auth.ts @@ -27,7 +27,7 @@ auth.get("/client-metadata.json", (c) => { redirect_uris: [redirectUri], grant_types: ["authorization_code", "refresh_token"], response_types: ["code"], - scope: "atproto transition:generic", + scope: "atproto site.standard.graph.subscription", token_endpoint_auth_method: "none", application_type: "web", dpop_bound_access_tokens: true, @@ -44,7 +44,7 @@ auth.get("/login", async (c) => { const client = createOAuthClient(c.env.SEQUOIA_SESSIONS, c.env.CLIENT_URL); const authUrl = await client.authorize(handle, { - scope: "atproto transition:generic", + scope: "atproto site.standard.graph.subscription", }); return c.redirect(authUrl.toString()); -- 2.51.2