'use strict' require('dotenv').config() const Util = require('util') const Jwt = require('@hapi/jwt') const Schmervice = require('@hapipal/schmervice') const SecurePassword = require('secure-password') const hasher = async (pwd, steak) => { const hash = await pwd.hash(steak) const result = await pwd.verify(steak, hash) let squirtle = null switch (result) { case SecurePassword.INVALID_UNRECOGNIZED_HASH: return console.error( 'This hash was not made with secure-password. Attempt legacy algorithm', ) case SecurePassword.INVALID: return console.log('Invalid password') case SecurePassword.VALID: return result case SecurePassword.VALID_NEEDS_REHASH: console.log('Yay you made it, wait for us to improve your safety') try { squirtle = await pwd.hash(steak) // console.log('improvedHash', squirtle) // const saveHash = Auth.insert({user_email: matchingEmails}, ).into('token') return squirtle } catch (err) { console.error( 'You are authenticated, but we could not improve your safety this time around', ) } break } } /** Class for methods used in the User plugin */ module.exports = class UserService extends Schmervice.Service { /** * Unsure of what our constructor does * @param {...any} args */ constructor(...args) { super(...args) const pwd = new SecurePassword() this.pwd = { hash: Util.promisify(pwd.hash.bind(pwd)), verify: Util.promisify(pwd.verify.bind(pwd)), } } /** * Use knex to find users with id column * @param {number} id * @param {*} txn * @returns */ async findById(id, txn) { const { User } = this.server.models() return await User.query(txn) .throwIfNotFound() .first() .where({ user_id: id }) } /** * Use knew to find first user with username * @param {*} username * @param {*} txn * @returns */ async findByUsername(username, txn) { const { User } = this.server.models() return await User.query(txn) .throwIfNotFound() .first() .where({ user_name: username }) } /** * Signup function * @param {*} param0 * @param {*} txn * @returns */ async signup({ password, userInfo }, txn) { const { User, Auth } = this.server.models() const matchingEmails = await User.query().where( 'user_email', userInfo.user_email, ) if (matchingEmails.length > 0) { throw `User ${userInfo.user_email} already exists: Cannot create a user without a unique email` } // Library: Secure-Password const pepper = process.env.PEPPER // add pepper to pw and convert to buffer to prep for hash bytes const steak = Buffer.from(password + pepper, 'utf-8') // send peppered pw to (argon algorithm) library for salted hash const hashed = await hasher(this.pwd, steak) console.log("hashed", hashed) const newAuth = await Auth.query(txn).insert({ user_email: userInfo.user_email, created_at: new Date.now(), token: hashed, }) console.log("newAuth", newAuth) // return newAuth // const user = await User.query(txn).insert(userInfo) // user.user_id = user.id // delete user.id // await this.changePassword(id, password, txn) // return user } /** * Updates user's info * @param {number} id * @param {*} param1 * @param {*} txn * @returns */ async update(id, { password, ...userInfo }, txn) { const { User } = this.server.models() if (Object.keys(userInfo).length > 0) { await User.query(txn) .throwIfNotFound() .where({ id }) .patch(userInfo) } if (password) { await this.changePassword(id, password, txn) } return id } /** * Self explanatory * @param {*} param0 * @param {*} txn * @returns */ async login({ email, password }, txn) { const { User } = this.server.models() const user = await User.query(txn) .throwIfNotFound() .first() .where({ user_email: email }) /** Uncomment to run password check using SecurePassword */ // const passwordCheck = await this.pwd.verify(Buffer.from(password), user.password) // if (passwordCheck === SecurePassword.VALID_NEEDS_REHASH) { // await this.changePassword(user.id, password, txn) // } // else if (passwordCheck !== SecurePassword.VALID) { // throw User.createNotFoundError() // } return user } /** * Create a token to be sent in request headers * @param {User} user * @returns {Token} */ createToken(user) { const key = this.server.registrations['main-app-plugin'].options.jwtKey return Jwt.token.generate( { aud: 'urn:audience:test', iss: 'urn:issuer:test', email: user.user_email, }, { key: key, algorithm: 'HS256', }, { ttlSec: 4 * 60 * 60, // 7 days }, ) } /** * Use knex to try to change password entry * @param {number} id * @param {string} password * @param {*} txn * @returns {number} */ async changePassword(id, password, txn) { const { User } = this.server.models() return 'done' // rework with Auth model // await User.query(txn) // .throwIfNotFound() // .where({ id }) // .patch({ // password: await this.pwd.hash(Buffer.from(password)), // }) // return id } async getPassword(email, txn) { const { Auth } = this.server.models() const passwordRow = await Auth.query(txn) .where('user_email', email) .first() return passwordRow ? passwordRow.token : null } }