decolua/9router · warning

Cross-origin callback rejected

Error message

Cross-origin callback rejected

What it means

startTraeProxy enforces an anti-CSRF check on the OAuth callback: requests must either carry no Origin header (like a genuine browser redirect from a top-level navigation) or have an Origin that resolves to a loopback host. A request with a cross-origin Origin header is answered 403 with 'Cross-origin callback rejected'. This prevents a malicious web page from scripting fetches against the loopback callback endpoint to inject forged authorization codes.

Source

Thrown at src/lib/oauth/utils/server.js:483

      return;
    }
    const server = http.createServer(async (req, res) => {
      const url = new URL(req.url, "http://localhost");
      if (url.pathname !== TRAE_CONFIG.callbackPath && url.pathname !== "/auth/callback") {
        res.writeHead(404);
        res.end("Not found");
        return;
      }
      const session = traeSession;
      if (!session) {
        res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
        res.end(renderCodexResultPage(false, "No active Trae login session"));
        return;
      }
      // Anti-CSRF: reject cross-origin fetches (legit redirects send no Origin),
      // and reject state mismatch when state is present.
      if (!isLoopbackOrigin(req.headers.origin)) {
        res.writeHead(403, { "Content-Type": "text/html; charset=utf-8" });
        res.end(renderCodexResultPage(false, "Cross-origin callback rejected"));
        return;
      }
      const cbState = url.searchParams.get("state");
      if (cbState && session.state && cbState !== session.state) {
        session.status = "error";
        session.error = "Trae callback state mismatch";
        res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
        res.end(renderCodexResultPage(false, session.error));
        stopTraeProxy();
        return;
      }
      // Pass the raw callback query to exchangeTokens → parseTraeCallback
      const rawCallback = `${url.pathname}?${url.searchParams.toString()}`;
      try {
        const { exchangeTokens } = await import("../providers.js");
        const { createProviderConnection } = await import("@/models");
        const tokenData = await exchangeTokens("trae", rawCallback);

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Complete the login through the normal redirect flow — top-level redirects send no Origin header and pass the check.
  2. If testing through a tunnel, test against http://127.0.0.1 directly instead of the public tunnel URL.
  3. If an extension/proxy injects an Origin header, disable it or exclude localhost from its rewriting rules.

Example fix

// before (test via curl with a foreign Origin)
curl -H 'Origin: https://evil.example' 'http://127.0.0.1:3456/callback?code=...&state=...'
// after
curl 'http://127.0.0.1:3456/callback?code=...&state=...'  // no Origin header → accepted
Defensive patterns

Strategy: try-catch

Validate before calling

const origin = req.headers.origin;
if (origin && !['http://127.0.0.1','http://localhost'].some(h => origin.startsWith(h))) {
  // cross-origin scripted request — will be rejected; use a top-level redirect instead
}

Type guard

function isLoopbackOrigin(origin) {
  if (!origin) return true; // redirects send no Origin
  try { const u = new URL(origin); return ['127.0.0.1','localhost','[::1]'].includes(u.hostname); }
  catch { return false; }
}

Try / catch

const res = await fetch(callbackUrl, { redirect: 'manual' });
if (res.status === 403 && (await res.text()).includes('Cross-origin callback rejected')) {
  console.warn('CSRF guard fired — complete login via normal redirect, not scripted fetch');
}

Prevention

When it happens

Trigger: A browser page on a non-loopback origin (e.g. http://evil.example) issues a fetch/XHR to http://127.0.0.1:<traePort>/...callback during an active Trae session; CORS preflight or credentialed cross-site calls; misconfigured redirect landing from a non-loopback host header context that sets Origin.

Common situations: Clickjacking/phishing page attempting a CSRF login attack; browser extensions proxying requests with an Origin header; corporate proxies that rewrite requests and attach an external Origin; developer testing the callback via a forwarded public tunnel (ngrok) whose Origin is not loopback.

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/6642703b4c950a1d. Report an issue: GitHub.