From 35f53ca4365c40d0eecd8fb6843f3c28eb769f19 Mon Sep 17 00:00:00 2001 From: mcull Date: Mon, 29 Sep 2025 16:22:34 -0700 Subject: [PATCH] fix(invite): route unauthenticated join through /auth/callback and honor returnTo after profile completion\n\n- CollectionDetailClient: On 401 join, redirect to /auth/signin?callbackUrl=/auth/callback so invite cookies are consumed and membership is created\n- ProfileCreationHandler: Support returnTo param to send users back to the invited library after finishing profile\n- Outcome: invite->guest->join->auth now consistently converts to member and routes to profile then back to library --- .../profile/create/ProfileCreationHandler.tsx | 17 ++++++++++++++--- src/components/CollectionDetailClient.tsx | 6 ++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/app/profile/create/ProfileCreationHandler.tsx b/src/app/profile/create/ProfileCreationHandler.tsx index d89be75..bcfe563 100644 --- a/src/app/profile/create/ProfileCreationHandler.tsx +++ b/src/app/profile/create/ProfileCreationHandler.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useRouter } from 'next/navigation'; +import { useRouter, useSearchParams } from 'next/navigation'; import { useEffect, useState } from 'react'; import { @@ -33,6 +33,7 @@ export function ProfileCreationHandler({ const [loadingUser, setLoadingUser] = useState(false); const [userId, setUserId] = useState(userIdProp); const [user, setUser] = useState(userProp); + const searchParams = useSearchParams(); // Ensure we have the current user and id; redirect if unauthorized useEffect(() => { @@ -135,8 +136,18 @@ export function ProfileCreationHandler({ // Clear any draft data localStorage.removeItem('profile-wizard-draft'); - // Redirect to lobby with welcome message - router.push('/stacks?welcome=true'); + // Redirect to intended destination if provided, otherwise to lobby + const returnTo = searchParams?.get('returnTo'); + if (returnTo) { + try { + const decoded = decodeURIComponent(returnTo); + router.push(decoded); + } catch { + router.push('/stacks?welcome=true'); + } + } else { + router.push('/stacks?welcome=true'); + } router.refresh(); // Refresh to update session data } catch (error) { alert( diff --git a/src/components/CollectionDetailClient.tsx b/src/components/CollectionDetailClient.tsx index 5b8759b..c5d38ee 100644 --- a/src/components/CollectionDetailClient.tsx +++ b/src/components/CollectionDetailClient.tsx @@ -402,8 +402,10 @@ export function CollectionDetailClient({ storedInviteToken ); } - const returnTo = encodeURIComponent(`/collection/${collectionId}`); - window.location.href = `/api/auth/signin?callbackUrl=${returnTo}`; + // Always route through our smart auth callback so invite consumption + // and profile completion logic can run before landing on the library + const callback = encodeURIComponent('/auth/callback'); + window.location.href = `/auth/signin?callbackUrl=${callback}`; } else { const data = await res.json().catch(() => ({})); console.error('[CollectionDetailClient] join failed', {