auth: strip prompt=consent from initial authorize (auto-approve for connector)
build / test (push) Successful in 7s
build / build (push) Successful in 8s

Claude forces the consent screen with prompt=consent; the consent endpoint's
signed-query round-trip is brittle. Simpler: drop prompt=consent from the initial
unsigned authorize so skipConsent applies and no consent page is shown. Also pass
raw query bytes to the login/consent handlers.
This commit is contained in:
2026-07-06 17:11:52 -04:00
parent 462d8c0223
commit 63a9553868
+19 -2
View File
@@ -77,6 +77,11 @@ async function handleConsent(req: IncomingMessage, res: ServerResponse, rawSearc
const server = createServer((req, res) => {
const url = new URL(req.url ?? "/", "http://localhost");
// Raw query bytes (the URL API re-encodes .search, which breaks Better Auth's
// signed oauth_query check on /consent). Take the query straight from req.url.
const rawUrl = req.url ?? "/";
const qIndex = rawUrl.indexOf("?");
const rawSearch = qIndex >= 0 ? rawUrl.slice(qIndex) : "";
if (url.pathname !== "/healthz") {
// Request-level trace (path only, no query — avoids logging codes/tokens).
// eslint-disable-next-line no-console
@@ -89,13 +94,25 @@ const server = createServer((req, res) => {
return;
}
if (url.pathname === "/login" && req.method === "GET") {
void startLogin(req, res, url.search);
void startLogin(req, res, rawSearch);
return;
}
if (url.pathname === "/consent" && req.method === "GET") {
void handleConsent(req, res, url.search);
void handleConsent(req, res, rawSearch);
return;
}
// Claude sends prompt=consent on the initial authorize, which forces the
// consent screen even with skipConsent. For a personal connector the login is
// the authorization, so strip it from the initial (unsigned) request — the
// signed resume after login has a `sig` and is left untouched.
if (
url.pathname === "/api/auth/oauth2/authorize" &&
!url.searchParams.has("sig") &&
url.searchParams.get("prompt") === "consent"
) {
url.searchParams.delete("prompt");
req.url = url.pathname + (url.searchParams.toString() ? `?${url.searchParams}` : "");
}
// Everything else -> Better Auth (async handler).
void handler(req, res);
});