auth-controller.ts 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. // db
  2. import { selPool, updPool } from "#db";
  3. import { DbSchema } from "#db-schema";
  4. import { sql } from "slonik";
  5. // api
  6. import { api } from "#api";
  7. // error
  8. import { ApiError } from "#exceptions/api-error.js";
  9. // other
  10. import { z } from "zod";
  11. import bcript from "bcrypt";
  12. import { v7 as uuidv7 } from "uuid";
  13. import tokenService from "../services/token-service.js";
  14. import { UserAuthService } from "../services/user-auth-service.js";
  15. import { ConfirmPinsService } from "#modules/users/confirm-pins/confirm-pins-service.js";
  16. import { RouterUtils } from "#utils/router-utils.js";
  17. import { config } from "#config";
  18. import { Request, Response } from "express";
  19. class authController {
  20. // --- Регистрация ---
  21. async register(
  22. req: Request,
  23. res: Response,
  24. // next: NextFunction
  25. ) {
  26. // валидация запроса
  27. const { email } = api.auth.POST_Registration.req.parse(req.body);
  28. const isUserExist = await UserAuthService.checkUserExistByEmail(email);
  29. // если пользователь уже зарегистрирован
  30. if (isUserExist) {
  31. RouterUtils.validAndSendResponse(
  32. api.auth.POST_Registration.res,
  33. res,
  34. { code: "alreadyExists" },
  35. 400,
  36. );
  37. return;
  38. }
  39. // отправка пина
  40. const transactionId = uuidv7();
  41. try {
  42. await ConfirmPinsService.sendConfirmPin(transactionId, email);
  43. } catch {
  44. RouterUtils.validAndSendResponse(
  45. api.auth.POST_Registration.res,
  46. res,
  47. { code: "pinIsNotSent" },
  48. 400,
  49. );
  50. return;
  51. }
  52. RouterUtils.validAndSendResponse(api.auth.POST_Registration.res, res, {
  53. code: "pinIsSent",
  54. transactionId: transactionId,
  55. });
  56. }
  57. async confirmRegistration(req: Request, res: Response) {
  58. // валидация запроса
  59. const { password, transactionId, confirmPin } =
  60. api.auth.POST_ConfirmRegistration.req.parse(req.body);
  61. // проверка пина
  62. const pinInfo = await ConfirmPinsService.checkConfirmPin(
  63. transactionId,
  64. confirmPin,
  65. );
  66. switch (pinInfo.status) {
  67. case "rotten": {
  68. RouterUtils.validAndSendResponse(
  69. api.auth.POST_ConfirmRegistration.res,
  70. res,
  71. { code: "pinIsRotten" },
  72. 400,
  73. );
  74. return;
  75. }
  76. case "tooManyTries": {
  77. RouterUtils.validAndSendResponse(
  78. api.auth.POST_ConfirmRegistration.res,
  79. res,
  80. { code: "tooManyTries" },
  81. 400,
  82. );
  83. return;
  84. }
  85. case "wrong": {
  86. RouterUtils.validAndSendResponse(
  87. api.auth.POST_ConfirmRegistration.res,
  88. res,
  89. {
  90. code: "pinIsWrong",
  91. triesRemained: pinInfo.triesRemained,
  92. },
  93. 400,
  94. );
  95. return;
  96. }
  97. }
  98. // пин правильный
  99. const email = pinInfo.email;
  100. // регистрация
  101. const hashPassword = await bcript.hash(password, 3);
  102. const userId = uuidv7();
  103. await updPool.query(
  104. sql.unsafe`
  105. insert into usr.users
  106. (user_id, email, password)
  107. values
  108. (${userId}, ${email}, ${hashPassword})`,
  109. );
  110. // токены
  111. const { accessToken, refreshToken } = tokenService.generateTokens({
  112. email,
  113. userId,
  114. });
  115. await tokenService.insertRefreshToken(userId, refreshToken);
  116. tokenService.setRefreshTokenInCookie(res, refreshToken);
  117. RouterUtils.validAndSendResponse(
  118. api.auth.POST_ConfirmRegistration.res,
  119. res,
  120. {
  121. code: "registered",
  122. accessToken,
  123. userData: {
  124. email,
  125. userId,
  126. },
  127. },
  128. );
  129. }
  130. async login(req: Request, res: Response) {
  131. // валидация запроса
  132. const { email, password } = api.auth.POST_Login.req.parse(req.body);
  133. // поиск юзера
  134. const user = await selPool.maybeOne(
  135. sql.type(
  136. z.object({
  137. userId: DbSchema.usr.users.userId,
  138. password: DbSchema.usr.users.password,
  139. wrongPassTries: DbSchema.usr.users.wrongPassTries,
  140. }),
  141. )`
  142. select
  143. user_id as "userId",
  144. password,
  145. wrong_pass_tries as "wrongPassTries"
  146. from
  147. usr.users
  148. where
  149. email = ${email}`,
  150. );
  151. if (!user) {
  152. RouterUtils.validAndSendResponse(
  153. api.auth.POST_Login.res,
  154. res,
  155. {
  156. code: "userNotFound",
  157. },
  158. 400,
  159. );
  160. return;
  161. }
  162. // если количество попыток превышено
  163. if (user.wrongPassTries > config.PASSWORD_MAX_TRIES - 1) {
  164. RouterUtils.validAndSendResponse(
  165. api.auth.POST_Login.res,
  166. res,
  167. {
  168. code: "tooManyTries",
  169. // TODO: сделать дату разблокировки
  170. unblockingDate: new Date().toISOString(),
  171. },
  172. 400,
  173. );
  174. return;
  175. }
  176. // проверка пароля
  177. const isPassEquals = await bcript.compare(password, user.password);
  178. if (!isPassEquals) {
  179. await UserAuthService.authTriesIncrement(user.userId);
  180. const triesRemained = config.PASSWORD_MAX_TRIES - 1 - user.wrongPassTries;
  181. RouterUtils.validAndSendResponse(
  182. api.auth.POST_Login.res,
  183. res,
  184. {
  185. code: "passIsWrong",
  186. triesRemained,
  187. },
  188. 400,
  189. );
  190. return;
  191. }
  192. // успешный логин
  193. await UserAuthService.resetAuthTries(user.userId);
  194. // токены
  195. const { accessToken, refreshToken } = tokenService.generateTokens({
  196. email,
  197. userId: user.userId,
  198. });
  199. await tokenService.insertRefreshToken(user.userId, refreshToken);
  200. tokenService.setRefreshTokenInCookie(res, refreshToken);
  201. RouterUtils.validAndSendResponse(
  202. api.auth.POST_Login.res,
  203. res,
  204. {
  205. code: "success",
  206. accessToken,
  207. userData: { email, userId: user.userId },
  208. },
  209. 200,
  210. );
  211. }
  212. async logout(req: Request, res: Response) {
  213. const { refreshToken } = req.cookies;
  214. const userData = tokenService.validateRefreshToken(refreshToken);
  215. await tokenService.removeToken(userData.userId, refreshToken);
  216. res.clearCookie("refreshToken");
  217. RouterUtils.validAndSendResponse(api.auth.POST_Logout.res, res, {
  218. code: "success",
  219. });
  220. }
  221. async logoutAllDevices(req: Request, res: Response) {
  222. const { refreshToken } = req.cookies;
  223. const userData = tokenService.validateRefreshToken(refreshToken);
  224. await tokenService.removeAllUserTokens(userData.userId);
  225. res.clearCookie("refreshToken");
  226. RouterUtils.validAndSendResponse(api.auth.POST_LogoutAllDevices.res, res, {
  227. code: "success",
  228. });
  229. }
  230. async refresh(req: Request, res: Response) {
  231. const { refreshToken } = req.cookies;
  232. if (!refreshToken) throw ApiError.UnauthorizedError();
  233. const userData = tokenService.validateRefreshToken(refreshToken);
  234. const tokenIsExist = await tokenService.refreshTokenIsExist(
  235. userData.userId,
  236. refreshToken,
  237. );
  238. if (!userData || !tokenIsExist) {
  239. res.clearCookie("refreshToken");
  240. throw ApiError.UnauthorizedError();
  241. }
  242. // обновляем токены
  243. const newUserData = await selPool.maybeOne(
  244. sql.type(
  245. z.object({
  246. email: DbSchema.usr.users.email,
  247. }),
  248. )`select email from usr.users where user_id = ${userData.userId}`,
  249. );
  250. if (!newUserData) {
  251. throw ApiError.UnauthorizedError();
  252. }
  253. const newTokens = tokenService.generateTokens({
  254. userId: userData.userId,
  255. email: newUserData.email,
  256. });
  257. await tokenService.updateRefreshToken(
  258. userData.userId,
  259. refreshToken,
  260. newTokens.refreshToken,
  261. );
  262. tokenService.setRefreshTokenInCookie(res, newTokens.refreshToken);
  263. RouterUtils.validAndSendResponse(api.auth.POST_Refresh.res, res, {
  264. code: "success",
  265. accessToken: newTokens.accessToken,
  266. userData: {
  267. email: newUserData.email,
  268. userId: userData.userId,
  269. },
  270. });
  271. }
  272. }
  273. export const AuthController = new authController();