fix: Prisma $queryRaw MySQL type coercion for BigInt and Boolean
$queryRaw on MySQL returns BigInt for integer columns and 0/1 for booleans. Passing these raw values back to Prisma client methods causes validation errors: - Expected Int, provided BigInt - Expected Boolean, provided Int Fixed in auth refresh, TOTP login, and TOTP backup code flows by wrapping storedToken.id, storedToken.user_id with Number() and remember_me with Boolean(). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -126,10 +126,14 @@ export default async function authRoutes(
|
||||
return { error: "Neplatný nebo expirovaný login token", status: 401 };
|
||||
}
|
||||
|
||||
await tx.totp_login_tokens.delete({ where: { id: storedToken.id } });
|
||||
// $queryRaw on MySQL may return BigInt for integer columns
|
||||
const storedTokenId = Number(storedToken.id);
|
||||
const storedUserId = Number(storedToken.user_id);
|
||||
|
||||
await tx.totp_login_tokens.delete({ where: { id: storedTokenId } });
|
||||
|
||||
const user = await tx.users.findUnique({
|
||||
where: { id: storedToken.user_id },
|
||||
where: { id: storedUserId },
|
||||
include: { roles: true },
|
||||
});
|
||||
|
||||
|
||||
@@ -282,8 +282,12 @@ export default async function totpRoutes(
|
||||
return { error: "Neplatný nebo expirovaný login token", status: 401 };
|
||||
}
|
||||
|
||||
// $queryRaw on MySQL may return BigInt for integer columns
|
||||
const storedTokenId = Number(storedToken.id);
|
||||
const storedUserId = Number(storedToken.user_id);
|
||||
|
||||
const user = await tx.users.findUnique({
|
||||
where: { id: storedToken.user_id },
|
||||
where: { id: storedUserId },
|
||||
include: { roles: true },
|
||||
});
|
||||
|
||||
@@ -315,7 +319,7 @@ export default async function totpRoutes(
|
||||
const newFailedAttempts = (user.failed_login_attempts ?? 0) + 1;
|
||||
if (newFailedAttempts >= settings.max_login_attempts) {
|
||||
await tx.totp_login_tokens.delete({
|
||||
where: { id: storedToken.id },
|
||||
where: { id: storedTokenId },
|
||||
});
|
||||
await tx.users.update({
|
||||
where: { id: user.id },
|
||||
|
||||
@@ -274,7 +274,11 @@ export async function refreshAccessToken(
|
||||
return { type: "error", message: "Neplatný refresh token", status: 401 };
|
||||
}
|
||||
|
||||
const authData = await loadAuthData(storedToken.user_id);
|
||||
// $queryRaw on MySQL may return BigInt for integer columns
|
||||
const storedTokenId = Number(storedToken.id);
|
||||
const storedUserId = Number(storedToken.user_id);
|
||||
|
||||
const authData = await loadAuthData(storedUserId);
|
||||
if (!authData) {
|
||||
return { type: "error", message: "Uživatel nenalezen", status: 401 };
|
||||
}
|
||||
@@ -286,16 +290,19 @@ export async function refreshAccessToken(
|
||||
? config.jwt.refreshTokenRememberExpiry
|
||||
: config.jwt.refreshTokenSessionExpiry;
|
||||
|
||||
// $queryRaw on MySQL returns 0/1 for booleans; Prisma expects true/false
|
||||
const rememberMe = Boolean(storedToken.remember_me);
|
||||
|
||||
await tx.refresh_tokens.update({
|
||||
where: { id: storedToken.id },
|
||||
where: { id: storedTokenId },
|
||||
data: { replaced_at: new Date(), replaced_by_hash: newRefreshTokenHash },
|
||||
});
|
||||
await tx.refresh_tokens.create({
|
||||
data: {
|
||||
user_id: storedToken.user_id,
|
||||
user_id: storedUserId,
|
||||
token_hash: newRefreshTokenHash,
|
||||
expires_at: new Date(Date.now() + expiresIn * 1000),
|
||||
remember_me: storedToken.remember_me ?? false,
|
||||
remember_me: rememberMe,
|
||||
ip_address: request.ip,
|
||||
user_agent: request.headers["user-agent"] ?? null,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user