authController.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. const fs = require('fs');
  2. const path = require('path');
  3. const passport = require('passport');
  4. const JWT = require('jsonwebtoken');
  5. const axios = require('axios');
  6. const config = require('../config');
  7. const transporter = require('../mail/mail');
  8. const { resetMailText, verifyMailText } = require('../mail/text');
  9. const {
  10. createUser,
  11. changePassword,
  12. generateApiKey,
  13. getUser,
  14. verifyUser,
  15. requestPasswordReset,
  16. resetPassword,
  17. } = require('../db/user');
  18. /* Read email template */
  19. const resetEmailTemplatePath = path.join(__dirname, '../mail/template-reset.html');
  20. const verifyEmailTemplatePath = path.join(__dirname, '../mail/template-verify.html');
  21. const resetEmailTemplate = fs
  22. .readFileSync(resetEmailTemplatePath, { encoding: 'utf-8' })
  23. .replace(/{{domain}}/gm, config.DEFAULT_DOMAIN);
  24. const verifyEmailTemplate = fs
  25. .readFileSync(verifyEmailTemplatePath, { encoding: 'utf-8' })
  26. .replace(/{{domain}}/gm, config.DEFAULT_DOMAIN);
  27. /* Function to generate JWT */
  28. const signToken = user =>
  29. JWT.sign(
  30. {
  31. iss: 'ApiAuth',
  32. sub: user.email,
  33. iat: new Date().getTime(),
  34. exp: new Date().setDate(new Date().getDate() + 7),
  35. },
  36. config.JWT_SECRET
  37. );
  38. /* Passport.js authentication controller */
  39. const authenticate = (type, error, isStrict = true) =>
  40. function auth(req, res, next) {
  41. if (req.user) return next();
  42. return passport.authenticate(type, (err, user) => {
  43. if (err) return res.status(400);
  44. if (!user && isStrict) return res.status(401).json({ error });
  45. req.user = user;
  46. return next();
  47. })(req, res, next);
  48. };
  49. exports.authLocal = authenticate('local', 'Login email and/or password are wrong.');
  50. exports.authJwt = authenticate('jwt', 'Unauthorized.');
  51. exports.authJwtLoose = authenticate('jwt', 'Unauthorized.', false);
  52. exports.authApikey = authenticate('localapikey', 'API key is not correct.', false);
  53. /* reCaptcha controller */
  54. exports.recaptcha = async (req, res, next) => {
  55. if (!req.user) {
  56. const isReCaptchaValid = await axios({
  57. method: 'post',
  58. url: 'https://www.google.com/recaptcha/api/siteverify',
  59. headers: {
  60. 'Content-type': 'application/x-www-form-urlencoded',
  61. },
  62. params: {
  63. secret: config.RECAPTCHA_SECRET_KEY,
  64. response: req.body.reCaptchaToken,
  65. remoteip: req.realIp,
  66. },
  67. });
  68. if (!isReCaptchaValid.data.success) {
  69. return res.status(401).json({ error: 'reCAPTCHA is not valid. Try again.' });
  70. }
  71. }
  72. return next();
  73. };
  74. exports.signup = async (req, res) => {
  75. const { email, password } = req.body;
  76. if (password.length > 64) {
  77. return res.status(400).json({ error: 'Maximum password length is 64.' });
  78. }
  79. if (email.length > 64) {
  80. return res.status(400).json({ error: 'Maximum email length is 64.' });
  81. }
  82. const user = await getUser({ email });
  83. if (user && user.verified) return res.status(403).json({ error: 'Email is already in use.' });
  84. const newUser = await createUser({ email, password });
  85. const mail = await transporter.sendMail({
  86. from: config.MAIL_USER,
  87. to: newUser.email,
  88. subject: 'Verify your account',
  89. text: verifyMailText.replace('{{verification}}', newUser.verificationToken),
  90. html: verifyEmailTemplate.replace('{{verification}}', newUser.verificationToken),
  91. });
  92. if (mail.accepted.length) {
  93. return res.status(201).json({ email, message: 'Verification email has been sent.' });
  94. }
  95. return res.status(400).json({ error: "Couldn't send verification email. Try again." });
  96. };
  97. exports.login = ({ user }, res) => {
  98. const token = signToken(user);
  99. return res.status(200).json({ token });
  100. };
  101. exports.renew = ({ user }, res) => {
  102. const token = signToken(user);
  103. return res.status(200).json({ token });
  104. };
  105. exports.verify = async (req, res, next) => {
  106. const { verificationToken = '' } = req.params;
  107. const user = await verifyUser({ verificationToken });
  108. if (user) {
  109. const token = signToken(user);
  110. req.user = { token };
  111. }
  112. return next();
  113. };
  114. exports.changePassword = async ({ body: { password }, user }, res) => {
  115. if (password.length < 8) {
  116. return res.status(400).json({ error: 'Password must be at least 8 chars long.' });
  117. }
  118. if (password.length > 64) {
  119. return res.status(400).json({ error: 'Maximum password length is 64.' });
  120. }
  121. const changedUser = await changePassword({ email: user.email, password });
  122. if (changedUser) {
  123. return res.status(200).json({ message: 'Your password has been changed successfully.' });
  124. }
  125. return res.status(400).json({ error: "Couldn't change the password. Try again later" });
  126. };
  127. exports.generateApiKey = async ({ user }, res) => {
  128. const { apikey } = await generateApiKey({ email: user.email });
  129. if (apikey) {
  130. return res.status(201).json({ apikey });
  131. }
  132. return res.status(400).json({ error: 'Sorry, an error occured. Please try again later.' });
  133. };
  134. exports.userSettings = ({ user }, res) =>
  135. res.status(200).json({ apikey: user.apikey || '', customDomain: user.domain || '' });
  136. exports.requestPasswordReset = async ({ body: { email } }, res) => {
  137. const user = await requestPasswordReset({ email });
  138. if (!user) {
  139. return res.status(400).json({ error: "Couldn't reset password." });
  140. }
  141. const mail = await transporter.sendMail({
  142. from: config.MAIL_USER,
  143. to: user.email,
  144. subject: 'Reset your password',
  145. text: resetMailText
  146. .replace('{{resetpassword}}', user.resetPasswordToken)
  147. .replace('{{domain}}', config.DEFAULT_DOMAIN),
  148. html: resetEmailTemplate
  149. .replace('{{resetpassword}}', user.resetPasswordToken)
  150. .replace('{{domain}}', config.DEFAULT_DOMAIN),
  151. });
  152. if (mail.accepted.length) {
  153. return res.status(200).json({ email, message: 'Reset password email has been sent.' });
  154. }
  155. return res.status(400).json({ error: "Couldn't reset password." });
  156. };
  157. exports.resetPassword = async (req, res, next) => {
  158. const { resetPasswordToken = '' } = req.params;
  159. const user = await resetPassword({ resetPasswordToken });
  160. if (user) {
  161. const token = signToken(user);
  162. req.user = { token };
  163. }
  164. return next();
  165. };