diff --git a/app/actions.ts b/app/actions.ts index 3c6967c..cab6259 100644 --- a/app/actions.ts +++ b/app/actions.ts @@ -1,11 +1,11 @@ "use server"; +import { auth } from "@/auth"; import { prisma } from "@/lib/prisma"; -import { getServerSession } from "@/lib/session/get-server-session"; import { revalidatePath } from "next/cache"; export async function removeChat({ id, path }: { id: string; path: string }) { - const session = await getServerSession(); + const session = await auth(); const userId = session?.user?.email; if (!userId || !id) { diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 0000000..e47b0fd --- /dev/null +++ b/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,2 @@ +export { GET, POST } from "@/auth"; +export const runtime = "edge"; diff --git a/app/api/auth/callback/route.ts b/app/api/auth/callback/route.ts deleted file mode 100644 index a235634..0000000 --- a/app/api/auth/callback/route.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; -import { serialize } from 'cookie'; -import { createSession } from '@/lib/session/create'; -import { encryptJWECookie } from '@/lib/cookies/encrypt-jwe'; -import ms from 'ms'; -import { userSessionCookieName } from '@/lib/session/constants'; - -export const runtime = 'edge'; - -const sharedTokenSecret = process.env.SHARE_TOKEN_ENDPOINT_SECRET; -const cookieTTL = ms('1y'); - -export async function GET(req: NextRequest) { - const { origin, searchParams } = req.nextUrl; - const shareableToken = searchParams.get('shareableToken'); - - if (!sharedTokenSecret || !shareableToken) { - // TODO: Make an error page - return NextResponse.json({ error: 'Invalid request' }, { status: 500 }); - } - - const query = new URLSearchParams({ - sharedTokenSecret, - }); - - const response = await fetch( - `https://api.vercel.com/user/tokens/shared/${shareableToken}?${query.toString()}` - ); - - const { token } = (await response.json()) as { token: string }; - - const session = await createSession(token); - - const cookieValue = await encryptJWECookie(session, '1y'); - const next = req.cookies.get('auth-next')?.value; - - return NextResponse.redirect(new URL(next ?? '/', origin), { - headers: [ - [ - 'set-cookie', - serialize(userSessionCookieName, cookieValue, { - path: '/', - maxAge: cookieTTL / 1000, - expires: new Date(Date.now() + cookieTTL), - httpOnly: true, - secure: process.env.NODE_ENV !== 'development', - }), - ], - // There's currently an issue with adding more than one cookie to a response: https://github.com/vercel/next.js/issues/46579 - // the `auth-next` cookie will expire in 10 min anyway - // [ - // 'set-cookie', - // serialize('auth-next', '', { - // path: '/api/auth/callback', - // maxAge: 0, - // expires: new Date(), - // httpOnly: true, - // }) - // ] - ], - }); -} diff --git a/app/api/auth/login/route.ts b/app/api/auth/login/route.ts deleted file mode 100644 index 1bd23cc..0000000 --- a/app/api/auth/login/route.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; -import { serialize } from 'cookie'; - -export const runtime = 'edge'; - -export async function GET(req: NextRequest) { - const { origin } = req.nextUrl; - - const next = req.nextUrl.searchParams.get('next'); - - const params = new URLSearchParams({ - redirectUrl: new URL('/api/auth/callback', origin).toString(), - }); - - return NextResponse.redirect( - `https://vercel.com/api/vercel-auth?${params.toString()}`, - { - headers: next - ? { - 'Set-Cookie': serialize('auth-next', next, { - path: '/api/auth/callback', - maxAge: 60 * 60 * 10, - httpOnly: true, - }), - } - : undefined, - } - ); -} diff --git a/app/api/auth/logout/route.ts b/app/api/auth/logout/route.ts deleted file mode 100644 index a8b2768..0000000 --- a/app/api/auth/logout/route.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { userSessionCookieName } from '@/lib/session/constants'; -import { serialize } from 'cookie'; -import { NextRequest, NextResponse } from 'next/server'; - -export const runtime = 'edge'; - -export async function GET(req: NextRequest) { - const { origin, searchParams } = req.nextUrl; - const next = searchParams.get('next'); - - return NextResponse.redirect(new URL(next ?? '/', origin), { - headers: [ - [ - 'set-cookie', - serialize(userSessionCookieName, '', { - path: '/', - maxAge: 0, - expires: new Date(), - httpOnly: true, - secure: process.env.NODE_ENV !== 'development', - }), - ], - ], - }); -} diff --git a/app/api/generate/route.ts b/app/api/generate/route.ts index 7d34526..5d73c5a 100644 --- a/app/api/generate/route.ts +++ b/app/api/generate/route.ts @@ -1,10 +1,11 @@ +import { auth } from "@/auth"; import { OpenAIStream, openai } from "@/lib/openai"; -import { getServerSession } from "@/lib/session/get-server-session"; import { prisma } from "@/lib/prisma"; -export async function POST(req: Request) { +export const POST = auth(async function POST(req: Request) { const json = await req.json(); - const session = await getServerSession(); + // @ts-ignore + console.log(req.auth); // todo fix types const res = await openai.createChatCompletion({ model: "gpt-3.5-turbo", @@ -41,4 +42,4 @@ export async function POST(req: Request) { status: 200, headers: { "Content-Type": "text/event-stream" }, }); -} +}); diff --git a/app/chat/[id]/page.tsx b/app/chat/[id]/page.tsx index 4129e8c..f4e6f06 100644 --- a/app/chat/[id]/page.tsx +++ b/app/chat/[id]/page.tsx @@ -3,7 +3,7 @@ import { prisma } from "@/lib/prisma"; import { Chat } from "../../chat"; import { type Metadata } from "next"; -import { getServerSession } from "@/lib/session/get-server-session"; +import { auth } from "@/auth"; export interface ChatPageProps { params: { @@ -29,7 +29,7 @@ export const runtime = "nodejs"; // default export const preferredRegion = "home"; export const dynamic = "force-dynamic"; export default async function ChatPage({ params }: ChatPageProps) { - const session = await getServerSession(); + const session = await auth(); const chat = await prisma.chat.findFirst({ where: { id: params.id, diff --git a/app/page.tsx b/app/page.tsx index a3ca75a..7dec5b3 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,6 +1,6 @@ -import { getServerSession } from "@/lib/session/get-server-session"; import { Chat } from "./chat"; import { Sidebar } from "./sidebar"; +import { auth } from "@/auth"; // Prisma does not support Edge without the Data Proxy currently export const runtime = "nodejs"; // default @@ -8,7 +8,7 @@ export const preferredRegion = "home"; export const dynamic = "force-dynamic"; export default async function IndexPage() { - const session = await getServerSession(); + const session = await auth(); return (
diff --git a/app/sidebar.tsx b/app/sidebar.tsx index e2379f3..d43cd82 100644 --- a/app/sidebar.tsx +++ b/app/sidebar.tsx @@ -2,7 +2,7 @@ import { NextChatLogo } from "@/components/ui/nextchat-logo"; import { Login } from "@/components/ui/login"; import { UserMenu } from "@/components/ui/user-menu"; import { prisma } from "@/lib/prisma"; -import { type Session } from "@/lib/session/types"; +import { type Session } from "@auth/nextjs/types"; import { cn } from "@/lib/utils"; import { Plus } from "lucide-react"; import Link from "next/link"; @@ -70,7 +70,8 @@ async function SidebarList({ session }: { session?: Session }) { updatedAt: "desc", }, }), - [session?.user.id || ""], + // @ts-ignore + [session?.user?.id || ""], { revalidate: 3600, } diff --git a/auth.ts b/auth.ts new file mode 100644 index 0000000..100244e --- /dev/null +++ b/auth.ts @@ -0,0 +1,26 @@ +import NextAuth from "@auth/nextjs"; +import GitHub from "@auth/nextjs/providers/github"; +import { NextResponse } from "next/server"; +export const { + handlers: { GET, POST }, + auth, + CSRF_experimental, +} = NextAuth({ + // @ts-ignore + providers: [GitHub], + session: { strategy: "jwt" }, + async authorized({ request, auth }: any) { + const url = request.nextUrl; + + if (request.method === "POST") { + const { authToken } = (await request.json()) ?? {}; + // If the request has a valid auth token, it is authorized + const valid = true; + if (valid) return true; + return NextResponse.json("Invalid auth token", { status: 401 }); + } + + // Logged in users are authenticated, otherwise redirect to login page + return !!auth; + }, +}); diff --git a/components/ui/login.tsx b/components/ui/login.tsx index f0c37f4..07edb90 100644 --- a/components/ui/login.tsx +++ b/components/ui/login.tsx @@ -1,14 +1,13 @@ "use client"; - -import Link from "next/link"; +import { signIn } from "@auth/nextjs/client"; export function Login() { return ( - signIn("github")} > Login - + ); } diff --git a/components/ui/user-menu.tsx b/components/ui/user-menu.tsx index 97ff9b4..fb32743 100644 --- a/components/ui/user-menu.tsx +++ b/components/ui/user-menu.tsx @@ -1,7 +1,9 @@ "use client"; import { ThemeToggle } from "@/components/theme-toggle"; -import { type Session } from "@/lib/session/types"; +import { type Session } from "@auth/nextjs/types"; import * as DropdownMenu from "@radix-ui/react-dropdown-menu"; +import { signOut } from "@auth/nextjs/client"; + import Image from "next/image"; export interface UserMenuProps { @@ -14,12 +16,12 @@ export function UserMenu({ session }: UserMenuProps) {