fix(hub): redirect Feishu OAuth default login to org admin SPA

Default returnTo=/admin previously landed on the static complete page
meant for chat onboarding; send users to /admin/org/:slug instead.
This commit is contained in:
2026-07-15 14:29:42 +08:00
parent 46ce942aec
commit 11de9e81db
2 changed files with 4 additions and 9 deletions
+3 -1
View File
@@ -433,8 +433,10 @@ async function resolvePostLoginRedirect(
}); });
if (intended === null) return "/admin?error=not_an_active_org_member"; if (intended === null) return "/admin?error=not_an_active_org_member";
const orgRoot = `/admin/org/${intended.organization.slug}`; const orgRoot = `/admin/org/${intended.organization.slug}`;
// Default / missing returnTo sanitizes to "/admin". Always land in the org
// admin SPA (not the legacy static "close this tab" complete page).
if (returnTo === "/admin") { if (returnTo === "/admin") {
return `/auth/feishu/complete?org=${encodeURIComponent(intended.organization.name)}`; return orgRoot;
} }
return returnTo === orgRoot || returnTo.startsWith(`${orgRoot}/`) ? returnTo : orgRoot; return returnTo === orgRoot || returnTo.startsWith(`${orgRoot}/`) ? returnTo : orgRoot;
} }
+1 -8
View File
@@ -346,14 +346,7 @@ describe("admin auth + org API guards", () => {
headers: { cookie: cookiePair(defaultStart.headers["set-cookie"], OAUTH_STATE_COOKIE_NAME) }, headers: { cookie: cookiePair(defaultStart.headers["set-cookie"], OAUTH_STATE_COOKIE_NAME) },
}); });
expect(defaultCallback.statusCode).toBe(302); expect(defaultCallback.statusCode).toBe(302);
expect(defaultCallback.headers.location).toBe( expect(defaultCallback.headers.location).toBe("/admin/org/test-default");
"/auth/feishu/complete?org=Test%20Default%20Organization",
);
const complete = await app.inject({ method: "GET", url: defaultCallback.headers.location! });
expect(complete.statusCode).toBe(200);
expect(complete.headers["content-type"]).toContain("text/html");
expect(complete.body).toContain("登录并加入组织成功");
expect(complete.body).toContain("Test Default Organization");
await connections.disable({ organizationId: DEFAULT_ORG_ID, actorUserId: "scoped-owner" }); await connections.disable({ organizationId: DEFAULT_ORG_ID, actorUserId: "scoped-owner" });
const revoked = await app.inject({ method: "GET", url: "/api/me", headers: { cookie: sessionCookie } }); const revoked = await app.inject({ method: "GET", url: "/api/me", headers: { cookie: sessionCookie } });