qwik-account-manager/routes/auth.js

160 lines
5.1 KiB
JavaScript
Raw Normal View History

2022-01-22 21:32:14 +01:00
import bcrypt from 'bcrypt'
2022-01-15 19:27:18 +01:00
import crypto from 'crypto'
2022-01-31 20:47:34 +01:00
import jwt from 'jsonwebtoken'
2022-01-15 19:27:18 +01:00
import { Router } from 'express'
2022-01-22 21:32:14 +01:00
import { glauth } from '../libs/database.js'
2022-01-15 19:27:18 +01:00
import execawait from '../libs/execawait.js'
2022-01-31 20:47:34 +01:00
export const SECRET = process.env.TK_SECRET ?? crypto.generateKeySync("aes", { length: 128 }).export().toString()
2022-01-15 19:27:18 +01:00
const AUTH = Router()
let valid = {}
2022-01-23 17:49:27 +01:00
/**
2022-01-31 20:47:34 +01:00
* This is needed because GLAuth (GLAuth's bcrypt) and Node (Node's bcrypt) read hashes differently.
2022-01-23 17:49:27 +01:00
* @param {string} hash The hash to be converted to hex.
* @returns {string} hex of hash
*/
function hash2hex(hash) {
return hash
.split('')
.map(e => e
.charCodeAt(0)
.toString(16)
)
.join('')
}
2022-01-15 19:27:18 +01:00
2022-01-23 15:52:12 +01:00
AUTH.post('/register', async (req, res) => {
2022-01-22 21:32:14 +01:00
const { captcha, password, username } = req.body
2022-01-15 19:27:18 +01:00
2022-01-22 21:32:14 +01:00
if (!username || !password || !captcha)
return(res.send(`Not entered:${username ? '' : ' username,'}${password ? '' : ' password,'}${captcha ? '' : ' captcha'}`))
2022-01-15 19:27:18 +01:00
2022-01-22 21:32:14 +01:00
if (!valid[captcha])
2022-01-15 19:27:18 +01:00
return(res.send("Invalid captcha!"))
2022-01-22 21:32:14 +01:00
const captchaAge = Math.abs((valid[captcha].getTime() - new Date().getTime())/1000)
2022-01-15 19:27:18 +01:00
2022-01-22 21:32:14 +01:00
if (captchaAge > 600)
2022-01-15 19:27:18 +01:00
return(res.send("Invalid captcha!"))
2022-01-31 20:47:34 +01:00
// Does the username match the requirements?
if (!(/^\w{2,20}$/.test(username)))
2022-01-22 21:32:14 +01:00
return(res.send("Username does not match the requirements"))
2023-08-02 16:13:05 +02:00
if ((await glauth.query("SELECT * FROM users WHERE lower(name) = lower($1::text)", [ username ])).rowCount)
2022-01-15 19:27:18 +01:00
return(res.send("User already exists"))
2023-08-02 16:13:05 +02:00
// Expire the captcha.
delete valid[captcha]
2022-04-21 20:48:02 +02:00
bcrypt.hash(password, 12).then(
2022-01-22 21:32:14 +01:00
hash => {
2022-01-23 17:49:27 +01:00
const hexHash = hash2hex(hash)
2022-01-23 15:52:12 +01:00
glauth.query(
"INSERT INTO users(name, mail, primarygroup, passbcrypt, qam_pass) VALUES($1::text, $2::text, 0, $3::text, $4::text)",
[ username, `${username}@qwik.space`, hexHash, hash ]
2022-01-23 15:52:12 +01:00
).then(
() => res.send("Account registered!")
).catch(
err => res.json({ _: "Sorry an error occured!", err })
)
2022-01-22 21:32:14 +01:00
}
)
})
2022-01-23 15:52:12 +01:00
AUTH.post('/login', async (req, res) => {
2022-01-22 21:32:14 +01:00
const { password, username } = req.body
// Was input sent?
if (!username || !password )
return(res.send(`Not entered:${username ? '' : ' username,'}${password ? '' : ' password'}`))
2023-08-02 16:13:05 +02:00
const user = (await glauth.query("SELECT * FROM users WHERE lower(name) = lower($1::text)", [ username ])).rows[0]
2022-01-22 21:32:14 +01:00
if (!user)
return(res.send("User doesn't exist!"))
2022-01-15 19:27:18 +01:00
2022-01-23 17:49:27 +01:00
bcrypt.compare(password, user.qam_pass).then(
2022-01-31 20:47:34 +01:00
async match => {
2022-01-22 21:32:14 +01:00
if (!match)
2023-08-02 16:13:05 +02:00
return res.send("Password is incorrect!")
2022-01-15 19:27:18 +01:00
2022-01-31 20:47:34 +01:00
const bearer = {
name: user.name,
id: user.uidnumber,
group: (await glauth.query("SELECT name FROM ldapgroups WHERE gidnumber = $1::int", [ user.primarygroup ])).rows[0].name
2022-01-31 20:47:34 +01:00
}
const token = jwt.sign(
bearer,
SECRET,
{ expiresIn: '1h' }
)
const fourHoursInMillis = 60 * 60 * 1000
res.cookie(
'api-token',
token,
{
expires: new Date(Date.now() + fourHoursInMillis),
httpOnly: true,
signed: true
}
).redirect('/manager')
2022-01-22 21:32:14 +01:00
}
)
2022-01-15 19:27:18 +01:00
})
2022-04-21 20:48:02 +02:00
AUTH.post('/changePass', async (req, res) => {
const { newPass, oldPass } = req.body
// Was input sent?
if (!newPass|| !oldPass )
return(res.send(`Not entered:${newPass ? '' : ' new password,'}${oldPass ? '' : ' old password'}`))
const token = jwt.verify(req.signedCookies["api-token"], SECRET)
if (!token)
return res.send("Token error! Please sign in ag ain anusername = token.named retry...")
const username = token.name
2023-08-02 16:13:05 +02:00
const user = (await glauth.query("SELECT * FROM users WHERE lower(name) = lower($1::text)", [ username ])).rows[0]
2022-04-21 20:48:02 +02:00
if (!user)
return(res.send("User doesn't exist!"))
bcrypt.compare(oldPass, user.qam_pass).then(
async match => {
if (!match)
return res.send("Password's is incorrect!")
const newPassHash = await bcrypt.hash(newPass, 12)
const newPassHexHash = hash2hex(newPassHash)
glauth.query("UPDATE users SET qam_pass = $1::text, passbcrypt = $2::text WHERE name = $3::text", [ newPassHash, newPassHexHash, username ])
.then(
() => res.send("Successfully changed password!")
).catch(
() => res.send("Database error while changing password!")
)
}
)
})
2022-01-15 19:27:18 +01:00
AUTH.get('/captcha', async (req, res) => {
const captcha = crypto.randomBytes(3).toString('hex')
2022-01-23 15:52:12 +01:00
await execawait(`./captcha.sh ${captcha} > captcha.png`)
// Make it valid for 10 minutes
2022-04-21 20:48:02 +02:00
valid[captcha] = new Date(Date.now() + 10 * 60 * 1000)
2022-01-23 15:52:12 +01:00
// Send the captcha image
res.contentType('image/png')
.sendFile('captcha.png', { root: './' })
2022-01-15 19:27:18 +01:00
})
export default AUTH