From 1f0830394786d7e18c2227582492cee254b01c3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn-Michael=20Miehe?= Date: Sun, 16 Dec 2018 22:51:08 +0100 Subject: [PATCH] usable authentication and session management --- inc/fftcgdb.coffee | 64 +++++++++++++++++++++++---------- inc/fftcgrouter.coffee | 33 ++++++++++------- server.coffee | 9 ++--- src/index.coffee | 82 +++++++++++++++++++++++++++++++----------- src/style/custom.scss | 2 +- views/index.pug | 7 ++-- 6 files changed, 136 insertions(+), 61 deletions(-) diff --git a/inc/fftcgdb.coffee b/inc/fftcgdb.coffee index 6cd94e5..a3155c6 100644 --- a/inc/fftcgdb.coffee +++ b/inc/fftcgdb.coffee @@ -16,10 +16,10 @@ FFTCGDB = (filename) -> CREATE TABLE IF NOT EXISTS users ( login text NOT NULL COLLATE NOCASE, pwdhash text NOT NULL, - session text, UNIQUE(login) ); """ + console.log "[FFTCGDB] Connected to '#{@filename}'" return @@ -35,46 +35,74 @@ FFTCGDB::register = (login, password) -> that = @ new Promise (resolve, reject) -> - # validate username + # validate user input + if login == '' or password == '' + # no user name or password given + console.log "[FFTCGDB] reg: user name '#{login}' or password empty" + reject 'invalid' # hash password bcrypt.hash password, saltRounds, (err, hash) -> - reject 'hash' if err + if err + console.log "[FFTCGDB] reg: hash fail for name '#{login}'" + reject 'hash' + # try creating row in users table - that.db.run "INSERT INTO users (login, pwdhash) VALUES ('#{login}', '#{hash}');", (err) -> + stmt = that.db.prepare 'INSERT INTO users (login, pwdhash) VALUES (?, ?)' + stmt.run [login, hash], (err) -> if err - if err.code == 'SQLITE_CONSTRAINT' - reject 'existence' - else - reject 'db' + console.log "[FFTCGDB] reg: DB fail '#{err.code}' for name '#{login}'" + stmt.finalize() + # reduce attack surface, don't disclose user names + reject 'db' # user already exists else + console.log "[FFTCGDB] reg: OK '#{login}'" + stmt.finalize() # registration successful - resolve @lastID + resolve + uid: @lastID + login: login FFTCGDB::login = (login, password) -> that = @ new Promise (resolve, reject) -> - # validate username - # get users table row - that.db.all "SELECT rowid, pwdhash FROM users WHERE login = '#{login}';", (err, rows) -> + stmt = that.db.prepare 'SELECT rowid, login, pwdhash FROM users WHERE login = ?' + stmt.get [login], (err, row) -> if err + console.log "[FFTCGDB] login: DB fail '#{err.code}' for name '#{login}'" + stmt.finalize() reject 'db' - else if rows.length == 0 - # hashing the password for timing attack reasons + else if not row + # hash the password for timing attack reasons bcrypt.hash password, saltRounds, (err, hash) -> - reject 'existence' + console.log "[FFTCGDB] login: nonexistent '#{login}'" + stmt.finalize() + # reduce attack surface, don't disclose user names + reject 'login' # user doesnt exist else - row = rows[0] bcrypt.compare password, row.pwdhash, (err, res) -> - reject 'hash' if err + if err + console.log "[FFTCGDB] login: hash fail for name '#{login}'" + reject 'hash' + if res == true - resolve row.rowid + console.log "[FFTCGDB] login: OK '#{row.login}'" + stmt.finalize() + # login successful + resolve + uid: row.rowid + login: row.login + else + console.log "[FFTCGDB] login: wrong password for '#{login}'" + stmt.finalize() + # login failed reject 'login' + module.exports = FFTCGDB diff --git a/inc/fftcgrouter.coffee b/inc/fftcgrouter.coffee index c4ebb39..9b6259c 100644 --- a/inc/fftcgrouter.coffee +++ b/inc/fftcgrouter.coffee @@ -1,5 +1,5 @@ # node libraries -FFTCGROUTER = (require 'express').Router() +express = (require 'express') path = (require 'path') # my libraries @@ -8,17 +8,22 @@ FFTCGDB = (require './fftcgdb') # open fftcg db fftcgdb = new FFTCGDB path.resolve(__dirname, '../fftcg.db') +# create router +FFTCGROUTER = express.Router() +FFTCGROUTER.use express.static path.resolve(__dirname, '../public_html') + # register user FFTCGROUTER.post '/register', (req, res) -> fftcgdb.register req.body.login, req.body.password - .then (userid) -> - console.log "registered '#{req.body.login}'" + .then (user) -> + # registration successful, return JSON status res.json status: 'ok' - uid: userid - text: req.body.login + uid: user.id + login: user.login + .catch (err) -> - console.log "failed to register '#{req.body.login}'" + # registration failed, return JSON status res.json status: 'fail' text: err @@ -26,17 +31,19 @@ FFTCGROUTER.post '/register', (req, res) -> # log in user FFTCGROUTER.post '/login', (req, res) -> fftcgdb.login req.body.login, req.body.password - .then (userid) -> - req.session.userID = userid - req.session.userLogin = req.body.login + .then (user) -> + # login successful, save stuff in session + req.session.user = user req.session.save() - console.log "logged in '#{req.body.login}'" + + # return JSON status res.json status: 'ok' - uid: userid - text: req.body.login + uid: user.uid + login: user.login + .catch (err) -> - console.log "failed to login '#{req.body.login}'" + # login failed, return JSON status res.json status: 'fail' text: err diff --git a/server.coffee b/server.coffee index f81072f..c4e49ef 100644 --- a/server.coffee +++ b/server.coffee @@ -21,17 +21,14 @@ app.use bodyParser.urlencoded sessionMiddleware = FFTCGSESSION(app) app.use sessionMiddleware -# REST routes +# routes app.use FFTCGROUTER -# Static content -app.use express.static path.resolve(__dirname, 'public_html') - # Templates app.set 'view engine', 'pug' app.get '/:template.html', (req, res) -> - if req.session - console.log "logged in as '#{req.session.userLogin}'" + if req.session.user + console.log "[FFTCG] user is '#{req.session.user.login}'" res.render (req.params.template + '.pug') # socket.io diff --git a/src/index.coffee b/src/index.coffee index cc0c2ff..d0908ab 100644 --- a/src/index.coffee +++ b/src/index.coffee @@ -1,23 +1,39 @@ # libs window.$ = require('jquery') +# import bootstrap +require './style/custom.scss' +require 'bootstrap/js/dist/alert' + +window.showAlert = (level, content) -> + ($ '.alert').alert 'close' + + ($ '#alert-area').append ($ '
', + class: "alert alert-#{level} alert-dismissible fade show" + role: 'alert' + .append content, ($ '