From 01541b95cb6b33935c22128cbdc656e71a482159 Mon Sep 17 00:00:00 2001 From: Pamela Chia Date: Mon, 27 Jul 2026 23:02:05 +0800 Subject: [PATCH] fix(studio): keep organization_slug in oauth signup redirect (#48352) Email signups inside a partner OAuth flow lose `organization_slug` on the post-confirmation redirect: the OAuth branch in `SignUpForm` hand-builds the `/authorize` return URL from only `auth_id` and `token`, and the component's nuqs hook never reads the param at all. The consent screen uses `organization_slug` to preselect and lock the partner's requested org, so affected multi-org users land on an empty picker and the partner's requested org is silently dropped. The GitHub-OAuth signup path goes through `buildPathWithParams` and preserves the param, which is how this went unnoticed. I validated the drop in production traffic before fixing: joining sign-up pageviews to their post-signup `/authorize` return on the `auth_id` URL param (30d), 28 of 36 resolvable flows came back without the slug, and the 8 that kept it were the GitHub branch. ## To test Needs a partner OAuth authorize link that includes an org, opened signed-out: `/dashboard/authorize?auth_id=&organization_slug=` (note `auth_id` records expire quickly, so generate a fresh authorize request from an OAuth app). - [x] Sign up with email from that flow; after confirmation the redirect lands on `/authorize` with `organization_slug` still in the URL - [ ] Consent screen shows the requested org preselected and locked - [x] Same flow without `organization_slug` behaves as before (no trailing empty params in the redirect URL) ## Linear - fixes GROWTH-1031 ## Summary by CodeRabbit * **Bug Fixes** * Improved sign-up redirects during authentication flows by preserving invitation tokens and organization information. * Enhanced handling of sign-up links containing organization details. --- apps/studio/components/interfaces/SignIn/SignUpForm.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/studio/components/interfaces/SignIn/SignUpForm.tsx b/apps/studio/components/interfaces/SignIn/SignUpForm.tsx index 26cd71e1fcb..6f5e6a33df8 100644 --- a/apps/studio/components/interfaces/SignIn/SignUpForm.tsx +++ b/apps/studio/components/interfaces/SignIn/SignUpForm.tsx @@ -67,6 +67,7 @@ export const SignUpForm = () => { const [searchParams] = useQueryStates({ auth_id: parseAsString.withDefault(''), token: parseAsString.withDefault(''), + organization_slug: parseAsString.withDefault(''), }) const trackFunnelError = useTrackFunnelError() @@ -102,7 +103,12 @@ export const SignUpForm = () => { let redirectTo: string if (isInsideOAuthFlow) { - redirectTo = `${redirectUrlBase}/authorize?auth_id=${searchParams.auth_id}${searchParams.token && `&token=${searchParams.token}`}` + const authorizeParams = new URLSearchParams({ auth_id: searchParams.auth_id }) + if (searchParams.token) authorizeParams.set('token', searchParams.token) + if (searchParams.organization_slug) { + authorizeParams.set('organization_slug', searchParams.organization_slug) + } + redirectTo = `${redirectUrlBase}/authorize?${authorizeParams.toString()}` } else { // Use getRedirectToPath to handle redirect_to parameter and other query params const { returnTo } = router.query