From 77a510b29e9781d1b8c22c0c047d702ef48974f3 Mon Sep 17 00:00:00 2001 From: dancer <144584931+dancer@users.noreply.github.com> Date: Fri, 13 Mar 2026 23:08:30 +0000 Subject: [PATCH 1/2] fix(auth): migrate from next-auth to better-auth --- .env.example | 3 +- app/(auth)/actions.ts | 37 +- app/(auth)/api/auth/[...nextauth]/route.ts | 1 - app/(auth)/api/auth/guest/route.ts | 21 - app/(auth)/auth.config.ts | 14 - app/(auth)/auth.ts | 94 ----- app/(auth)/login/page.tsx | 8 +- app/(auth)/register/page.tsx | 8 +- app/(chat)/api/auth/[...all]/route.ts | 4 + app/(chat)/api/chat/route.ts | 8 +- app/(chat)/api/document/route.ts | 8 +- app/(chat)/api/files/upload/route.ts | 4 +- app/(chat)/api/history/route.ts | 6 +- app/(chat)/api/suggestions/route.ts | 4 +- app/(chat)/api/vote/route.ts | 6 +- app/(chat)/chat/[id]/page.tsx | 6 +- app/(chat)/layout.tsx | 4 +- app/layout.tsx | 3 +- components/app-sidebar.tsx | 4 +- components/chat.tsx | 2 +- components/multimodal-input.tsx | 16 +- components/sidebar-history.tsx | 12 +- components/sidebar-user-nav.tsx | 21 +- components/sign-out-form.tsx | 11 +- lib/ai/entitlements.ts | 2 +- lib/ai/tools/create-document.ts | 4 +- lib/ai/tools/request-suggestions.ts | 4 +- lib/ai/tools/update-document.ts | 4 +- lib/artifacts/server.ts | 6 +- lib/auth.ts | 67 +++ lib/client.ts | 9 + lib/constants.ts | 6 - lib/db/migrations/0010_better_auth.sql | 49 +++ lib/db/queries.ts | 47 +-- lib/db/schema.ts | 52 +++ lib/db/utils.ts | 16 - next.config.ts | 1 - package.json | 2 +- pnpm-lock.yaml | 462 +++++++++++++++++---- proxy.ts | 22 +- 40 files changed, 671 insertions(+), 387 deletions(-) delete mode 100644 app/(auth)/api/auth/[...nextauth]/route.ts delete mode 100644 app/(auth)/api/auth/guest/route.ts delete mode 100644 app/(auth)/auth.config.ts delete mode 100644 app/(auth)/auth.ts create mode 100644 app/(chat)/api/auth/[...all]/route.ts create mode 100644 lib/auth.ts create mode 100644 lib/client.ts create mode 100644 lib/db/migrations/0010_better_auth.sql delete mode 100644 lib/db/utils.ts diff --git a/.env.example b/.env.example index 42bdcf2c91..50c7f12f7f 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,6 @@ # Generate a random secret: https://generate-secret.vercel.app/32 or `openssl rand -base64 32` -AUTH_SECRET=**** +BETTER_AUTH_SECRET=**** +BETTER_AUTH_URL=**** # The following keys below are automatically created and # added to your environment when you deploy on Vercel diff --git a/app/(auth)/actions.ts b/app/(auth)/actions.ts index 024ff518ed..fb817ad50f 100644 --- a/app/(auth)/actions.ts +++ b/app/(auth)/actions.ts @@ -1,10 +1,7 @@ "use server"; import { z } from "zod"; - -import { createUser, getUser } from "@/lib/db/queries"; - -import { signIn } from "./auth"; +import { auth } from "@/lib/auth"; const authFormSchema = z.object({ email: z.string().email(), @@ -25,10 +22,11 @@ export const login = async ( password: formData.get("password"), }); - await signIn("credentials", { - email: validatedData.email, - password: validatedData.password, - redirect: false, + await auth.api.signInEmail({ + body: { + email: validatedData.email, + password: validatedData.password, + }, }); return { status: "success" }; @@ -61,17 +59,17 @@ export const register = async ( password: formData.get("password"), }); - const [user] = await getUser(validatedData.email); + const result = await auth.api.signUpEmail({ + body: { + email: validatedData.email, + password: validatedData.password, + name: validatedData.email, + }, + }); - if (user) { - return { status: "user_exists" } as RegisterActionState; + if (!result) { + return { status: "failed" }; } - await createUser(validatedData.email, validatedData.password); - await signIn("credentials", { - email: validatedData.email, - password: validatedData.password, - redirect: false, - }); return { status: "success" }; } catch (error) { @@ -79,6 +77,11 @@ export const register = async ( return { status: "invalid_data" }; } + const message = error instanceof Error ? error.message : ""; + if (message.includes("already exists") || message.includes("UNIQUE")) { + return { status: "user_exists" }; + } + return { status: "failed" }; } }; diff --git a/app/(auth)/api/auth/[...nextauth]/route.ts b/app/(auth)/api/auth/[...nextauth]/route.ts deleted file mode 100644 index d104b65e6d..0000000000 --- a/app/(auth)/api/auth/[...nextauth]/route.ts +++ /dev/null @@ -1 +0,0 @@ -export { GET, POST } from "@/app/(auth)/auth"; diff --git a/app/(auth)/api/auth/guest/route.ts b/app/(auth)/api/auth/guest/route.ts deleted file mode 100644 index dca565c5ab..0000000000 --- a/app/(auth)/api/auth/guest/route.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { NextResponse } from "next/server"; -import { getToken } from "next-auth/jwt"; -import { signIn } from "@/app/(auth)/auth"; -import { isDevelopmentEnvironment } from "@/lib/constants"; - -export async function GET(request: Request) { - const { searchParams } = new URL(request.url); - const redirectUrl = searchParams.get("redirectUrl") || "/"; - - const token = await getToken({ - req: request, - secret: process.env.AUTH_SECRET, - secureCookie: !isDevelopmentEnvironment, - }); - - if (token) { - return NextResponse.redirect(new URL("/", request.url)); - } - - return signIn("guest", { redirect: true, redirectTo: redirectUrl }); -} diff --git a/app/(auth)/auth.config.ts b/app/(auth)/auth.config.ts deleted file mode 100644 index b03fad0431..0000000000 --- a/app/(auth)/auth.config.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { NextAuthConfig } from "next-auth"; - -export const authConfig = { - basePath: "/api/auth", - pages: { - signIn: "/login", - newUser: "/", - }, - providers: [ - // added later in auth.ts since it requires bcrypt which is only compatible with Node.js - // while this file is also used in non-Node.js environments - ], - callbacks: {}, -} satisfies NextAuthConfig; diff --git a/app/(auth)/auth.ts b/app/(auth)/auth.ts deleted file mode 100644 index ffd72be0bf..0000000000 --- a/app/(auth)/auth.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { compare } from "bcrypt-ts"; -import NextAuth, { type DefaultSession } from "next-auth"; -import type { DefaultJWT } from "next-auth/jwt"; -import Credentials from "next-auth/providers/credentials"; -import { DUMMY_PASSWORD } from "@/lib/constants"; -import { createGuestUser, getUser } from "@/lib/db/queries"; -import { authConfig } from "./auth.config"; - -export type UserType = "guest" | "regular"; - -declare module "next-auth" { - interface Session extends DefaultSession { - user: { - id: string; - type: UserType; - } & DefaultSession["user"]; - } - - interface User { - id?: string; - email?: string | null; - type: UserType; - } -} - -declare module "next-auth/jwt" { - interface JWT extends DefaultJWT { - id: string; - type: UserType; - } -} - -export const { - handlers: { GET, POST }, - auth, - signIn, - signOut, -} = NextAuth({ - ...authConfig, - providers: [ - Credentials({ - credentials: {}, - async authorize({ email, password }: any) { - const users = await getUser(email); - - if (users.length === 0) { - await compare(password, DUMMY_PASSWORD); - return null; - } - - const [user] = users; - - if (!user.password) { - await compare(password, DUMMY_PASSWORD); - return null; - } - - const passwordsMatch = await compare(password, user.password); - - if (!passwordsMatch) { - return null; - } - - return { ...user, type: "regular" }; - }, - }), - Credentials({ - id: "guest", - credentials: {}, - async authorize() { - const [guestUser] = await createGuestUser(); - return { ...guestUser, type: "guest" }; - }, - }), - ], - callbacks: { - jwt({ token, user }) { - if (user) { - token.id = user.id as string; - token.type = user.type; - } - - return token; - }, - session({ session, token }) { - if (session.user) { - session.user.id = token.id; - session.user.type = token.type; - } - - return session; - }, - }, -}); diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx index 666feee365..1e32e62d31 100644 --- a/app/(auth)/login/page.tsx +++ b/app/(auth)/login/page.tsx @@ -2,12 +2,12 @@ import Link from "next/link"; import { useRouter } from "next/navigation"; -import { useSession } from "next-auth/react"; import { useActionState, useEffect, useState } from "react"; import { AuthForm } from "@/components/auth-form"; import { SubmitButton } from "@/components/submit-button"; import { toast } from "@/components/toast"; +import { useSession } from "@/lib/client"; import { type LoginActionState, login } from "../actions"; export default function Page() { @@ -23,9 +23,9 @@ export default function Page() { } ); - const { update: updateSession } = useSession(); + const { refetch } = useSession(); - // biome-ignore lint/correctness/useExhaustiveDependencies: router and updateSession are stable refs + // biome-ignore lint/correctness/useExhaustiveDependencies: router and refetch are stable refs useEffect(() => { if (state.status === "failed") { toast({ @@ -39,7 +39,7 @@ export default function Page() { }); } else if (state.status === "success") { setIsSuccessful(true); - updateSession(); + refetch(); router.refresh(); } }, [state.status]); diff --git a/app/(auth)/register/page.tsx b/app/(auth)/register/page.tsx index ff2f1e80f1..475af5b222 100644 --- a/app/(auth)/register/page.tsx +++ b/app/(auth)/register/page.tsx @@ -2,11 +2,11 @@ import Link from "next/link"; import { useRouter } from "next/navigation"; -import { useSession } from "next-auth/react"; import { useActionState, useEffect, useState } from "react"; import { AuthForm } from "@/components/auth-form"; import { SubmitButton } from "@/components/submit-button"; import { toast } from "@/components/toast"; +import { useSession } from "@/lib/client"; import { type RegisterActionState, register } from "../actions"; export default function Page() { @@ -22,9 +22,9 @@ export default function Page() { } ); - const { update: updateSession } = useSession(); + const { refetch } = useSession(); - // biome-ignore lint/correctness/useExhaustiveDependencies: router and updateSession are stable refs + // biome-ignore lint/correctness/useExhaustiveDependencies: router and refetch are stable refs useEffect(() => { if (state.status === "user_exists") { toast({ type: "error", description: "Account already exists!" }); @@ -39,7 +39,7 @@ export default function Page() { toast({ type: "success", description: "Account created successfully!" }); setIsSuccessful(true); - updateSession(); + refetch(); router.refresh(); } }, [state.status]); diff --git a/app/(chat)/api/auth/[...all]/route.ts b/app/(chat)/api/auth/[...all]/route.ts new file mode 100644 index 0000000000..5b67b0644b --- /dev/null +++ b/app/(chat)/api/auth/[...all]/route.ts @@ -0,0 +1,4 @@ +import { auth } from "@/lib/auth"; +import { toNextJsHandler } from "better-auth/next-js"; + +export const { GET, POST } = toNextJsHandler(auth); diff --git a/app/(chat)/api/chat/route.ts b/app/(chat)/api/chat/route.ts index cf4138fd07..8c8d75c5ec 100644 --- a/app/(chat)/api/chat/route.ts +++ b/app/(chat)/api/chat/route.ts @@ -10,7 +10,7 @@ import { import { checkBotId } from "botid/server"; import { after } from "next/server"; import { createResumableStreamContext } from "resumable-stream"; -import { auth, type UserType } from "@/app/(auth)/auth"; +import { getSession, getUserType, type UserType } from "@/lib/auth"; import { entitlementsByUserType } from "@/lib/ai/entitlements"; import { allowedModelIds } from "@/lib/ai/models"; import { type RequestHints, systemPrompt } from "@/lib/ai/prompts"; @@ -67,7 +67,7 @@ export async function POST(request: Request) { const [, session] = await Promise.all([ checkBotId().catch(() => null), - auth(), + getSession(), ]); if (!session?.user) { @@ -80,7 +80,7 @@ export async function POST(request: Request) { await checkIpRateLimit(ipAddress(request)); - const userType: UserType = session.user.type; + const userType: UserType = getUserType(session.user); const messageCount = await getMessageCountByUserId({ id: session.user.id, @@ -295,7 +295,7 @@ export async function DELETE(request: Request) { return new ChatbotError("bad_request:api").toResponse(); } - const session = await auth(); + const session = await getSession(); if (!session?.user) { return new ChatbotError("unauthorized:chat").toResponse(); diff --git a/app/(chat)/api/document/route.ts b/app/(chat)/api/document/route.ts index fe912a1e61..a5963e4013 100644 --- a/app/(chat)/api/document/route.ts +++ b/app/(chat)/api/document/route.ts @@ -1,4 +1,4 @@ -import { auth } from "@/app/(auth)/auth"; +import { getSession } from "@/lib/auth"; import type { ArtifactKind } from "@/components/artifact"; import { deleteDocumentsByIdAfterTimestamp, @@ -18,7 +18,7 @@ export async function GET(request: Request) { ).toResponse(); } - const session = await auth(); + const session = await getSession(); if (!session?.user) { return new ChatbotError("unauthorized:document").toResponse(); @@ -50,7 +50,7 @@ export async function POST(request: Request) { ).toResponse(); } - const session = await auth(); + const session = await getSession(); if (!session?.user) { return new ChatbotError("not_found:document").toResponse(); @@ -103,7 +103,7 @@ export async function DELETE(request: Request) { ).toResponse(); } - const session = await auth(); + const session = await getSession(); if (!session?.user) { return new ChatbotError("unauthorized:document").toResponse(); diff --git a/app/(chat)/api/files/upload/route.ts b/app/(chat)/api/files/upload/route.ts index 4e4e4f3caf..b81566d5b2 100644 --- a/app/(chat)/api/files/upload/route.ts +++ b/app/(chat)/api/files/upload/route.ts @@ -2,7 +2,7 @@ import { put } from "@vercel/blob"; import { NextResponse } from "next/server"; import { z } from "zod"; -import { auth } from "@/app/(auth)/auth"; +import { getSession } from "@/lib/auth"; // Use Blob instead of File since File is not available in Node.js environment const FileSchema = z.object({ @@ -18,7 +18,7 @@ const FileSchema = z.object({ }); export async function POST(request: Request) { - const session = await auth(); + const session = await getSession(); if (!session) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); diff --git a/app/(chat)/api/history/route.ts b/app/(chat)/api/history/route.ts index f8b7f9ed48..4e80080b39 100644 --- a/app/(chat)/api/history/route.ts +++ b/app/(chat)/api/history/route.ts @@ -1,5 +1,5 @@ import type { NextRequest } from "next/server"; -import { auth } from "@/app/(auth)/auth"; +import { getSession } from "@/lib/auth"; import { deleteAllChatsByUserId, getChatsByUserId } from "@/lib/db/queries"; import { ChatbotError } from "@/lib/errors"; @@ -17,7 +17,7 @@ export async function GET(request: NextRequest) { ).toResponse(); } - const session = await auth(); + const session = await getSession(); if (!session?.user) { return new ChatbotError("unauthorized:chat").toResponse(); @@ -34,7 +34,7 @@ export async function GET(request: NextRequest) { } export async function DELETE() { - const session = await auth(); + const session = await getSession(); if (!session?.user) { return new ChatbotError("unauthorized:chat").toResponse(); diff --git a/app/(chat)/api/suggestions/route.ts b/app/(chat)/api/suggestions/route.ts index 303f45ed26..babfb9d58d 100644 --- a/app/(chat)/api/suggestions/route.ts +++ b/app/(chat)/api/suggestions/route.ts @@ -1,4 +1,4 @@ -import { auth } from "@/app/(auth)/auth"; +import { getSession } from "@/lib/auth"; import { getSuggestionsByDocumentId } from "@/lib/db/queries"; import { ChatbotError } from "@/lib/errors"; @@ -13,7 +13,7 @@ export async function GET(request: Request) { ).toResponse(); } - const session = await auth(); + const session = await getSession(); if (!session?.user) { return new ChatbotError("unauthorized:suggestions").toResponse(); diff --git a/app/(chat)/api/vote/route.ts b/app/(chat)/api/vote/route.ts index 4a2e4bf412..4f69e2cad1 100644 --- a/app/(chat)/api/vote/route.ts +++ b/app/(chat)/api/vote/route.ts @@ -1,4 +1,4 @@ -import { auth } from "@/app/(auth)/auth"; +import { getSession } from "@/lib/auth"; import { getChatById, getVotesByChatId, voteMessage } from "@/lib/db/queries"; import { ChatbotError } from "@/lib/errors"; @@ -13,7 +13,7 @@ export async function GET(request: Request) { ).toResponse(); } - const session = await auth(); + const session = await getSession(); if (!session?.user) { return new ChatbotError("unauthorized:vote").toResponse(); @@ -49,7 +49,7 @@ export async function PATCH(request: Request) { ).toResponse(); } - const session = await auth(); + const session = await getSession(); if (!session?.user) { return new ChatbotError("unauthorized:vote").toResponse(); diff --git a/app/(chat)/chat/[id]/page.tsx b/app/(chat)/chat/[id]/page.tsx index 1bd5693765..ecbd5fcd64 100644 --- a/app/(chat)/chat/[id]/page.tsx +++ b/app/(chat)/chat/[id]/page.tsx @@ -2,7 +2,7 @@ import { cookies } from "next/headers"; import { notFound, redirect } from "next/navigation"; import { Suspense } from "react"; -import { auth } from "@/app/(auth)/auth"; +import { getSession } from "@/lib/auth"; import { Chat } from "@/components/chat"; import { DataStreamHandler } from "@/components/data-stream-handler"; import { DEFAULT_CHAT_MODEL } from "@/lib/ai/models"; @@ -25,10 +25,10 @@ async function ChatPage({ params }: { params: Promise<{ id: string }> }) { redirect("/"); } - const session = await auth(); + const session = await getSession(); if (!session) { - redirect("/api/auth/guest"); + redirect("/login"); } if (chat.visibility === "private") { diff --git a/app/(chat)/layout.tsx b/app/(chat)/layout.tsx index f6d4f5feea..aca0f0fe37 100644 --- a/app/(chat)/layout.tsx +++ b/app/(chat)/layout.tsx @@ -4,7 +4,7 @@ import { Suspense } from "react"; import { AppSidebar } from "@/components/app-sidebar"; import { DataStreamProvider } from "@/components/data-stream-provider"; import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar"; -import { auth } from "../(auth)/auth"; +import { getSession } from "@/lib/auth"; export default function Layout({ children }: { children: React.ReactNode }) { return ( @@ -23,7 +23,7 @@ export default function Layout({ children }: { children: React.ReactNode }) { } async function SidebarWrapper({ children }: { children: React.ReactNode }) { - const [session, cookieStore] = await Promise.all([auth(), cookies()]); + const [session, cookieStore] = await Promise.all([getSession(), cookies()]); const isCollapsed = cookieStore.get("sidebar_state")?.value !== "true"; return ( diff --git a/app/layout.tsx b/app/layout.tsx index 3179bac86f..348a4d696d 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -6,7 +6,6 @@ import { ThemeProvider } from "@/components/theme-provider"; import "katex/dist/katex.min.css"; import "./globals.css"; -import { SessionProvider } from "next-auth/react"; export const metadata: Metadata = { metadataBase: new URL("https://chat.vercel.ai"), @@ -81,7 +80,7 @@ export default function RootLayout({ enableSystem > - {children} + {children} diff --git a/components/app-sidebar.tsx b/components/app-sidebar.tsx index eaba764fef..527c5dc11e 100644 --- a/components/app-sidebar.tsx +++ b/components/app-sidebar.tsx @@ -2,7 +2,7 @@ import Link from "next/link"; import { useRouter } from "next/navigation"; -import type { User } from "next-auth"; +import type { AuthUser } from "@/lib/auth"; import { useState } from "react"; import { toast } from "sonner"; import { useSWRConfig } from "swr"; @@ -34,7 +34,7 @@ import { } from "./ui/alert-dialog"; import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip"; -export function AppSidebar({ user }: { user: User | undefined }) { +export function AppSidebar({ user }: { user: AuthUser | undefined }) { const router = useRouter(); const { setOpenMobile } = useSidebar(); const { mutate } = useSWRConfig(); diff --git a/components/chat.tsx b/components/chat.tsx index ffc264c020..022983ff67 100644 --- a/components/chat.tsx +++ b/components/chat.tsx @@ -176,7 +176,7 @@ export function Chat({ }, [query, sendMessage, hasAppendedQuery, id]); const { data: votes } = useSWR( - messages.length >= 2 + !isReadonly && messages.length >= 2 ? `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/vote?chatId=${id}` : null, fetcher diff --git a/components/multimodal-input.tsx b/components/multimodal-input.tsx index d18f12f5ea..5a99157b07 100644 --- a/components/multimodal-input.tsx +++ b/components/multimodal-input.tsx @@ -33,6 +33,7 @@ import { modelsByProvider, } from "@/lib/ai/models"; import type { Attachment, ChatMessage } from "@/lib/types"; +import { signIn, useSession } from "@/lib/client"; import { cn } from "@/lib/utils"; import { PromptInput, @@ -86,6 +87,8 @@ function PureMultimodalInput({ }) { const textareaRef = useRef(null); const { width } = useWindowSize(); + const { data: session, refetch: refetchSession } = useSession(); + const [isSigningIn, setIsSigningIn] = useState(false); const hasAutoFocused = useRef(false); useEffect(() => { @@ -304,10 +307,21 @@ function PureMultimodalInput({ { + onSubmit={async () => { if (!input.trim() && attachments.length === 0) { return; } + if (!session && !isSigningIn) { + setIsSigningIn(true); + const { error } = await signIn.anonymous(); + if (error) { + toast.error("Failed to create session, please try again!"); + setIsSigningIn(false); + return; + } + await refetchSession(); + setIsSigningIn(false); + } if (status === "ready") { submitForm(); } else { diff --git a/components/sidebar-history.tsx b/components/sidebar-history.tsx index 4aecfe24bf..2ca6619300 100644 --- a/components/sidebar-history.tsx +++ b/components/sidebar-history.tsx @@ -3,7 +3,7 @@ import { isToday, isYesterday, subMonths, subWeeks } from "date-fns"; import { motion } from "framer-motion"; import { usePathname, useRouter } from "next/navigation"; -import type { User } from "next-auth"; +import type { AuthUser } from "@/lib/auth"; import { useState } from "react"; import { toast } from "sonner"; import useSWRInfinite from "swr/infinite"; @@ -97,7 +97,7 @@ export function getChatHistoryPaginationKey( return `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/history?ending_before=${firstChatFromPage.id}&limit=${PAGE_SIZE}`; } -export function SidebarHistory({ user }: { user: User | undefined }) { +export function SidebarHistory({ user }: { user: AuthUser | undefined }) { const { setOpenMobile } = useSidebar(); const pathname = usePathname(); const id = pathname?.startsWith("/chat/") ? pathname.split("/")[2] : null; @@ -108,9 +108,11 @@ export function SidebarHistory({ user }: { user: User | undefined }) { isValidating, isLoading, mutate, - } = useSWRInfinite(getChatHistoryPaginationKey, fetcher, { - fallbackData: [], - }); + } = useSWRInfinite( + user ? getChatHistoryPaginationKey : () => null, + fetcher, + { fallbackData: [] }, + ); const router = useRouter(); const [deleteId, setDeleteId] = useState(null); diff --git a/components/sidebar-user-nav.tsx b/components/sidebar-user-nav.tsx index 81b52d8d10..4cd65b6c68 100644 --- a/components/sidebar-user-nav.tsx +++ b/components/sidebar-user-nav.tsx @@ -3,8 +3,6 @@ import { ChevronUp } from "lucide-react"; import Image from "next/image"; import { useRouter } from "next/navigation"; -import type { User } from "next-auth"; -import { signOut, useSession } from "next-auth/react"; import { useTheme } from "next-themes"; import { DropdownMenu, @@ -18,23 +16,23 @@ import { SidebarMenuButton, SidebarMenuItem, } from "@/components/ui/sidebar"; -import { guestRegex } from "@/lib/constants"; +import { signOut, useSession } from "@/lib/client"; import { LoaderIcon } from "./icons"; import { toast } from "./toast"; -export function SidebarUserNav({ user }: { user: User }) { +export function SidebarUserNav({ user }: { user: { email?: string | null; isAnonymous?: boolean | null } }) { const router = useRouter(); - const { data, status } = useSession(); + const { data, isPending } = useSession(); const { setTheme, resolvedTheme } = useTheme(); - const isGuest = guestRegex.test(data?.user?.email ?? ""); + const isGuest = data?.user?.isAnonymous ?? user.isAnonymous ?? false; return ( - {status === "loading" ? ( + {isPending ? (
@@ -84,7 +82,7 @@ export function SidebarUserNav({ user }: { user: User }) {