diff --git a/ui/src/pages/CloudUpstream.test.tsx b/ui/src/pages/CloudUpstream.test.tsx
index 21f4a29464c..4eaee06876e 100644
--- a/ui/src/pages/CloudUpstream.test.tsx
+++ b/ui/src/pages/CloudUpstream.test.tsx
@@ -38,7 +38,7 @@ vi.mock("@/context/BreadcrumbContext", () => ({
vi.mock("@/context/CompanyContext", () => ({
useCompany: () => ({
- selectedCompany: { id: "company-1", name: "Paperclip" },
+ selectedCompany: { id: "company-1", name: "Paperclip", issuePrefix: "PAP" },
selectedCompanyId: "company-1",
}),
}));
@@ -136,6 +136,54 @@ describe("CloudUpstream", () => {
});
});
+ it("sends a company-prefixed redirectUri when starting Connect", async () => {
+ mockCloudUpstreamsApi.list.mockResolvedValue({ connections: [], runs: [] });
+ mockCloudUpstreamsApi.startConnect.mockResolvedValue({
+ pendingConnectionId: "pending-1",
+ authorizationUrl: "https://cloud.example/upstream-consent?state=abc",
+ });
+ const root = createRoot(container);
+ const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } });
+
+ await act(async () => {
+ root.render(
+
+
+ ,
+ );
+ });
+ await flushReact();
+ await flushReact();
+
+ const input = container.querySelector("input[aria-label='Paperclip Cloud stack URL']");
+ expect(input).toBeTruthy();
+ await act(async () => {
+ const setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")!.set!;
+ setter.call(input!, "https://cloud.example/PAP/dashboard");
+ input!.dispatchEvent(new Event("input", { bubbles: true }));
+ });
+ await flushReact();
+
+ const connectButton = Array.from(container.querySelectorAll("button"))
+ .find((button) => button.textContent?.trim() === "Connect") as HTMLButtonElement | undefined;
+ expect(connectButton).toBeTruthy();
+
+ await act(async () => {
+ connectButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
+ });
+ await flushReact();
+
+ expect(mockCloudUpstreamsApi.startConnect).toHaveBeenCalledWith({
+ companyId: "company-1",
+ remoteUrl: "https://cloud.example/PAP/dashboard",
+ redirectUri: `${window.location.origin}/PAP/company/settings/cloud-upstream`,
+ });
+
+ await act(async () => {
+ root.unmount();
+ });
+ });
+
it("keeps retry only for failed or cancelled runs", async () => {
mockCloudUpstreamsApi.list.mockResolvedValue(stateWithRun(buildRun({ status: "failed" })));
const root = createRoot(container);
diff --git a/ui/src/pages/CloudUpstream.tsx b/ui/src/pages/CloudUpstream.tsx
index b99f4e80840..fe3735cf478 100644
--- a/ui/src/pages/CloudUpstream.tsx
+++ b/ui/src/pages/CloudUpstream.tsx
@@ -24,6 +24,7 @@ import { cloudUpstreamsApi } from "@/api/cloudUpstreams";
import { instanceSettingsApi } from "@/api/instanceSettings";
import { useBreadcrumbs } from "@/context/BreadcrumbContext";
import { useCompany } from "@/context/CompanyContext";
+import { applyCompanyPrefix } from "@/lib/company-routes";
import { Link, useLocation } from "@/lib/router";
import { queryKeys } from "@/lib/queryKeys";
@@ -101,6 +102,8 @@ export function CloudUpstream() {
const state = callbackParams.get("state");
const callbackError = callbackParams.get("error");
+ const settingsPath = applyCompanyPrefix("/company/settings/cloud-upstream", selectedCompany?.issuePrefix ?? null);
+
const finishMutation = useMutation({
mutationFn: (input: { pendingConnectionId: string; code: string; state: string }) =>
cloudUpstreamsApi.finishConnect(input),
@@ -109,7 +112,7 @@ export function CloudUpstream() {
setNotice("Cloud upstream connection approved.");
setActionError(null);
await invalidateUpstreams();
- window.history.replaceState(null, "", "/company/settings/cloud-upstream");
+ window.history.replaceState(null, "", settingsPath);
},
onError: (error) => setActionError(error instanceof Error ? error.message : "Failed to finish connection."),
});
@@ -135,7 +138,7 @@ export function CloudUpstream() {
cloudUpstreamsApi.startConnect({
companyId: selectedCompanyId!,
remoteUrl,
- redirectUri: `${window.location.origin}/company/settings/cloud-upstream`,
+ redirectUri: `${window.location.origin}${settingsPath}`,
}),
onSuccess: (result) => {
localStorage.setItem(PENDING_CONNECTION_KEY, result.pendingConnectionId);