UNPKG

@qelos/auth

Version:

Express Passport authentication service

263 lines (229 loc) 6.94 kB
import User, { UserDocument, UserModel } from '../models/user'; import { cookieTokenExpiration } from '../../config'; import { Types } from 'mongoose'; import { getAbsoluteDate } from './dates'; import logger from './logger'; import { AuthRequest } from '../../types'; import { verifyToken } from './tokens'; import { IAuthConfigurationMetadata, IUserAdditionalField } from '@qelos/global-types'; export function getValidMetadata(metadata: any = {}, additionalFields: IUserAdditionalField[] = []) { const result = {}; for (const field of additionalFields) { if (metadata[field.key] !== undefined && typeof metadata[field.key] === field.valueType) { result[field.key] = metadata[field.key]; } else if (field.defaultValue) { result[field.key] = field.defaultValue; } else if (field.required) { throw { code: 'INVALID_METADATA' }; } } return result; } export async function getUser(query: any) { try { const user = await User.findOne(query).exec(); if (user) { return user; } else { query.email = query.username; delete query.username; const raceUser = await User.findOne(query).exec(); if (raceUser) { raceUser.username = raceUser.email; await raceUser.save() return raceUser; } } } catch (err) { throw { code: 'FORM_SUBMISSION_FAILED', info: err }; } throw { code: 'INCORRECT_CREDENTIALS' }; } export async function updateUser( user: UserDocument | { _id: Types.ObjectId | string, tenant: string }, { username = null, password = null, fullName = null, roles = null, firstName = null, lastName = null, birthDate = null, profileImage = null, metadata = null }, authConfig?: IAuthConfigurationMetadata ) { let directUpdate; if (!(user instanceof User)) { if (username || roles || password) { user = await User.findOne({ _id: user._id, tenant: user.tenant }).exec(); } else { directUpdate = { _id: user._id, tenant: user.tenant }; user = {} as UserDocument; } } if (fullName) { user.fullName = fullName; } if (username) { user.username = username; switch (authConfig?.treatUsernameAs) { case 'email': user.email = user.username; break; case 'phone': user.phone = user.username; break; default: break; } } if (password) { user.password = password; } if (firstName) { user.firstName = firstName; } if (lastName) { user.lastName = lastName; } if (birthDate) { user.birthDate = getAbsoluteDate(birthDate); } if (profileImage) { user.profileImage = profileImage; } if (roles) { user.roles = roles; } if (metadata && authConfig) { user.metadata = getValidMetadata(metadata, authConfig.additionalUserFields); } return (directUpdate ? User.updateOne(directUpdate, { $set: user }) : user.save() ).catch((err: Error) => { logger.error('failed to update user', err) return Promise.reject({ code: 'UPDATE_USER_FAILED', info: err }) }); } export async function deleteUser(userId: string, tenant: string) { if (!tenant) { throw new Error('tenant is missing'); } try { await User.deleteOne({ _id: userId, tenant }).exec(); return { code: 'USER_DELETED_SUCCESSFULLY', info: userId }; } catch (error) { throw { code: 'USER_DELETE_FAILED', info: error } } } export function comparePassword(user: UserModel, password: string): Promise<UserModel> { return new Promise((resolve, reject) => { return user.comparePassword(password.trim(), (passwordErr, isMatch) => { if (passwordErr) { return reject({ code: 'FORM_SUBMISSION_FAILED', info: passwordErr }); } if (!isMatch) { return reject({ code: 'INCORRECT_CREDENTIALS' }); } resolve(user); }); }); } export function setToken({ user, workspace }: { user: UserDocument, workspace?: any }, authType: string) { if (authType === 'oauth') { return setOAuthAuthentication(user, workspace); } if (authType === 'cookie') { return setCookieAuthentication(user, workspace); } throw { code: 'INVALID_AUTH_TYPE' }; } export function updateToken( user: UserModel, authType: string, currentPayload: { tokenIdentifier, workspace?: { _id, name, roles } }, newToken: string ) { return user .updateToken(authType, currentPayload, newToken) .catch((err) => Promise.reject({ code: 'UPDATE_TOKEN_FAILED', info: err })); } export async function deleteToken( tenant: string, userId: string, authType: string, token: string, isRelatedToken: boolean ) { try { const user: any = await User.findOne({ _id: userId, tenant }).exec(); if (isRelatedToken) { token = await user?.getTokenByRelatedTokens(authType, token); } else { token = ((await verifyToken(token, tenant)) as any)?.tokenIdentifier || ''; } user?.deleteToken(authType, token); } catch (e) { return false; } return true; } function setOAuthAuthentication(user: any, workspace?: any) { const token = user.getToken({ authType: 'oauth', workspace }); const refreshToken = user.getRefreshToken(token, workspace); return user.save().then(() => { return { token, refreshToken, user, workspace, }; }); } function setCookieAuthentication(user: any, workspace?: any) { const cookieToken = user.getToken({ authType: 'cookie', expiresIn: cookieTokenExpiration / 1000, workspace }); return user.save().then(() => { return { user, workspace, cookieToken }; }); } export function getUserIfTokenExists(tenant: string, userId: string, tokenId: string) { return User.findOne({ _id: userId, tenant, 'tokens.tokenIdentifier': tokenId, }) .then((user: any) => user || Promise.reject()) .catch(() => Promise.reject({ code: 'USER_WITH_TOKEN_NOT_EXISTS' })); } export async function clearOldTokens(userId: string) { if (!userId) { return; } const user: any = await User.findOne({ _id: userId }).select('tokens').exec(); if (!user) { return; } const now = Date.now() const validTokens = user.tokens.filter(t => new Date(t.expiresAt).getTime() > now) if (validTokens.length === user.tokens) { return } await User.updateOne({ _id: userId }, { $set: { tokens: validTokens } }).exec() } export function getCookieTokenName(tenant: string) { return `qlt_${tenant.substring(0, 8)}`.trim(); } export function getCookieTokenValue(req: AuthRequest) { const tokenKey = getCookieTokenName(req.headers.tenant); return req.signedCookies[tokenKey] || req.cookies[tokenKey] || req.signedCookies.token || req.cookies.token; } export async function getUserMetadata(userId: string, tenant: string) { const user = await User.findOne({ _id: userId, tenant }).select('metadata').lean().exec(); return user?.metadata || {}; }