diff --git a/lib/new-admin/graphql/modules/authentication.js b/lib/new-admin/graphql/modules/authentication.js index 5520b328..3e1ebccb 100644 --- a/lib/new-admin/graphql/modules/authentication.js +++ b/lib/new-admin/graphql/modules/authentication.js @@ -1,5 +1,5 @@ const otplib = require('otplib') -const bcrypt = require('bcrypt') +const argon2 = require('argon2') const loginHelper = require('../../services/login') const T = require('../../../time') @@ -14,7 +14,7 @@ const authenticateUser = (username, password) => { .then(user => { const hashedPassword = user.password if (!hashedPassword || !user.enabled) throw new authErrors.InvalidCredentialsError() - return Promise.all([bcrypt.compare(password, hashedPassword), hashedPassword]) + return Promise.all([argon2.verify(hashedPassword, password), hashedPassword]) }) .then(([isMatch, hashedPassword]) => { if (!isMatch) throw new authErrors.InvalidCredentialsError() @@ -76,7 +76,7 @@ const get2FASecret = (username, password) => { return authenticateUser(username, password) .then(user => { const secret = otplib.authenticator.generateSecret() - const otpauth = otplib.authenticator.keyuri(user.username, 'Lamassu Industries', secret) + const otpauth = otplib.authenticator.keyuri(user.username, 'Lamassu', secret) return Promise.all([users.saveTemp2FASecret(user.id, secret), secret, otpauth]) }) .then(([_, secret, otpauth]) => { @@ -125,7 +125,7 @@ const validateReset2FALink = token => { }) .then(user => { const secret = otplib.authenticator.generateSecret() - const otpauth = otplib.authenticator.keyuri(user.username, 'Lamassu Industries', secret) + const otpauth = otplib.authenticator.keyuri(user.username, 'Lamassu', secret) return Promise.all([users.saveTemp2FASecret(user.id, secret), user, secret, otpauth]) }) .then(([_, user, secret, otpauth]) => { diff --git a/lib/new-admin/graphql/types/transaction.type.js b/lib/new-admin/graphql/types/transaction.type.js index a4a553f9..f9a062ee 100644 --- a/lib/new-admin/graphql/types/transaction.type.js +++ b/lib/new-admin/graphql/types/transaction.type.js @@ -47,7 +47,7 @@ const typeDef = gql` type Query { transactions(from: Date, until: Date, limit: Int, offset: Int, deviceId: ID): [Transaction] @auth - transactionsCsv(from: Date, until: Date, limit: Int, offset: Int): String + transactionsCsv(from: Date, until: Date, limit: Int, offset: Int): String @auth } ` diff --git a/lib/users.js b/lib/users.js index 9b7e3522..ef0b3aa6 100644 --- a/lib/users.js +++ b/lib/users.js @@ -1,7 +1,7 @@ const _ = require('lodash/fp') const pgp = require('pg-promise')() const crypto = require('crypto') -const bcrypt = require('bcrypt') +const argon2 = require('argon2') const uuid = require('uuid') const db = require('./db') @@ -107,7 +107,7 @@ function createAuthToken (userID, type) { function updatePassword (token, id, password) { return validateAuthToken(token, 'reset_password').then(res => { if (!res.success) throw new Error('Failed to verify password reset token') - return bcrypt.hash(password, 12).then(function (hash) { + return argon2.hash(password).then(function (hash) { return db.tx(t => { const q1 = t.none(`UPDATE users SET password=$1 WHERE id=$2`, [hash, id]) const q2 = t.none(`DELETE FROM user_sessions WHERE sess -> 'user' ->> 'id'=$1`, [id]) @@ -136,7 +136,7 @@ function validateUserRegistrationToken (token) { function register (token, username, password, role) { return validateUserRegistrationToken(token).then(res => { if (!res.success) throw new Error('Failed to verify registration token') - return bcrypt.hash(password, 12).then(hash => { + return argon2.hash(password).then(hash => { return db.tx(t => { const q1 = t.none(`INSERT INTO users (id, username, password, role) VALUES ($1, $2, $3, $4)`, [uuid.v4(), username, hash, role]) const q2 = t.none(`DELETE FROM user_register_tokens WHERE token=$1`, [token]) diff --git a/migrations/1618843631500-users.js b/migrations/1618843631500-users.js index df6be1fc..509a4cdd 100644 --- a/migrations/1618843631500-users.js +++ b/migrations/1618843631500-users.js @@ -5,7 +5,7 @@ exports.up = function (next) { `CREATE TYPE role AS ENUM('user', 'superuser')`, `CREATE TABLE users ( id UUID PRIMARY KEY, - username VARCHAR(50) NOT NULL UNIQUE, + username TEXT NOT NULL UNIQUE, password VARCHAR(100), role role NOT NULL DEFAULT 'user', enabled BOOLEAN DEFAULT true, @@ -18,7 +18,7 @@ exports.up = function (next) { `CREATE TABLE "user_sessions" ( "sid" VARCHAR NOT NULL COLLATE "default", "sess" JSON NOT NULL, - "expire" TIMESTAMP(6) NOT NULL ) + "expire" TIMESTAMPTZ NOT NULL ) WITH (OIDS=FALSE)`, `ALTER TABLE "user_sessions" ADD CONSTRAINT "session_pkey" PRIMARY KEY ("sid") NOT DEFERRABLE INITIALLY IMMEDIATE`, `CREATE INDEX "IDX_session_expire" ON "user_sessions" ("expire")`, diff --git a/package-lock.json b/package-lock.json index b97bdc20..e1c28960 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2017,6 +2017,11 @@ "@otplib/plugin-thirty-two": "^12.0.1" } }, + "@phc/format": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz", + "integrity": "sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==" + }, "@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -3056,6 +3061,170 @@ } } }, + "argon2": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.27.2.tgz", + "integrity": "sha512-evnzS/Q9rj6ahaaCJjLDoJo9ZuXHhVL2BrBz3wFHb5/i9zAJovBuIY+5t2En7tJjhFXs4O3rUZDeGZxBiDOLwQ==", + "requires": { + "@mapbox/node-pre-gyp": "^1.0.1", + "@phc/format": "^1.0.0", + "node-addon-api": "^3.0.2", + "opencollective-postinstall": "^2.0.3" + }, + "dependencies": { + "@mapbox/node-pre-gyp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.4.tgz", + "integrity": "sha512-M669Qo4nRT7iDmQEjQYC7RU8Z6dpz9UmSbkJ1OFEja3uevCdLKh7IZZki7L1TZj02kRyl82snXFY8QqkyfowrQ==", + "requires": { + "detect-libc": "^1.0.3", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.1", + "nopt": "^5.0.0", + "npmlog": "^4.1.2", + "rimraf": "^3.0.2", + "semver": "^7.3.4", + "tar": "^6.1.0" + } + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "requires": { + "minipass": "^3.0.0" + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "minipass": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", + "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", + "requires": { + "yallist": "^4.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node-addon-api": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.1.0.tgz", + "integrity": "sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw==" + }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "requires": { + "abbrev": "1" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "tar": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", + "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -12696,6 +12865,11 @@ "mimic-fn": "^1.0.0" } }, + "opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==" + }, "optimism": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.14.0.tgz", diff --git a/package.json b/package.json index d7280a96..9443729a 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "dependencies": { "@fczbkk/uuid4": "^3.0.0", "apollo-server-express": "^2.9.14", + "argon2": "^0.27.2", "axios": "^0.16.1", "base-x": "^3.0.2", "bchaddrjs": "^0.3.0",