import { config } from "#config"; import jwt from "jsonwebtoken"; // база данных import { selPool, updPool } from "#db"; import { sql } from "slonik"; // types import { TokenPayload } from "../types/token-playload-type.js"; import type { Response } from "express"; import { z } from "zod"; import { v7 } from "uuid"; import { logger } from "#plugins/logger.js"; const JWT_ACCESS_SECRET = config.JWT_ACCESS_SECRET || ""; const JWT_REFRESH_SECRET = config.JWT_REFRESH_SECRET || ""; const ACCESS_TOKEN_LIFETIME_MINS = config.ACCESS_TOKEN_LIFETIME_MINS || 15; const REFRESH_TOKEN_LIFETIME_DAYS = config.REFRESH_TOKEN_LIFETIME_DAYS || 30; class TokenService { /** * генерирует пару токенов: accessToken и refreshToken * @param payload - данные, которые будут зашифрованы в токенах * @returns обьект с двумя свойствами: accessToken и refreshToken */ generateTokens(payload: z.infer): { accessToken: string; refreshToken: string; } { const accessToken = jwt.sign(payload, JWT_ACCESS_SECRET, { expiresIn: `${ACCESS_TOKEN_LIFETIME_MINS}m`, }); const refreshToken = jwt.sign(payload, JWT_REFRESH_SECRET, { expiresIn: `${REFRESH_TOKEN_LIFETIME_DAYS}d`, }); return { accessToken, refreshToken, }; } /** * вставляет refreshToken в базу данных * @param userId - id пользователя * @param refreshToken - токен, который будет вставлен */ async insertRefreshToken(userId: string, refreshToken: string) { const id = v7(); await updPool.query( sql.unsafe` insert into usr.user_refresh_tokens (token_id, user_id, refresh_token) values (${id}, ${userId}, ${refreshToken}) `, ); } /** * обновляет refreshToken в базе данных * @param userId - id пользователя * @param oldRefreshToken - старый токен * @param newRefreshToken - новый токен */ async updateRefreshToken( userId: string, oldRefreshToken: string, newRefreshToken: string, ) { await updPool.query( sql.unsafe` update usr.user_refresh_tokens set refresh_token = ${newRefreshToken} where user_id = ${userId} and refresh_token = ${oldRefreshToken} `, ); } /** * удаляет refreshToken из базы данных * @param userId - id пользователя * @param refreshToken - токен, который будет удален */ async removeToken(userId: string, refreshToken: string) { await updPool.query(sql.unsafe` delete from usr.user_refresh_tokens where user_id = ${userId} and refresh_token = ${refreshToken} `); } /** * удаляет все токены пользователя по его id * @param userId - id пользователя */ async removeAllUserTokens(userId: string) { await updPool.query(sql.unsafe` delete from usr.user_refresh_tokens where user_id = ${userId} `); } /** * проверяет acess токен и возвращает данные из него * @param token - токен * @returns данные из токена */ validateAccessToken(token: string) { const userData = jwt.verify(token, JWT_ACCESS_SECRET); return TokenPayload.parse(userData); } /** * проверяет refresh токен и возвращает данные из него * @param token - токен * @returns данные из токена */ validateRefreshToken(token: string) { const userData = jwt.verify(token, JWT_REFRESH_SECRET); return TokenPayload.parse(userData); } /** * Проверяет, существует ли refresh токен в базе данных * @param userId - id пользователя * @param refreshToken - токен, который будет проверен * @returns true, если токен существует, false - иначе */ async refreshTokenIsExist( userId: string, refreshToken: string, ): Promise { const tokenIsExist = await selPool.exists( sql.unsafe` select user_id from usr.user_refresh_tokens where user_id = ${userId} and refresh_token = ${refreshToken}`, ); return tokenIsExist; } /** * установить refresh токен в cookie * @param res - объект Response * @param token - токен */ setRefreshTokenInCookie(res: Response, token: string) { logger.silly(`setRefreshTokenInCookie ${token}`); res.cookie("refreshToken", token, { maxAge: config.REFRESH_TOKEN_LIFETIME_DAYS * 24 * 60 * 60 * 1000, httpOnly: true, //запрет на изменение пользователем // secure: true, //после включения https sameSite: "lax", // TODO: Разбраться }); } } export default new TokenService();