107 lines
2.9 KiB
JavaScript
107 lines
2.9 KiB
JavaScript
// db/refreshToken.db.js
|
||
const prisma = require("./client")
|
||
|
||
function toDate(x) {
|
||
if (!x) return null
|
||
if (x instanceof Date) return x
|
||
const d = new Date(x)
|
||
return Number.isNaN(d.getTime()) ? null : d
|
||
}
|
||
|
||
async function createRefreshToken(userId, input = {}) {
|
||
const data = {
|
||
userId: Number(userId),
|
||
tokenHash: input.tokenHash, // required
|
||
familyId: input.familyId, // required
|
||
jti: input.jti, // required
|
||
expiresAt: toDate(input.expiresAt) || new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),
|
||
createdByIp: input.createdByIp ?? null,
|
||
userAgent: input.userAgent ?? null,
|
||
}
|
||
|
||
return prisma.refreshToken.create({ data })
|
||
}
|
||
|
||
async function findRefreshTokenByHash(tokenHash, options = {}) {
|
||
return prisma.refreshToken.findUnique({
|
||
where: { tokenHash },
|
||
select: options.select || undefined,
|
||
include: options.include || undefined,
|
||
})
|
||
}
|
||
|
||
async function revokeRefreshTokenById(id, meta = {}) {
|
||
return prisma.refreshToken.update({
|
||
where: { id },
|
||
data: {
|
||
revokedAt: meta.revokedAt ?? new Date(),
|
||
// optional audit
|
||
createdByIp: meta.createdByIp ?? undefined,
|
||
userAgent: meta.userAgent ?? undefined,
|
||
},
|
||
})
|
||
}
|
||
|
||
async function revokeRefreshTokenByHash(tokenHash, meta = {}) {
|
||
return prisma.refreshToken.update({
|
||
where: { tokenHash },
|
||
data: {
|
||
revokedAt: meta.revokedAt ?? new Date(),
|
||
createdByIp: meta.createdByIp ?? undefined,
|
||
userAgent: meta.userAgent ?? undefined,
|
||
},
|
||
})
|
||
}
|
||
|
||
// Rotation: eski token -> revoked + replacedById set, yeni token create
|
||
async function rotateRefreshToken({ oldId, newToken = {}, meta = {} }) {
|
||
return prisma.$transaction(async (tx) => {
|
||
const created = await tx.refreshToken.create({
|
||
data: {
|
||
userId: Number(newToken.userId),
|
||
tokenHash: newToken.tokenHash,
|
||
familyId: newToken.familyId,
|
||
jti: newToken.jti,
|
||
expiresAt: toDate(newToken.expiresAt),
|
||
createdByIp: meta.createdByIp ?? null,
|
||
userAgent: meta.userAgent ?? null,
|
||
},
|
||
})
|
||
|
||
const revoked = await tx.refreshToken.update({
|
||
where: { id: oldId },
|
||
data: {
|
||
revokedAt: meta.revokedAt ?? new Date(),
|
||
replacedById: created.id,
|
||
},
|
||
})
|
||
|
||
return { created, revoked }
|
||
})
|
||
}
|
||
|
||
// Reuse tespiti / güvenlik: aynı ailedeki tüm tokenları revoke et
|
||
async function revokeRefreshTokenFamily(familyId) {
|
||
return prisma.refreshToken.updateMany({
|
||
where: { familyId, revokedAt: null },
|
||
data: { revokedAt: new Date() },
|
||
})
|
||
}
|
||
|
||
async function revokeAllUserRefreshTokens(userId) {
|
||
return prisma.refreshToken.updateMany({
|
||
where: { userId: Number(userId), revokedAt: null },
|
||
data: { revokedAt: new Date() },
|
||
})
|
||
}
|
||
|
||
module.exports = {
|
||
createRefreshToken,
|
||
findRefreshTokenByHash,
|
||
revokeRefreshTokenById,
|
||
revokeRefreshTokenByHash,
|
||
rotateRefreshToken,
|
||
revokeRefreshTokenFamily,
|
||
revokeAllUserRefreshTokens,
|
||
}
|