Summary
When an invited person authenticates as an existing user (rather than creating a brand-new account through the invite link), the inviteId is never consumed. They end up belonging to zero organizations and are redirected into the "Create project" onboarding wizard instead of joining the org they were invited to.
Related: #363
Expected behavior
Accepting an invite should add the user to the inviting organization regardless of whether they are a new or existing user, and regardless of whether they go through sign-up or sign-in.
Actual behavior
The invite is only applied during new-account creation. Existing/returning users who authenticate are added to no organization, so index.tsx redirects them to /onboarding/project (the Create Project wizard).
Root cause
Membership is created exclusively by connectUserToOrganization({ user, inviteId }). It is called in only three places:
inviteUser mutation (packages/trpc/src/routers/organization.ts) — only when the invited email already has an account at invite time (userExists).
signUpEmail (packages/trpc/src/routers/auth.ts) — new email signup, with inviteId.
- OAuth
handleNewUser (apps/api/src/controllers/oauth-callback.controller.tsx) — new OAuth signup, with the inviteId cookie.
It is not called in:
handleExistingUser (apps/api/src/controllers/oauth-callback.controller.tsx) — the inviteId cookie is read at the top of githubCallback / googleCallback, but it is never passed into the handleExistingUser branch, so it's silently dropped.
signInEmail (packages/trpc/src/routers/auth.ts) — no inviteId parameter exists on this procedure.
In addition, the invite email links to /onboarding?inviteId=…, but that page's "Already have an account? Sign in" link points to /login with no inviteId, so a returning user loses the invite the moment they click it.
Steps to reproduce
- User A already has an OpenPanel account (or signs up independently).
- Admin invites User A's email to an organization.
- (If User A's account exists at this exact moment,
inviteUser auto-adds them and the bug is masked.)
- From the invite email, User A opens
/onboarding?inviteId=… and clicks "Sign in", or signs in with a GitHub/Google account whose email already exists.
- User A is authenticated via
handleExistingUser / signInEmail. The inviteId is ignored.
- With zero org memberships,
index.tsx redirects User A to /onboarding/project (Create Project wizard) instead of the invited org.
Suggested fix
Honor inviteId on the existing-user paths as well:
- Pass the
inviteId cookie into handleExistingUser and call connectUserToOrganization there.
- Add an optional
inviteId to signInEmail and consume it on success.
- Carry the
inviteId through the "Sign in" link on /onboarding so it isn't lost when a returning user authenticates.
Environment
Summary
When an invited person authenticates as an existing user (rather than creating a brand-new account through the invite link), the
inviteIdis never consumed. They end up belonging to zero organizations and are redirected into the "Create project" onboarding wizard instead of joining the org they were invited to.Related: #363
Expected behavior
Accepting an invite should add the user to the inviting organization regardless of whether they are a new or existing user, and regardless of whether they go through sign-up or sign-in.
Actual behavior
The invite is only applied during new-account creation. Existing/returning users who authenticate are added to no organization, so
index.tsxredirects them to/onboarding/project(the Create Project wizard).Root cause
Membership is created exclusively by
connectUserToOrganization({ user, inviteId }). It is called in only three places:inviteUsermutation (packages/trpc/src/routers/organization.ts) — only when the invited email already has an account at invite time (userExists).signUpEmail(packages/trpc/src/routers/auth.ts) — new email signup, withinviteId.handleNewUser(apps/api/src/controllers/oauth-callback.controller.tsx) — new OAuth signup, with theinviteIdcookie.It is not called in:
handleExistingUser(apps/api/src/controllers/oauth-callback.controller.tsx) — theinviteIdcookie is read at the top ofgithubCallback/googleCallback, but it is never passed into thehandleExistingUserbranch, so it's silently dropped.signInEmail(packages/trpc/src/routers/auth.ts) — noinviteIdparameter exists on this procedure.In addition, the invite email links to
/onboarding?inviteId=…, but that page's "Already have an account? Sign in" link points to/loginwith noinviteId, so a returning user loses the invite the moment they click it.Steps to reproduce
inviteUserauto-adds them and the bug is masked.)/onboarding?inviteId=…and clicks "Sign in", or signs in with a GitHub/Google account whose email already exists.handleExistingUser/signInEmail. TheinviteIdis ignored.index.tsxredirects User A to/onboarding/project(Create Project wizard) instead of the invited org.Suggested fix
Honor
inviteIdon the existing-user paths as well:inviteIdcookie intohandleExistingUserand callconnectUserToOrganizationthere.inviteIdtosignInEmailand consume it on success.inviteIdthrough the "Sign in" link on/onboardingso it isn't lost when a returning user authenticates.Environment