auth.ts 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import { differenceInMinutes, subMinutes } from "date-fns";
  2. import { Handler } from "express";
  3. import passport from "passport";
  4. import axios from "axios";
  5. import { isAdmin, CustomError } from "../utils";
  6. import knex from "../knex";
  7. const authenticate = (
  8. type: "jwt" | "local" | "localapikey",
  9. error: string,
  10. isStrict = true
  11. ) =>
  12. async function auth(req, res, next) {
  13. if (req.user) return next();
  14. return passport.authenticate(type, (err, user) => {
  15. if (err) {
  16. throw new CustomError("An error occurred");
  17. }
  18. if (!user && isStrict) {
  19. throw new CustomError(error, 401);
  20. }
  21. if (user && isStrict && !user.verified) {
  22. throw new CustomError(
  23. "Your email address is not verified. " +
  24. "Click on signup to get the verification link again.",
  25. 400
  26. );
  27. }
  28. if (user && user.banned) {
  29. throw new CustomError("Your are banned from using this website.", 403);
  30. }
  31. if (user) {
  32. req.user = {
  33. ...user,
  34. admin: isAdmin(user.email)
  35. };
  36. return next();
  37. }
  38. return next();
  39. })(req, res, next);
  40. };
  41. export const local = authenticate("local", "Login credentials are wrong.");
  42. export const jwt = authenticate("jwt", "Unauthorized.");
  43. export const jwtLoose = authenticate("jwt", "Unauthorized.", false);
  44. export const apikey = authenticate(
  45. "localapikey",
  46. "API key is not correct.",
  47. false
  48. );
  49. export const cooldown: Handler = async (req, res, next) => {
  50. const cooldownConfig = Number(process.env.NON_USER_COOLDOWN);
  51. if (req.user || !cooldownConfig) return next();
  52. const ip = await knex<IP>("ips")
  53. .where({ ip: req.realIP.toLowerCase() })
  54. .andWhere(
  55. "created_at",
  56. ">",
  57. subMinutes(new Date(), cooldownConfig).toISOString()
  58. )
  59. .first();
  60. if (ip) {
  61. const timeToWait =
  62. cooldownConfig - differenceInMinutes(new Date(), new Date(ip.created_at));
  63. throw new CustomError(
  64. `Non-logged in users are limited. Wait ${timeToWait} minutes or log in.`,
  65. 400
  66. );
  67. }
  68. next();
  69. };
  70. export const recaptcha: Handler = async (req, res, next) => {
  71. if (process.env.NODE_ENV !== "production") return next();
  72. if (!req.user) return next();
  73. const isReCaptchaValid = await axios({
  74. method: "post",
  75. url: "https://www.google.com/recaptcha/api/siteverify",
  76. headers: {
  77. "Content-type": "application/x-www-form-urlencoded"
  78. },
  79. params: {
  80. secret: process.env.RECAPTCHA_SECRET_KEY,
  81. response: req.body.reCaptchaToken,
  82. remoteip: req.realIP
  83. }
  84. });
  85. if (!isReCaptchaValid.data.success) {
  86. throw new CustomError("reCAPTCHA is not valid. Try again.", 401);
  87. }
  88. return next();
  89. };