diff --git a/apps/api/src/services/auth.service.ts b/apps/api/src/services/auth.service.ts index d6098b7..6150da7 100644 --- a/apps/api/src/services/auth.service.ts +++ b/apps/api/src/services/auth.service.ts @@ -147,52 +147,56 @@ export async function login(data: LoginRequest): Promise { } export async function refreshTokens(token: string): Promise<{ accessToken: string; refreshToken: string }> { - const storedToken = await prisma.refreshToken.findUnique({ - where: { token }, - }); + // Use a transaction to prevent race conditions + return await prisma.$transaction(async (tx) => { + const storedToken = await tx.refreshToken.findUnique({ + where: { token }, + }); - if (!storedToken) { - throw new AppError(401, 'Token inválido'); - } + if (!storedToken) { + throw new AppError(401, 'Token inválido'); + } - if (storedToken.expiresAt < new Date()) { - await prisma.refreshToken.delete({ where: { id: storedToken.id } }); - throw new AppError(401, 'Token expirado'); - } + if (storedToken.expiresAt < new Date()) { + await tx.refreshToken.deleteMany({ where: { id: storedToken.id } }); + throw new AppError(401, 'Token expirado'); + } - const payload = verifyToken(token); + const payload = verifyToken(token); - const user = await prisma.user.findUnique({ - where: { id: payload.userId }, - include: { tenant: true }, - }); + const user = await tx.user.findUnique({ + where: { id: payload.userId }, + include: { tenant: true }, + }); - if (!user || !user.active) { - throw new AppError(401, 'Usuario no encontrado o desactivado'); - } + if (!user || !user.active) { + throw new AppError(401, 'Usuario no encontrado o desactivado'); + } - await prisma.refreshToken.delete({ where: { id: storedToken.id } }); + // Use deleteMany to avoid error if already deleted (race condition) + await tx.refreshToken.deleteMany({ where: { id: storedToken.id } }); - const newTokenPayload = { - userId: user.id, - email: user.email, - role: user.role, - tenantId: user.tenantId, - schemaName: user.tenant.schemaName, - }; - - const accessToken = generateAccessToken(newTokenPayload); - const refreshToken = generateRefreshToken(newTokenPayload); - - await prisma.refreshToken.create({ - data: { + const newTokenPayload = { userId: user.id, - token: refreshToken, - expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), - }, - }); + email: user.email, + role: user.role, + tenantId: user.tenantId, + schemaName: user.tenant.schemaName, + }; - return { accessToken, refreshToken }; + const accessToken = generateAccessToken(newTokenPayload); + const refreshToken = generateRefreshToken(newTokenPayload); + + await tx.refreshToken.create({ + data: { + userId: user.id, + token: refreshToken, + expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), + }, + }); + + return { accessToken, refreshToken }; + }); } export async function logout(token: string): Promise {