auth.mjs 4.5 KB

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