diff --git a/teressa-copeland-homes/middleware.ts b/teressa-copeland-homes/middleware.ts index 561ec35..c6c296b 100644 --- a/teressa-copeland-homes/middleware.ts +++ b/teressa-copeland-homes/middleware.ts @@ -1,25 +1,9 @@ -import { auth } from "@/lib/auth"; -import { NextResponse } from "next/server"; +import NextAuth from "next-auth"; +import { authConfig } from "@/lib/auth.config"; -export default auth((req) => { - const isLoggedIn = !!req.auth; - const isAgentRoute = req.nextUrl.pathname.startsWith("/agent"); - const isLoginPage = req.nextUrl.pathname === "/agent/login"; - - // Protect all /agent/* routes except the login page itself - if (isAgentRoute && !isLoginPage && !isLoggedIn) { - return NextResponse.redirect(new URL("/agent/login", req.nextUrl.origin)); - } - - // Redirect already-authenticated users away from the login page - if (isLoginPage && isLoggedIn) { - return NextResponse.redirect(new URL("/agent/dashboard", req.nextUrl.origin)); - } -}); +const { auth } = NextAuth(authConfig); +export default auth; export const config = { - matcher: [ - // Run on /agent/* routes, excluding Next.js internals and static files - "/((?!_next/static|_next/image|favicon.ico).*)", - ], + matcher: ["/agent/:path*"], }; diff --git a/teressa-copeland-homes/src/app/agent/(protected)/dashboard/page.tsx b/teressa-copeland-homes/src/app/agent/(protected)/dashboard/page.tsx new file mode 100644 index 0000000..c07d97e --- /dev/null +++ b/teressa-copeland-homes/src/app/agent/(protected)/dashboard/page.tsx @@ -0,0 +1,17 @@ +import { auth } from "@/lib/auth"; +import { redirect } from "next/navigation"; + +export default async function DashboardPage() { + // Defense-in-depth session check (layout also checks, this is belt-and-suspenders) + const session = await auth(); + if (!session) redirect("/agent/login"); + + return ( +
+

Dashboard

+

+ Welcome back, {session.user?.email}. Portal content coming in Phase 3. +

+
+ ); +} diff --git a/teressa-copeland-homes/src/app/agent/(protected)/layout.tsx b/teressa-copeland-homes/src/app/agent/(protected)/layout.tsx new file mode 100644 index 0000000..a521b11 --- /dev/null +++ b/teressa-copeland-homes/src/app/agent/(protected)/layout.tsx @@ -0,0 +1,26 @@ +import { auth } from "@/lib/auth"; +import { redirect } from "next/navigation"; +import { LogoutButton } from "@/components/ui/LogoutButton"; + +export default async function AgentLayout({ + children, +}: { + children: React.ReactNode; +}) { + // Defense-in-depth: middleware handles most cases, this catches edge cases + const session = await auth(); + if (!session) redirect("/agent/login"); + + return ( +
+
+ Agent Portal +
+ {session.user?.email} + +
+
+
{children}
+
+ ); +} diff --git a/teressa-copeland-homes/src/lib/auth.config.ts b/teressa-copeland-homes/src/lib/auth.config.ts new file mode 100644 index 0000000..a57632e --- /dev/null +++ b/teressa-copeland-homes/src/lib/auth.config.ts @@ -0,0 +1,44 @@ +import type { NextAuthConfig } from "next-auth"; + +/** + * Edge-compatible auth config — no DB imports. + * Used by middleware.ts (Edge Runtime). + * Full auth.ts adds the Credentials provider with DB access. + */ +export const authConfig = { + session: { + strategy: "jwt", + maxAge: 7 * 24 * 60 * 60, + updateAge: 24 * 60 * 60, + }, + pages: { + signIn: "/agent/login", + }, + providers: [], // Providers added in auth.ts — not needed for middleware JWT check + callbacks: { + authorized({ auth, request: { nextUrl } }) { + const isLoggedIn = !!auth?.user; + const isLoginPage = nextUrl.pathname === "/agent/login"; + const isAgentRoute = nextUrl.pathname.startsWith("/agent"); + + if (isLoginPage) { + if (isLoggedIn) return Response.redirect(new URL("/agent/dashboard", nextUrl.origin)); + return true; // Always allow unauthenticated access to login page + } + + if (isAgentRoute) { + return isLoggedIn; // Redirect unauthenticated users to login + } + + return true; + }, + jwt({ token, user }) { + if (user) token.id = user.id; + return token; + }, + session({ session, token }) { + session.user.id = token.id as string; + return session; + }, + }, +} satisfies NextAuthConfig; diff --git a/teressa-copeland-homes/src/lib/auth.ts b/teressa-copeland-homes/src/lib/auth.ts index e5288c5..9303b66 100644 --- a/teressa-copeland-homes/src/lib/auth.ts +++ b/teressa-copeland-homes/src/lib/auth.ts @@ -5,6 +5,7 @@ import { users } from "@/lib/db/schema"; import { eq } from "drizzle-orm"; import bcrypt from "bcryptjs"; import { z } from "zod"; +import { authConfig } from "./auth.config"; const loginSchema = z.object({ email: z.string().email(), @@ -12,14 +13,7 @@ const loginSchema = z.object({ }); export const { handlers, auth, signIn, signOut } = NextAuth({ - session: { - strategy: "jwt", - maxAge: 7 * 24 * 60 * 60, // 7 days — rolling session per user decision - updateAge: 24 * 60 * 60, // Refresh cookie once per day (not every request) - }, - pages: { - signIn: "/agent/login", // Custom login page — per user decision - }, + ...authConfig, providers: [ Credentials({ async authorize(credentials) { @@ -44,14 +38,4 @@ export const { handlers, auth, signIn, signOut } = NextAuth({ }, }), ], - callbacks: { - jwt({ token, user }) { - if (user) token.id = user.id; - return token; - }, - session({ session, token }) { - session.user.id = token.id as string; - return session; - }, - }, });