53 lines
1.3 KiB
TypeScript
53 lines
1.3 KiB
TypeScript
// app/api/auth/callback/route.ts
|
|
import { NextResponse } from "next/server";
|
|
import { cookies } from "next/headers";
|
|
import { db } from "@/lib/db";
|
|
import { loginTokens } from "@/lib/schema";
|
|
import { and, eq, gt, isNull } from "drizzle-orm";
|
|
import { hashToken } from "@/lib/auth/tokens";
|
|
|
|
export async function POST(req: Request) {
|
|
const { token } = await req.json();
|
|
|
|
if (!token) {
|
|
return NextResponse.json({ error: "Missing token" }, { status: 400 });
|
|
}
|
|
|
|
const tokenHash = hashToken(token);
|
|
|
|
const loginToken = await db
|
|
.select()
|
|
.from(loginTokens)
|
|
.where(
|
|
and(
|
|
eq(loginTokens.tokenHash, tokenHash),
|
|
isNull(loginTokens.usedAt),
|
|
gt(loginTokens.expiresAt, new Date())
|
|
)
|
|
)
|
|
.limit(1)
|
|
.then((rows) => rows[0]);
|
|
|
|
if (!loginToken) {
|
|
return NextResponse.json(
|
|
{ error: "Invalid or expired token" },
|
|
{ status: 401 }
|
|
);
|
|
}
|
|
|
|
// ✅ mark token as used
|
|
await db
|
|
.update(loginTokens)
|
|
.set({ usedAt: new Date() })
|
|
.where(eq(loginTokens.id, loginToken.id));
|
|
|
|
// ✅ FIX: cookies() is async in Next 15+
|
|
const cookieStore = await cookies();
|
|
cookieStore.set("session", loginToken.userId, {
|
|
httpOnly: true,
|
|
sameSite: "lax",
|
|
path: "/",
|
|
});
|
|
|
|
return NextResponse.json({ ok: true });
|
|
}
|