import { hash, compare, genSalt } from 'bcrypt'; import { DbUser } from '#db/schemas/index.js'; import { AuthDto, RegisterDto } from '#dto/index.js'; import { SafeNSoundError } from '#util/error.js'; import { WhoAmIDto } from '#dto/auth/WhoAmIDto.js'; import { generateJwtToken } from '#util/jwtUtils.js'; async function whoAmI(token) {} export async function getUserById(id) { const user = await DbUser.findById(id).exec(); if (!user) { throw new SafeNSoundError({ errCode: 'ENTITY_NOT_FOUND', message: 'No such user!' }); } return user; } async function getUserByAuth(data) { if (!(data instanceof AuthDto)) throw new Error('Invalid data type. Expected AuthDto.'); let user; if (data.email) { user = await DbUser.findOne({ email: data.email }); } else if (data.username) { user = await DbUser.findOne({ username: data.username }); } if (!user) { // Sneaky: prevent user enumeration throw new SafeNSoundError({ errCode: 'INVALID_AUTH', message: 'Invalid username or password.' }); } const isPasswordValid = await compare(data.password, user.passwordHash); if (!isPasswordValid) { throw new SafeNSoundError({ errCode: 'INVALID_AUTH', message: 'Invalid username or password.' }); } return user; } /** * @param data {RegisterDto} * @returns {Promise} */ export async function registerUser(data) { if (!(data instanceof RegisterDto)) throw new Error('Invalid data type. Expected RegisterDto.'); const salt = await genSalt(1); const passwordHash = await hash(data.password, salt); if (!passwordHash) { throw new Error('Failed to hash password.'); } return DbUser.create({ username: data.username, passwordHash, email: data.email, type: data.type }); } export async function deleteUser(data) { var user = await getUserByAuth(data); await DbUser.findByIdAndDelete(user._id); } /** * @param data {AuthDto} * @param deviceName {string} * @returns {Promise} */ export async function loginUser(data, deviceName) { const user = await getUserByAuth(data); const device = await user.devices.create({ name: deviceName ?? 'Unknown Device' }); user.devices.push(device); await user.save(); const whoAmI = await WhoAmIDto.create({ userId: user._id, username: user.username, deviceId: device._id, type: user.type }); whoAmI.accessToken = await generateJwtToken({ type: user.type, sub: user._id.toString(), deviceId: device._id.toString(), iat: Math.floor(Date.now() / 1000) }); return whoAmI; }