import { NextRequest, NextResponse } from "next/server"; import { db } from "@/lib/db"; import { ACCESS_TOKEN_EXPIRES_IN, REFRESH_TOKEN_EXPIRES_IN, createMobileTokenPair, hashToken, revokeRefreshTokenFamily, } from "@/lib/mobileTokens"; export async function POST(req: NextRequest) { const { refreshToken } = await req.json().catch(() => ({})); if (typeof refreshToken !== "string" || !refreshToken.trim()) { return NextResponse.json({ error: "refreshToken is required" }, { status: 400 }); } const existingRefreshToken = await db.refreshToken.findUnique({ where: { tokenHash: hashToken(refreshToken.trim()) }, include: { user: { select: { id: true, name: true, phone: true, role: true, }, }, }, }); if (!existingRefreshToken) { return NextResponse.json({ error: "Invalid refresh token" }, { status: 401 }); } if (existingRefreshToken.revokedAt) { await revokeRefreshTokenFamily(existingRefreshToken.userId, existingRefreshToken.familyId); return NextResponse.json({ error: "Refresh token has been revoked" }, { status: 401 }); } if (existingRefreshToken.expiresAt <= new Date()) { await db.refreshToken.update({ where: { id: existingRefreshToken.id }, data: { revokedAt: new Date() }, }); return NextResponse.json({ error: "Refresh token has expired" }, { status: 401 }); } const tokens = await createMobileTokenPair(existingRefreshToken.userId, existingRefreshToken.familyId); await db.refreshToken.update({ where: { id: existingRefreshToken.id }, data: { revokedAt: new Date(), replacedByTokenId: tokens.refreshTokenId, }, }); return NextResponse.json({ accessToken: tokens.accessToken, token: tokens.accessToken, tokenType: "Bearer", expiresIn: ACCESS_TOKEN_EXPIRES_IN, expiresAt: tokens.accessTokenExpiresAt.toISOString(), refreshToken: tokens.refreshToken, refreshExpiresIn: REFRESH_TOKEN_EXPIRES_IN, refreshExpiresAt: tokens.refreshTokenExpiresAt.toISOString(), user: existingRefreshToken.user, }); }