diff --git a/src/beacon/components/providers/auth-provider.tsx b/src/beacon/components/providers/auth-provider.tsx index d2e8b1387a..ccddcc7005 100644 --- a/src/beacon/components/providers/auth-provider.tsx +++ b/src/beacon/components/providers/auth-provider.tsx @@ -8,6 +8,12 @@ import authService from '@/services/api-service'; function AuthEffect({ children }: { children: ReactNode }) { const { data: session, status } = useSession(); const [handoffProcessed, setHandoffProcessed] = useState(false); + const [isHandlingHandoff, setIsHandlingHandoff] = useState(() => { + if (typeof window !== 'undefined') { + return window.location.hash.includes('token='); + } + return false; + }); useEffect(() => { if (status === 'authenticated' && session) { @@ -29,11 +35,15 @@ function AuthEffect({ children }: { children: ReactNode }) { role: (rawUser as any).privilege || 'user', }); } + + if (isHandlingHandoff) { + setIsHandlingHandoff(false); + } } else if (status === 'unauthenticated') { authService.setToken(null); authService.setUserData(null); } - }, [session, status]); + }, [session, status, isHandlingHandoff]); useEffect(() => { if (handoffProcessed) return; @@ -50,9 +60,22 @@ function AuthEffect({ children }: { children: ReactNode }) { redirect: true, callbackUrl: callbackUrl, }); + } else { + setIsHandlingHandoff(false); } }, [handoffProcessed]); + if (isHandlingHandoff || (status === 'unauthenticated' && typeof window !== 'undefined' && window.location.hash.includes('token='))) { + return ( +
+
+
+
+
Completing sign in...
+
+ ); + } + return <>{children}; } diff --git a/src/beacon/middleware.ts b/src/beacon/middleware.ts index 5d40d0fb04..5e05706dc4 100644 --- a/src/beacon/middleware.ts +++ b/src/beacon/middleware.ts @@ -37,9 +37,11 @@ export default withAuth( typeof token?.airqoExp === 'number' && Date.now() / 1000 > token.airqoExp; const isAuthenticated = !!token && !isAirqoTokenExpired; const isPublicRoute = PUBLIC_ROUTES.some(route => pathname.startsWith(route)) + const isOAuthCallback = request.nextUrl.searchParams.has('success') || request.nextUrl.searchParams.has('token') - // Redirect unauthenticated users to login - if (!isAuthenticated && !isPublicRoute) { + // Redirect unauthenticated users to login, unless it's an OAuth callback + // (We need to let the client handle OAuth callbacks to preserve the URL hash fragment) + if (!isAuthenticated && !isPublicRoute && !isOAuthCallback) { const loginUrl = new URL('/login', request.url) loginUrl.searchParams.set('from', pathname) return NextResponse.redirect(loginUrl)