From e74a6ac2d49d2c610b608b00c73c1cb614af5883 Mon Sep 17 00:00:00 2001 From: Eric Date: Fri, 30 Oct 2026 18:27:03 -0500 Subject: [PATCH] fix(oauth): send a same-origin path as the OAuth return The agent refuses an absolute redirect_uri on another origin as an open redirect, so sign-in from scout.ciris.ai to agents on scoutapilb.ciris.ai failed for every user. Send the path; scoutapilb's nginx forwards /oauth-complete.html and /oauth/* back to scout.ciris.ai with the one-time code. Needs CIRISManager's web_client_origin forward deployed first. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019ZeGbV8M3kbUSir7VvKGso --- apps/agui/app/account/page.tsx | 5 +++-- apps/agui/app/login/page.tsx | 7 +++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/agui/app/account/page.tsx b/apps/agui/app/account/page.tsx index 4c7e890..c5827bf 100644 --- a/apps/agui/app/account/page.tsx +++ b/apps/agui/app/account/page.tsx @@ -100,8 +100,9 @@ function AccountPageContent() { const agentIdMatch = apiBaseUrl.match(/\/api\/([^\/]+)/); const agentId = agentIdMatch ? agentIdMatch[1] : (currentAgent?.agent_id || 'scout'); - // Use agent-specific callback URL for linking - const redirectUri = encodeURIComponent(`${window.location.origin}/oauth/${agentId}/${provider}/callback`); + // Same-origin path only (see login/page.tsx): the API host's nginx + // forwards /oauth/* back to this origin. + const redirectUri = encodeURIComponent(`/oauth/${agentId}/${provider}/callback`); // apiBaseUrl does NOT include /v1, so we need to add it const oauthUrl = `${apiBaseUrl}/v1/auth/oauth/${provider}/login`; diff --git a/apps/agui/app/login/page.tsx b/apps/agui/app/login/page.tsx index 2fce575..d1cfd30 100644 --- a/apps/agui/app/login/page.tsx +++ b/apps/agui/app/login/page.tsx @@ -91,8 +91,11 @@ export default function LoginPage() { const apiBaseUrl = process.env.NEXT_PUBLIC_SCOUT_API_URL || process.env.NEXT_PUBLIC_API_BASE_URL || window.location.origin; - // Use static HTML callback page - const callbackUrl = `${window.location.origin}/oauth-complete.html?marketing_opt_in=${marketingOptIn}`; + // The agent accepts only a same-origin PATH as its OAuth return (an + // absolute URL on another origin is refused as an open redirect). The + // browser therefore lands on the API host, whose nginx forwards + // /oauth-complete.html back to this origin with the one-time code. + const callbackUrl = `/oauth-complete.html?marketing_opt_in=${marketingOptIn}`; const redirectUri = encodeURIComponent(callbackUrl); // apiBaseUrl does NOT include /v1, so we need to add it const oauthUrl = `${apiBaseUrl}/v1/auth/oauth/${provider}/login`;