add otp swagger2

This commit is contained in:
2026-05-11 16:06:47 +03:30
parent e60401a86c
commit 3be3a49abd
13 changed files with 2017 additions and 26 deletions

View File

@@ -0,0 +1,28 @@
import { NextRequest, NextResponse } from "next/server";
import { db } from "@/lib/db";
import { hashToken, revokeRefreshTokenFamily } from "@/lib/mobileTokens";
export async function POST(req: NextRequest) {
const authHeader = req.headers.get("authorization");
const accessToken = authHeader?.match(/^Bearer\s+(.+)$/i)?.[1];
const { refreshToken } = await req.json().catch(() => ({}));
if (accessToken) {
await db.session.deleteMany({
where: { sessionToken: accessToken },
});
}
if (typeof refreshToken === "string" && refreshToken.trim()) {
const token = await db.refreshToken.findUnique({
where: { tokenHash: hashToken(refreshToken.trim()) },
select: { userId: true, familyId: true },
});
if (token) {
await revokeRefreshTokenFamily(token.userId, token.familyId);
}
}
return NextResponse.json({ ok: true });
}

View File

@@ -0,0 +1,71 @@
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,
});
}

View File

@@ -2,9 +2,13 @@ import { NextRequest, NextResponse } from "next/server";
import bcrypt from "bcryptjs";
import crypto from "crypto";
import { db } from "@/lib/db";
import {
ACCESS_TOKEN_EXPIRES_IN,
REFRESH_TOKEN_EXPIRES_IN,
createMobileTokenPair,
} from "@/lib/mobileTokens";
const IRAN_MOBILE_REGEX = /^(\+98|98|0)?9\d{9}$/;
const SESSION_MAX_AGE_DAYS = 30;
function normalizeIranMobile(phone: string) {
const digits = phone.replace(/[^\d+]/g, "");
@@ -70,21 +74,17 @@ export async function POST(req: NextRequest) {
data: { consumedAt: new Date() },
});
const token = crypto.randomBytes(32).toString("hex");
const expires = new Date(Date.now() + SESSION_MAX_AGE_DAYS * 24 * 60 * 60 * 1000);
await db.session.create({
data: {
sessionToken: token,
userId: user.id,
expires,
},
});
const tokens = await createMobileTokenPair(user.id);
return NextResponse.json({
token,
accessToken: tokens.accessToken,
token: tokens.accessToken,
tokenType: "Bearer",
expiresAt: expires.toISOString(),
expiresIn: ACCESS_TOKEN_EXPIRES_IN,
expiresAt: tokens.accessTokenExpiresAt.toISOString(),
refreshToken: tokens.refreshToken,
refreshExpiresIn: REFRESH_TOKEN_EXPIRES_IN,
refreshExpiresAt: tokens.refreshTokenExpiresAt.toISOString(),
user: {
id: user.id,
name: user.name,