You can now log in - fantastic! :D

This commit is contained in:
BurnyLlama 2022-01-31 20:47:34 +01:00
parent a10f234a2a
commit c0adc5e96b
7 changed files with 71 additions and 10 deletions

View File

@ -1,3 +1,5 @@
import cookieParser from 'cookie-parser'
import crypto from 'crypto'
import { dbInit } from './libs/database.js' import { dbInit } from './libs/database.js'
import dotenv from 'dotenv' import dotenv from 'dotenv'
import express from 'express' import express from 'express'
@ -19,6 +21,7 @@ await dbInit()
} }
) )
APP.use(cookieParser(process.env.CK_SECRET ?? crypto.generateKeySync("aes", { length: 128 }).export().toString(), {}))
APP.use(express.urlencoded({ extended: true })) APP.use(express.urlencoded({ extended: true }))
APP.use('/static', express.static('static')) APP.use('/static', express.static('static'))

View File

@ -11,8 +11,10 @@
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"bcrypt": "^5.0.1", "bcrypt": "^5.0.1",
"cookie-parser": "^1.4.6",
"dotenv": "^12.0.3", "dotenv": "^12.0.3",
"express": "^4.17.2", "express": "^4.17.2",
"jsonwebtoken": "^8.5.1",
"nunjucks": "^3.2.3", "nunjucks": "^3.2.3",
"pg": "^8.7.1" "pg": "^8.7.1"
} }

View File

@ -1,14 +1,16 @@
import bcrypt from 'bcrypt' import bcrypt from 'bcrypt'
import crypto from 'crypto' import crypto from 'crypto'
import jwt from 'jsonwebtoken'
import { Router } from 'express' import { Router } from 'express'
import { glauth } from '../libs/database.js' import { glauth } from '../libs/database.js'
import execawait from '../libs/execawait.js' import execawait from '../libs/execawait.js'
export const SECRET = process.env.TK_SECRET ?? crypto.generateKeySync("aes", { length: 128 }).export().toString()
const AUTH = Router() const AUTH = Router()
let valid = {} let valid = {}
/** /**
* ... * This is needed because GLAuth (GLAuth's bcrypt) and Node (Node's bcrypt) read hashes differently.
* @param {string} hash The hash to be converted to hex. * @param {string} hash The hash to be converted to hex.
* @returns {string} hex of hash * @returns {string} hex of hash
*/ */
@ -25,11 +27,9 @@ function hash2hex(hash) {
AUTH.post('/register', async (req, res) => { AUTH.post('/register', async (req, res) => {
const { captcha, password, username } = req.body const { captcha, password, username } = req.body
// Was input sent?
if (!username || !password || !captcha) if (!username || !password || !captcha)
return(res.send(`Not entered:${username ? '' : ' username,'}${password ? '' : ' password,'}${captcha ? '' : ' captcha'}`)) return(res.send(`Not entered:${username ? '' : ' username,'}${password ? '' : ' password,'}${captcha ? '' : ' captcha'}`))
// is captcha valid
if (!valid[captcha]) if (!valid[captcha])
return(res.send("Invalid captcha!")) return(res.send("Invalid captcha!"))
@ -38,10 +38,10 @@ AUTH.post('/register', async (req, res) => {
if (captchaAge > 600) if (captchaAge > 600)
return(res.send("Invalid captcha!")) return(res.send("Invalid captcha!"))
// expire the captcha // Expire the captcha.
delete valid[captcha] delete valid[captcha]
// does the username match the requirements // Does the username match the requirements?
if (!(/^(?=[a-zA-Z0-9]{2,20}$).*$/.test(username))) if (!(/^(?=[a-zA-Z0-9]{2,20}$).*$/.test(username)))
return(res.send("Username does not match the requirements")) return(res.send("Username does not match the requirements"))
@ -59,7 +59,6 @@ AUTH.post('/register', async (req, res) => {
).catch( ).catch(
err => res.json({ _: "Sorry an error occured!", err }) err => res.json({ _: "Sorry an error occured!", err })
) )
} }
) )
}) })
@ -77,11 +76,32 @@ AUTH.post('/login', async (req, res) => {
return(res.send("User doesn't exist!")) return(res.send("User doesn't exist!"))
bcrypt.compare(password, user.qam_pass).then( bcrypt.compare(password, user.qam_pass).then(
match => { async match => {
if (!match) if (!match)
return res.send("Password's is incorrect!") return res.send("Password's is incorrect!")
return res.send("Welcome " + user.name + "!") const bearer = {
name: user.name,
id: user.uidnumber,
group: (await glauth.query("SELECT name FROM groups WHERE gidnumber = $1::int", [ user.primarygroup ])).rows[0].name
}
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')
} }
) )
}) })

View File

@ -1,11 +1,31 @@
import jwt from 'jsonwebtoken'
import { Router } from 'express' import { Router } from 'express'
import AUTH from './auth.js' import AUTH, { SECRET } from './auth.js'
const ROUTES = Router() const ROUTES = Router()
ROUTES.get('/', (_, res) => res.render("pages/landing.njk")) ROUTES.get('/', (_, res) => res.render("pages/landing.njk"))
ROUTES.get('/login', (_, res) => res.render("pages/login.njk")) ROUTES.get('/login', (_, res) => res.render("pages/login.njk"))
ROUTES.get('/register', (_, res) => res.render("pages/register.njk")) ROUTES.get('/register', (_, res) => res.render("pages/register.njk"))
ROUTES.get(
'/manager',
(req, res) => {
const apiToken = req.signedCookies['api-token'] ?? null
if (!apiToken)
return res.redirect('/login')
try {
if (!jwt.verify(apiToken, SECRET))
return res.redirect('/login')
const bearer = jwt.decode(apiToken)
res.render('pages/manager.njk', { user: bearer })
} catch (error) {
res.redirect('/login')
}
}
)
ROUTES.use('/auth', AUTH) ROUTES.use('/auth', AUTH)
export default ROUTES export default ROUTES

View File

@ -65,7 +65,6 @@ h6 {
p { p {
margin: .5em 0 .25em 0; margin: .5em 0 .25em 0;
text-align: justify;
overflow-wrap: break-word; overflow-wrap: break-word;
} }

View File

@ -14,5 +14,7 @@
<input type="password" id="password" name="password" placeholder="VerySecurePassword;PleaseDon'tHackMe!LOL"> <input type="password" id="password" name="password" placeholder="VerySecurePassword;PleaseDon'tHackMe!LOL">
<input type="submit" value="Log in!"> <input type="submit" value="Log in!">
<p>Logging in will store an API token in a cookie. 😉</p>
</form> </form>
{% endblock %} {% endblock %}

15
views/pages/manager.njk Normal file
View File

@ -0,0 +1,15 @@
{% extends "templates/base.njk" %}
{% block head %}
<title>{{ user.name }}@qam</title>
{% endblock %}
{% block body %}
{# <img class="logo" src="https://qwik.space/assets/images/logo.svg" alt="qwik's logo"> #}
<header>Welcome {{ user.name }}! :D</header>
<h1>This is your account manager!</h1>
<p>
Currently no features are implemented, but we want to implement at least password changing.
It would also be nice to have proper account deletion. (For now, if you want to delete your account contact an admin!)
</p>
{% endblock %}