From 29292603a533ca665d5f03674a32697d6043f048 Mon Sep 17 00:00:00 2001 From: Ben Date: Tue, 4 Sep 2018 16:34:58 +0100 Subject: [PATCH] Token authentication on signup /user/ POST route working, started login and auth --- package-lock.json | 114 ++++++++++++++++-- package.json | 3 + src/controllers/index.js | 3 +- src/controllers/loginController.js | 55 +++++++++ src/controllers/middleware/rateLimits.js | 5 +- src/controllers/routes/router.js | 10 +- .../{user.js => userController.js} | 23 +++- src/models/api/API.js | 2 +- src/models/api/APIErrors.js | 3 +- src/models/api/userResponses.js | 23 +++- src/models/database/baseDatabase.js | 6 +- src/models/database/database.js | 1 + src/models/database/sqlite/database.sqlite | Bin 28672 -> 28672 bytes src/models/database/tokens.js | 29 +++++ src/models/database/users.js | 18 ++- src/models/user/passwords.js | 25 ++++ src/models/user/token.js | 16 +++ src/models/user/tokenGen.js | 10 -- src/models/user/user.js | 4 + 19 files changed, 298 insertions(+), 52 deletions(-) create mode 100644 src/controllers/loginController.js rename src/controllers/{user.js => userController.js} (81%) create mode 100644 src/models/database/tokens.js create mode 100644 src/models/user/passwords.js create mode 100644 src/models/user/token.js delete mode 100644 src/models/user/tokenGen.js diff --git a/package-lock.json b/package-lock.json index 87ef7f1..06584b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1607,6 +1607,11 @@ "tweetnacl": "^0.14.3" } }, + "bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" + }, "big.js": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", @@ -1798,6 +1803,11 @@ "isarray": "^1.0.0" } }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -2421,6 +2431,14 @@ "safer-buffer": "^2.1.0" } }, + "ecdsa-sig-formatter": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz", + "integrity": "sha1-HFlQAPBKiJffuFAAiSoPTDOvhsM=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -3018,16 +3036,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -3045,8 +3061,7 @@ "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", @@ -3152,7 +3167,7 @@ "dev": true, "optional": true, "requires": { - "minimatch": "3.0.4" + "minimatch": "^3.0.4" } }, "inflight": { @@ -3194,9 +3209,8 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -4140,6 +4154,11 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, + "js-sha256": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", + "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" + }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", @@ -4186,6 +4205,29 @@ "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", "dev": true }, + "jsonwebtoken": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.3.0.tgz", + "integrity": "sha512-oge/hvlmeJCH+iIz1DwcO7vKPkNGJHhgkspk8OH3VKlw+mbi42WtD4ig1+VXRln765vxptAv+xT26Fd3cteqag==", + "requires": { + "jws": "^3.1.5", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -4197,6 +4239,25 @@ "verror": "1.10.0" } }, + "jwa": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.6.tgz", + "integrity": "sha512-tBO/cf++BUsJkYql/kBbJroKOgHWEigTKBAjjBEmrMGYd1QMBC74Hr4Wo2zCZw6ZrVhlJPvoMrkcOnlWR/DJfw==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.10", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.5.tgz", + "integrity": "sha512-GsCSexFADNQUr8T5HPJvayTjvPIfoyJPtLQBwn5a4WZQchcrPMPMAWcC1AzJVRDKyD6ZPROPAxgv6rfHViO4uQ==", + "requires": { + "jwa": "^1.1.5", + "safe-buffer": "^5.0.1" + } + }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", @@ -4250,6 +4311,41 @@ "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", "dev": true }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, "long": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", diff --git a/package.json b/package.json index 62770ce..612a6c4 100644 --- a/package.json +++ b/package.json @@ -5,10 +5,13 @@ "main": "index.js", "dependencies": { "bcrypt": "^3.0.0", + "bcryptjs": "^2.4.3", "body-parser": "^1.18.3", "colors": "^1.3.1", "express": "^4.16.3", + "js-sha256": "^0.9.0", "json-stringify-safe": "^5.0.1", + "jsonwebtoken": "^8.3.0", "regenerator-runtime": "^0.12.1", "sequelize": "^4.38.0", "sqlite3": "^4.0.2" diff --git a/src/controllers/index.js b/src/controllers/index.js index 38623dd..fd4ef07 100644 --- a/src/controllers/index.js +++ b/src/controllers/index.js @@ -1,3 +1,4 @@ export let Controllers = {}; -Controllers.UserController = require('./user').UserController; +Controllers.UserController = require('./userController').UserController; +Controllers.LoginController = require('./loginController').LoginController; diff --git a/src/controllers/loginController.js b/src/controllers/loginController.js new file mode 100644 index 0000000..6fd9ba6 --- /dev/null +++ b/src/controllers/loginController.js @@ -0,0 +1,55 @@ +import {ControllerHandler} from './controllerHandler'; +import {API} from '../models/api/api'; +import {Database} from '../models/database/database' +import {User} from '../models/user/user'; + +export class LoginController extends ControllerHandler { + static async authenticate(req, res, next) { + let errors = new API.errors(res); + + let ip = req.connection.remoteAddress; + if (ip.startsWith('::ffff:')) ip = ip.substring(7); + + let username = req.body.username || undefined; + let email = req.body.email || undefined; + let password = req.body.password || undefined; + + if (!password) errors.addError(400, 'Bad request', 'A password is required'); + if (!username && !email) errors.addError(400, 'Bad request', 'A username or email is required'); + + if (errors.count() > 0) { + errors.endpoint(); + next(); + return; + } + + let id; + if (!username /*If they're loging in with email*/) { + if (await Database.users.getID('email', email) == -1) errors.addError(422, 'Unprocessable entity', 'There is no user with that email'); + id = await Database.users.getID('email', email); + } else { + if (await Database.users.getID('username', username) == -1) errors.addError(422, 'Unprocessable entity', 'There is no user with that username'); + id = await Database.users.getID('username', username); + } + + if (errors.count() > 0) { + errors.endpoint(); + next(); + return; + } + + let user = await Database.users.getUserByID(id); + let match = await User.Password.compare(password, user.password); + + if (!match) errors.addError(401, 'Unauthorized', 'Incorrect password for user'); + + if (errors.count() > 0) { + errors.endpoint(); + next(); + return; + } + + res.end('Welcome') + next(); + } +} diff --git a/src/controllers/middleware/rateLimits.js b/src/controllers/middleware/rateLimits.js index 2e22893..c376d79 100644 --- a/src/controllers/middleware/rateLimits.js +++ b/src/controllers/middleware/rateLimits.js @@ -12,7 +12,8 @@ export class RateLimits extends MiddleWare{ if (!buckets[ip]) { Logger.debug(`New rate limiting bucket`); RateLimits.newBucket(ip); - next(); + + MiddleWare.analytics(req, res, next); return; } @@ -25,7 +26,7 @@ export class RateLimits extends MiddleWare{ } buckets[ip].tokens.pop(); - next(); + MiddleWare.analytics(req, res, next); } static newBucket(ip) { diff --git a/src/controllers/routes/router.js b/src/controllers/routes/router.js index 56ff231..95f1e41 100644 --- a/src/controllers/routes/router.js +++ b/src/controllers/routes/router.js @@ -12,12 +12,14 @@ export class Router { app = Server.App; - app.get('/', [MiddleWare.RateLimits.request, MiddleWare.analytics, Router.frontPage]); + app.get('/', [MiddleWare.RateLimits.request, Router.frontPage]); - app.get('/user/:id', [MiddleWare.RateLimits.request, MiddleWare.analytics,]); - app.delete('/user/:id', [MiddleWare.RateLimits.request, MiddleWare.analytics,]); - app.post('/user', [MiddleWare.RateLimits.request, MiddleWare.analytics, Controllers.UserController.newUser]); + app.get('/user/:id', [MiddleWare.RateLimits.request]); + app.delete('/user/:id', [MiddleWare.RateLimits.request]); + app.post('/user', [MiddleWare.RateLimits.request, Controllers.UserController.newUser]); + app.post('/login', [MiddleWare.RateLimits.request, Controllers.LoginController.authenticate]) + app.get('*', [MiddleWare.RateLimits.request, StatusCodes.pageNotFound]); Logger.info('HTTP endpoints settup'); } diff --git a/src/controllers/user.js b/src/controllers/userController.js similarity index 81% rename from src/controllers/user.js rename to src/controllers/userController.js index ef59eaa..b9c90bf 100644 --- a/src/controllers/user.js +++ b/src/controllers/userController.js @@ -3,7 +3,7 @@ import bcrypt from 'bcrypt'; import {Logger} from '../models/logger'; import {ControllerHandler} from './controllerHandler'; import {API} from '../models/api/api'; -import {Database} from '../models/database/database'; +import {Database} from '../models/database/database' import {User} from '../models/user/user'; export class UserController extends ControllerHandler { @@ -27,16 +27,27 @@ export class UserController extends ControllerHandler { if (await Database.users.getID('username', username) != -1) errors.addError(422, 'Unprocessable entity', 'A user with that username allready exists'); if (await Database.users.getID('email', email) != -1) errors.addError(422, 'Unprocessable entity', 'A user with that email allready exists'); - let id = new Date().getTime(); - let token = "1234"; - if (errors.count() > 0) { errors.endpoint(); next(); return; } + + let response = new API.user(res, id, username, email, new Date().toLocaleString()); - let user = new User(id, username, password, email, ip, 1234) + + let encryptedPass = await User.Password.gen(password); + password = null; // Cleaning password from memory + + console.log(encryptedPass); + + let status = response.getStatus; + + let id = new Date().getTime(); + let token = await User.Token.gen(status, id, encryptedPass); + response.Token = token; + + let user = new User(id, username, encryptedPass, email, ip, 1234); let success = await user.insert(); if (success == -1) { errors.addError(500, 'Internal server error').endpoint(); @@ -44,7 +55,7 @@ export class UserController extends ControllerHandler { return; } - new API.user(res, id, username, email, new Date().toLocaleString(), token).endpoint(); + response.endpoint(); next(); } diff --git a/src/models/api/API.js b/src/models/api/API.js index 8b4dbaa..88ba461 100644 --- a/src/models/api/API.js +++ b/src/models/api/API.js @@ -7,4 +7,4 @@ export class API extends BaseAPI { } API.errors = require('./APIErrors').APIErrors; -API.user = require('./userResponses').User; +API.user = require('./userResponses').UserAPI; diff --git a/src/models/api/APIErrors.js b/src/models/api/APIErrors.js index d242bd1..932399e 100644 --- a/src/models/api/APIErrors.js +++ b/src/models/api/APIErrors.js @@ -31,5 +31,4 @@ export class APIErrors extends API { this.res .status(this.errors.status.code) .end(JSON.stringify(this.errors, false, 4)); - } -} + }} diff --git a/src/models/api/userResponses.js b/src/models/api/userResponses.js index ac94b1d..c28dcdb 100644 --- a/src/models/api/userResponses.js +++ b/src/models/api/userResponses.js @@ -1,8 +1,15 @@ import {API} from './API'; +import {User} from '../../models/user/user'; -export class User extends API { - constructor(res, id, username, email, updated, token) { +export class UserAPI extends API { + get getStatus() {return this.response.status} + + set Token(t) {this.response.data[0].token = t} + set Pass(p) {this.password = p} + + constructor(res, id, username, email, updated) { super(); + this.res = res; this.response = { status: { error: false, @@ -19,12 +26,22 @@ export class User extends API { email: email, updated: updated }, - token: token + token: null } ] } } + sign(password) { + if (password) { + return this.response.data[0].token = User.Token.gen(this.response.status, this.id, password); + } else if (this.password) { + return this.response.data[0].token = User.Token.gen(this.response.status, this.id, this.password); + } else { + return -1; + } + } + endpoint() { this.res.status(200).end(JSON.stringify(this.response, false, 4)); } diff --git a/src/models/database/baseDatabase.js b/src/models/database/baseDatabase.js index ed93257..cc58dc8 100644 --- a/src/models/database/baseDatabase.js +++ b/src/models/database/baseDatabase.js @@ -50,10 +50,8 @@ export class BaseDatabase { primaryKey: true, unique: true }, - selector: Sequelize.TEXT, - validator: Sequelize.TEXT, - uid: Sequelize.BIGINT, - expires: Sequelize.TEXT + token: Sequelize.TEXT, + passhash: Sequelize.TEXT }, { tableName: `auth` }); diff --git a/src/models/database/database.js b/src/models/database/database.js index 982950f..7947242 100644 --- a/src/models/database/database.js +++ b/src/models/database/database.js @@ -28,3 +28,4 @@ export class Database extends BaseDatabase { } Database.users = require('./users').UserTools; +Database.auth = require('./tokens').TokenTools; diff --git a/src/models/database/sqlite/database.sqlite b/src/models/database/sqlite/database.sqlite index 0a882ad7f870054eb84180e089199ed8c4a95df4..13659336c3585fc0f566e4dd044fedcd9f2571b4 100644 GIT binary patch delta 197 zcmZp8z}WDBaRZwGivt6{!)8GNGybSXTSi`1*+y1VVRa5sK~GP{)TL_QZx!Su=A{}K z7^oN}sTdllSSCB`g}RxUo4L67_&Fwd6{qM22B)U`SsHj*dPXGYmHAhA7o;a1IKNY<|^74n`hUSy@wIbq-NMPfy0wX&3*kDac98OEoYs z2msNB28NSo$xFGKS{NIfnOPcH8XFoJo9G%E85mmV8d&NY7%CVUSQ(jHnON!>S(+$l X8yHv_7~qiI{8OG;fCXg7A_E5i#dRg= diff --git a/src/models/database/tokens.js b/src/models/database/tokens.js new file mode 100644 index 0000000..382e03b --- /dev/null +++ b/src/models/database/tokens.js @@ -0,0 +1,29 @@ +import {BaseDatabase} from './baseDatabase'; +import {Logger} from '../logger'; + +export class TokenTools extends BaseDatabase { + static async listAll() { + let Auth = BaseDatabase.Auth; + return Auth.findAll(); + } + + static async newToken(id, token, passHash) { + + } + + static async getTokenByID(id) { + + } + + static async getIDByToken(token) { + + } + + static async getTokenByPassHash(hash) { + + } + + static async updateToken(id, newToken) { + + } +} diff --git a/src/models/database/users.js b/src/models/database/users.js index c6541a5..4718d4f 100644 --- a/src/models/database/users.js +++ b/src/models/database/users.js @@ -60,31 +60,29 @@ export class UserTools extends BaseDatabase { let User = BaseDatabase.User; try { + let user; if (column == 'id') { return search; } else if (column == 'username') { - let user = await User.findOne({where: {username: search}}); + user = await User.findOne({where: {username: search}}); if (user == null) return -1; - return user; } else if (column == 'email') { - let user = await User.findOne({where: {email: search}}); + user = await User.findOne({where: {email: search}}); if (user == null) return -1; - return user; } else if (column == 'password') { - let user = await User.findOne({where: {password: search}}); + user = await User.findOne({where: {password: search}}); if (user == null) return -1; - return user; } else if (column == 'ip') { - let user = await User.findOne({where: {ip: search}}); + user = await User.findOne({where: {ip: search}}); if (user == null) return -1; - return user; } else if (column == 'authcode') { - let user = await User.findOne({where: {authcode: search}}); + user = await User.findOne({where: {authcode: search}}); if (user == null) return -1; - return user; } else { return -1 } + return user.id; + } catch (e) { Logger.error(`An error occured while querying the id of a user where ${column} is ${search}: ${e}`); return -1; diff --git a/src/models/user/passwords.js b/src/models/user/passwords.js new file mode 100644 index 0000000..0484be9 --- /dev/null +++ b/src/models/user/passwords.js @@ -0,0 +1,25 @@ +import sha256 from 'js-sha256'; +import bcrypt from 'bcrypt'; + +import {User} from './user'; +import {Logger} from '../logger'; + +export class Password extends User { + static async gen(passwordSecret) { + let salt = await bcrypt.genSaltSync(10); + let prehash = await sha256(passwordSecret) + let hash = await bcrypt.hashSync(prehash, salt); + + return hash; + } + + static async compare(password, hashToCompare) { + try { + let prehash = await sha256(password); + let res = await bcrypt.compareSync(prehash, hashToCompare); + return res; + } catch (e) { + Logger.error(`Somthing went wrong with comparing password hashes: ${e}`); + } + } +} diff --git a/src/models/user/token.js b/src/models/user/token.js new file mode 100644 index 0000000..10a5e4f --- /dev/null +++ b/src/models/user/token.js @@ -0,0 +1,16 @@ +import jwt from 'jsonwebtoken'; + +import {User} from './user'; + +export class Token extends User { + static async gen(status, clientID, clientSecret) { + let preGen = clientID + ':' + clientSecret; + + let token = await jwt.sign(status, preGen); + return token; + } + + static async check() { + + } +} diff --git a/src/models/user/tokenGen.js b/src/models/user/tokenGen.js deleted file mode 100644 index a7f7c87..0000000 --- a/src/models/user/tokenGen.js +++ /dev/null @@ -1,10 +0,0 @@ -import {User} from './user'; - -class tokenGen extends User { - static async gen() { - - let success = false; - - - } -} diff --git a/src/models/user/user.js b/src/models/user/user.js index c3d1aa0..30e233c 100644 --- a/src/models/user/user.js +++ b/src/models/user/user.js @@ -1,6 +1,7 @@ import {Logger} from '../logger'; import {BaseUser} from './baseUser'; import {Database} from '../database/database'; +import { UserTools } from '../database/users'; export class User extends BaseUser { constructor(id, username, password, email, ip, authcode) { @@ -17,3 +18,6 @@ export class User extends BaseUser { return this._instance.delete(); } } + +User.Token = require('./token').Token; +User.Password = require('./passwords').Password; \ No newline at end of file