auth.mjs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. import express from 'express'
  2. import ExpressBrute from 'express-brute'
  3. import BruteKnex from '../helpers/brute-knex.mjs'
  4. import { find, isEmpty, set } from 'lodash-es'
  5. import path from 'node:path'
  6. import { DateTime } from 'luxon'
  7. export default function () {
  8. const router = express.Router()
  9. const bruteforce = new ExpressBrute(new BruteKnex({
  10. createTable: true,
  11. knex: WIKI.db.knex
  12. }), {
  13. freeRetries: 5,
  14. minWait: 5 * 60 * 1000, // 5 minutes
  15. maxWait: 60 * 60 * 1000, // 1 hour
  16. failCallback: (req, res, next) => {
  17. res.status(401).send('Too many failed attempts. Try again later.')
  18. }
  19. })
  20. /**
  21. * Login form
  22. */
  23. router.get('/login', async (req, res, next) => {
  24. // -> Bypass Login
  25. if (WIKI.config.auth.autoLogin && !req.query.all) {
  26. const stg = await WIKI.db.authentication.query().orderBy('order').first()
  27. const stgInfo = find(WIKI.data.authentication, ['key', stg.strategyKey])
  28. if (!stgInfo.useForm) {
  29. return res.redirect(`/login/${stg.key}`)
  30. }
  31. }
  32. // -> Show Login
  33. res.sendFile(path.join(WIKI.ROOTPATH, 'assets/index.html'))
  34. })
  35. /**
  36. * Social Strategies Login
  37. */
  38. router.get('/login/:strategy', async (req, res, next) => {
  39. try {
  40. await WIKI.db.users.login({
  41. strategy: req.params.strategy
  42. }, { req, res })
  43. } catch (err) {
  44. next(err)
  45. }
  46. })
  47. /**
  48. * Social Strategies Callback
  49. */
  50. router.all('/login/:strategy/callback', async (req, res, next) => {
  51. if (req.method !== 'GET' && req.method !== 'POST') { return next() }
  52. try {
  53. const authResult = await WIKI.db.users.login({
  54. strategy: req.params.strategy
  55. }, { req, res })
  56. res.cookie('jwt', authResult.jwt, { expires: DateTime.now().plus({ years: 1 }).toJSDate() })
  57. const loginRedirect = req.cookies['loginRedirect']
  58. if (loginRedirect === '/' && authResult.redirect) {
  59. res.clearCookie('loginRedirect')
  60. res.redirect(authResult.redirect)
  61. } else if (loginRedirect) {
  62. res.clearCookie('loginRedirect')
  63. res.redirect(loginRedirect)
  64. } else if (authResult.redirect) {
  65. res.redirect(authResult.redirect)
  66. } else {
  67. res.redirect('/')
  68. }
  69. } catch (err) {
  70. next(err)
  71. }
  72. })
  73. /**
  74. * Logout
  75. */
  76. router.get('/logout', async (req, res, next) => {
  77. const redirURL = await WIKI.db.users.logout({ req, res })
  78. req.logout((err) => {
  79. if (err) { return next(err) }
  80. res.clearCookie('jwt')
  81. res.redirect(redirURL)
  82. })
  83. })
  84. /**
  85. * Register form
  86. */
  87. router.get('/register', async (req, res, next) => {
  88. set(res.locals, 'pageMeta.title', 'Register')
  89. const localStrg = await WIKI.db.authentication.getStrategy('local')
  90. if (localStrg.selfRegistration) {
  91. res.sendFile(path.join(WIKI.ROOTPATH, 'assets/index.html'))
  92. } else {
  93. next(new WIKI.Error.AuthRegistrationDisabled())
  94. }
  95. })
  96. /**
  97. * Verify
  98. */
  99. router.get('/verify/:token', bruteforce.prevent, async (req, res, next) => {
  100. try {
  101. const usr = await WIKI.db.userKeys.validateToken({ kind: 'verify', token: req.params.token })
  102. await WIKI.db.users.query().patch({ isVerified: true }).where('id', usr.id)
  103. req.brute.reset()
  104. if (WIKI.config.auth.enforce2FA) {
  105. res.redirect('/login')
  106. } else {
  107. const result = await WIKI.db.users.refreshToken(usr)
  108. res.cookie('jwt', result.token, { expires: DateTime.now().plus({ years: 1 }).toJSDate() })
  109. res.redirect('/')
  110. }
  111. } catch (err) {
  112. next(err)
  113. }
  114. })
  115. /**
  116. * Reset Password
  117. */
  118. router.get('/login-reset/:token', bruteforce.prevent, async (req, res, next) => {
  119. try {
  120. const usr = await WIKI.db.userKeys.validateToken({ kind: 'resetPwd', token: req.params.token })
  121. if (!usr) {
  122. throw new Error('Invalid Token')
  123. }
  124. req.brute.reset()
  125. const changePwdContinuationToken = await WIKI.db.userKeys.generateToken({
  126. userId: usr.id,
  127. kind: 'changePwd'
  128. })
  129. const bgUrl = !isEmpty(WIKI.config.auth.loginBgUrl) ? WIKI.config.auth.loginBgUrl : '/_assets/img/splash/1.jpg'
  130. res.render('login', { bgUrl, hideLocal: WIKI.config.auth.hideLocal, changePwdContinuationToken })
  131. } catch (err) {
  132. next(err)
  133. }
  134. })
  135. /**
  136. * JWT Public Endpoints
  137. */
  138. router.get('/.well-known/jwk.json', function (req, res, next) {
  139. res.json(WIKI.config.certs.jwk)
  140. })
  141. router.get('/.well-known/jwk.pem', function (req, res, next) {
  142. res.send(WIKI.config.certs.public)
  143. })
  144. return router
  145. }