diff --git a/app/api/auth/[...nextauth]/auth.config.ts b/app/api/auth/[...nextauth]/auth.config.ts index 06b80ef1a..378fb9d15 100644 --- a/app/api/auth/[...nextauth]/auth.config.ts +++ b/app/api/auth/[...nextauth]/auth.config.ts @@ -2,7 +2,6 @@ import type { NextAuthOptions } from 'next-auth'; import GoogleProvider from 'next-auth/providers/google'; import CredentialsProvider from 'next-auth/providers/credentials'; import { AuthService } from '@/services/auth.service'; -import { AuthSharingService } from '@/services/auth-sharing.service'; // Debug flag - set to true to enable detailed authentication logging const DEBUG_AUTH = true; @@ -37,7 +36,6 @@ export const authOptions: NextAuthOptions = { authToken: credentials.authToken, }; } catch (error) { - AuthSharingService.removeSharedAuthToken(); return promptInvalidCredentials(); } } diff --git a/app/api/auth/logout/route.ts b/app/api/auth/logout/route.ts index f5e671bba..e8acc3de5 100644 --- a/app/api/auth/logout/route.ts +++ b/app/api/auth/logout/route.ts @@ -1,5 +1,4 @@ import { NextResponse } from 'next/server'; -import { AuthSharingService } from '@/services/auth-sharing.service'; /** * Gets the cookie prefix based on the environment @@ -14,7 +13,7 @@ function getCookiePrefix(): string { } /** - * Handles the shared logout process by clearing the authentication cookies + * Handles the logout process by clearing the NextAuth cookies * and redirecting to the callback URL or the origin. * * @param request - The HTTP request object @@ -35,7 +34,6 @@ export async function GET(request: Request) { 'Set-Cookie': [ `${prefix}next-auth.session-token=; Expires=${expiredDate}; Path=/; HttpOnly; Secure`, `${prefix}next-auth.csrf-token=; Expires=${expiredDate}; Path=/; HttpOnly; Secure`, - `${AuthSharingService.getAuthCookieName()}=; Expires=${expiredDate}; Path=/; Domain=${AuthSharingService.getParentDomain()}; HttpOnly; Secure`, ].join(', '), }, }); diff --git a/components/AuthSharingWrapper.tsx b/components/AuthSharingWrapper.tsx deleted file mode 100644 index 065b2f9c0..000000000 --- a/components/AuthSharingWrapper.tsx +++ /dev/null @@ -1,88 +0,0 @@ -'use client'; - -import { Dialog, Transition } from '@headlessui/react'; -import { Fragment } from 'react'; -import { useSession, signIn } from 'next-auth/react'; -import { useEffect, useState } from 'react'; -import { AuthSharingService } from '@/services/auth-sharing.service'; -import { Loader2 } from 'lucide-react'; - -export function AuthSharingWrapper({ children }: { children: React.ReactNode }) { - const { data: session, status } = useSession(); - const [isChecking, setIsChecking] = useState(false); - - useEffect(() => { - const checkSharedToken = async () => { - // Only check if we're not authenticated - if (status === 'unauthenticated') { - const sharedToken = AuthSharingService.getSharedAuthToken(); - - if (sharedToken) { - setIsChecking(true); - await signIn('credentials', { - authToken: sharedToken, - redirect: false, - }); - AuthSharingService.removeSharedAuthToken(); - setIsChecking(false); - } - } - }; - - checkSharedToken(); - }, [status]); - - useEffect(() => { - if (session?.authToken) { - AuthSharingService.setSharedAuthToken(session.authToken); - } - }, [session?.authToken]); - - return ( - <> - {children} - - - {}}> - -
- - -
-
- - -
- - - Authorizing... - -

- We found your existing session. Signing you in automatically... -

-
-
-
-
-
-
-
- - ); -} diff --git a/components/menus/UserMenu.tsx b/components/menus/UserMenu.tsx index a417441cd..c7766ce11 100644 --- a/components/menus/UserMenu.tsx +++ b/components/menus/UserMenu.tsx @@ -13,7 +13,7 @@ import { VerifiedBadge } from '@/components/ui/VerifiedBadge'; import { SwipeableDrawer } from '@/components/ui/SwipeableDrawer'; import { ResearchCoinIcon } from '@/components/ui/icons/ResearchCoinIcon'; import Link from 'next/link'; -import { AuthSharingService } from '@/services/auth-sharing.service'; +import { signOut } from 'next-auth/react'; import { navigateToAuthorProfile } from '@/utils/navigation'; import { Button } from '@/components/ui/Button'; import { useVerification } from '@/contexts/VerificationContext'; @@ -245,11 +245,11 @@ export default function UserMenu({
AuthSharingService.signOutFromBothApps()} + onClick={() => signOut({ callbackUrl: '/' })} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); - AuthSharingService.signOutFromBothApps(); + signOut({ callbackUrl: '/' }); } }} tabIndex={0} @@ -417,7 +417,7 @@ export default function UserMenu({ )} AuthSharingService.signOutFromBothApps()} + onClick={() => signOut({ callbackUrl: '/' })} className="w-full px-4 py-2" >
diff --git a/components/providers/ClientProviders.tsx b/components/providers/ClientProviders.tsx index 234047c9c..d0b3cd7f6 100644 --- a/components/providers/ClientProviders.tsx +++ b/components/providers/ClientProviders.tsx @@ -14,7 +14,6 @@ import { OnchainProvider } from '@/contexts/OnchainContext'; import { FollowProvider } from '@/contexts/FollowContext'; import { ClickProvider } from '@/contexts/ClickContext'; import { NavigationProvider } from '@/contexts/NavigationContext'; -import { AuthSharingWrapper } from '@/components/AuthSharingWrapper'; import { VerificationProvider } from '@/contexts/VerificationContext'; import SignupModalContainer from '@/components/modals/SignupModalContainer'; import { ShareModalProvider } from '@/contexts/ShareContext'; @@ -40,35 +39,33 @@ export function ClientProviders({ children, session }: ClientProvidersProps) { - - - - - - - - - - - - - - {children} - - - - - - - - - - - - - - - + + + + + + + + + + + + + {children} + + + + + + + + + + + + + + diff --git a/contexts/UserContext.tsx b/contexts/UserContext.tsx index 7a9c7d8ef..f608d7363 100644 --- a/contexts/UserContext.tsx +++ b/contexts/UserContext.tsx @@ -1,10 +1,9 @@ 'use client'; import { createContext, useContext, ReactNode, useState, useEffect } from 'react'; -import { useSession } from 'next-auth/react'; +import { useSession, signOut } from 'next-auth/react'; import { AuthError, AuthService } from '@/services/auth.service'; import type { User } from '@/types/user'; -import { AuthSharingService } from '@/services/auth-sharing.service'; import AnalyticsService from '@/services/analytics.service'; import { Experiment } from '@/utils/experiment'; @@ -39,7 +38,8 @@ export function UserProvider({ children }: { children: ReactNode }) { } catch (err) { if (err instanceof AuthError) { if (err.code === 401) { - await AuthSharingService.signOutFromBothApps(); + AnalyticsService.clearUserSession(); + await signOut({ callbackUrl: '/' }); } } setError(err instanceof Error ? err : new Error('Failed to load user data')); diff --git a/services/auth-sharing.service.ts b/services/auth-sharing.service.ts deleted file mode 100644 index 902372c6c..000000000 --- a/services/auth-sharing.service.ts +++ /dev/null @@ -1,97 +0,0 @@ -import Cookies from 'js-cookie'; -import { AUTH_TOKEN } from '@/config/constants'; -import { signOut } from 'next-auth/react'; -import AnalyticsService from './analytics.service'; - -/** - * Service to handle authentication sharing between the old and new ResearchHub applications - */ -export class AuthSharingService { - private static readonly ENV_PREFIX = (() => { - switch (process.env.NEXT_PUBLIC_VERCEL_ENV) { - case 'production': - return 'prod'; - case 'preview': - case 'development': - return 'stg'; - default: - return 'local'; - } - })(); - - private static readonly AUTH_COOKIE_NAME = `${AuthSharingService.ENV_PREFIX}.${AUTH_TOKEN}`; - - /** - * The parent domain that both applications share (for cookie sharing) - */ - private static readonly PARENT_DOMAIN = (() => { - switch (process.env.NEXT_PUBLIC_VERCEL_ENV) { - case 'production': - return '.researchhub.com'; - case 'preview': - case 'development': - return '.staging.researchhub.com'; - default: - return 'localhost'; - } - })(); - - public static getAuthCookieName(): string { - return this.AUTH_COOKIE_NAME; - } - - public static getParentDomain(): string { - return this.PARENT_DOMAIN; - } - - /** - * Sets the authentication token in a shared cookie that can be accessed by both domains - * @param token The authentication token to store - * @param expiryDays Number of days until the cookie expires (default: 14) - */ - static setSharedAuthToken(token: string, expiryDays = 14): void { - const cookieOptions: Cookies.CookieAttributes = { - expires: new Date(Date.now() + expiryDays * 24 * 60 * 60 * 1000), - path: '/', - domain: this.PARENT_DOMAIN, - secure: process.env.NEXT_PUBLIC_VERCEL_ENV !== undefined, - sameSite: 'lax', - }; - - Cookies.set(this.AUTH_COOKIE_NAME, token, cookieOptions); - } - - /** - * Gets the authentication token from the shared cookie - * @returns The authentication token or null if not found - */ - static getSharedAuthToken(): string | null { - const token = Cookies.get(this.AUTH_COOKIE_NAME); - - return token || null; - } - - /** - * Removes the shared authentication token (logout) - */ - static removeSharedAuthToken(): void { - Cookies.remove(this.AUTH_COOKIE_NAME, { - domain: this.PARENT_DOMAIN, - path: '/', - }); - } - - /** - * Signs out from both applications by removing the token - * and calling the signOut method - */ - static async signOutFromBothApps(): Promise { - // First remove the shared cookie - this.removeSharedAuthToken(); - // Reset analytics session - AnalyticsService.clearUserSession(); - - // Finally, sign out from NextAuth - await signOut({ callbackUrl: '/' }); - } -}