UNPKG

@paroicms/server

Version:
219 lines 8.81 kB
import { messageOf } from "@paroicms/public-anywhere-lib"; import { ApiError } from "@paroicms/public-server-lib"; import { AccountPreferencesAT } from "../../common/data-format.js"; import { appConf } from "../../context.js"; import { comparePassword } from "../../helpers/passwordEncrypt-helper.js"; import { generateAdminUiToken } from "../../protected-site/protected-access-token.js"; import { findAccountByEmail, findAccountByIdAndEmail, insertSpecialAccount, updateAccountActive, updateAccountLoginMethod, } from "../account/account.queries.js"; import { recordEvent } from "../event-log/event-log.service.js"; import { generateAccessToken, verifyAccessToken, verifyPlatformToken, } from "./auth.helper.js"; import { isSpecialAccountEmail } from "./special-account.helper.js"; export async function loginLocalUser(siteContext, options) { const { user, defaultLanguage } = options; const normalizedEmail = user.email.trim().toLowerCase(); const isLocalDevAccount = appConf.localDevAccount?.email === normalizedEmail; let account = await findAccountByEmail(siteContext, normalizedEmail); if (account) { if (account.loginMethod === "localDev" && !isSpecialAccountEmail(account.email)) { return await deactivateLegacySpecialAccount(siteContext, account); } if (account.passwordHash) { if (!(await comparePassword(user.password, account.passwordHash))) { return { message: "Unauthorized", statusCode: 401 }; } } } else { if (!isLocalDevAccount) { return { message: "Unauthorized", statusCode: 401 }; } if (appConf.localDevAccount?.password !== user.password) { return { message: "Unauthorized", statusCode: 401 }; } account = await createLocalDevAccountInDatabase(siteContext, normalizedEmail); } if (!account.active) { return { message: "Account is not active", statusCode: 401 }; } const loginMethod = isLocalDevAccount ? "localDev" : "local"; if (account.loginMethod === undefined) { await updateAccountLoginMethod(siteContext, account.id, loginMethod); } const parsedPreferences = account.preferences ? AccountPreferencesAT.assert(JSON.parse(account.preferences)) : undefined; const adminUiToken = await generateAdminUiToken(); return { id: account.id, email: account.email, language: parsedPreferences?.language ?? defaultLanguage, name: account.name, token: generateAccessToken({ email: account.email, id: account.id, fqdn: siteContext.fqdn, loginMethod, }), adminUiToken, loginMethod, }; } export async function getVerifiedAccountFromToken(siteContext, options) { const { token, defaultLanguage } = options; let payload; try { payload = verifyAccessToken(token); if (payload.fqdn !== siteContext.fqdn) { throw new ApiError("Not the right token", 403); } const account = await findAccountByIdAndEmail(siteContext, { id: payload.id, email: payload.email, }); if (account.loginMethod !== payload.loginMethod) { throw new ApiError("Login method mismatch", 403); } const parsedPreferences = account.preferences ? AccountPreferencesAT.assert(JSON.parse(account.preferences)) : undefined; const adminUiToken = await generateAdminUiToken(); return { email: account.email, id: account.id, language: parsedPreferences?.language ?? defaultLanguage, name: account.name, token, adminUiToken, loginMethod: payload.loginMethod, }; } catch (error) { if (payload) { siteContext.logger.warn(`Invalid token: ${JSON.stringify(payload)}:`, messageOf(error)); } return { statusCode: 401, message: "Unauthorized", }; } } export async function loginByPlatformToken(siteContext, options) { try { const { token, defaultLanguage } = options; const payload = verifyPlatformToken(token); const normalizedEmail = payload.email.trim().toLowerCase(); const platAdmAccountIndex = (appConf.platformAdminAccounts ?? []).findIndex((acc) => acc.email.trim().toLowerCase() === normalizedEmail); const isPlatformAdmin = platAdmAccountIndex !== -1; let account = await findAccountByEmail(siteContext, normalizedEmail); if (account) { if (account.loginMethod === "platformAdmin" && !isSpecialAccountEmail(account.email)) { return await deactivateLegacySpecialAccount(siteContext, account); } } else { if (!isPlatformAdmin) { throw new ApiError("Account not found", 404); } account = await createPlatformAdminAccountInDatabase(siteContext, normalizedEmail, platAdmAccountIndex); } if (!account.active) { return { message: "Account is not active", statusCode: 401 }; } const loginMethod = isPlatformAdmin ? "platformAdmin" : "platform"; if (account.loginMethod === undefined) { await updateAccountLoginMethod(siteContext, account.id, loginMethod); } const parsedPreferences = account.preferences ? AccountPreferencesAT.assert(JSON.parse(account.preferences)) : undefined; const adminUiToken = await generateAdminUiToken(); return { id: account.id, email: normalizedEmail, language: parsedPreferences?.language ?? defaultLanguage, name: account.name, token: generateAccessToken({ email: normalizedEmail, id: account.id, fqdn: siteContext.fqdn, loginMethod, }), adminUiToken, loginMethod, }; } catch (error) { siteContext.logger.error(error); return { statusCode: 401, message: "Invalid account", }; } } async function deactivateLegacySpecialAccount(siteContext, account) { await updateAccountActive(siteContext, account.id, false); recordEvent(siteContext, { eventType: "account.deactivated", actorId: account.id, targetType: "account", targetId: account.id, eventData: { accountId: account.id, email: account.email, reason: "This email is not a special account", }, }); return { message: "Account deactivated", statusCode: 401 }; } async function createLocalDevAccountInDatabase(siteContext, email) { if (!appConf.localDevAccount) throw new ApiError("Local dev account not configured", 500); const accountId = await insertSpecialAccount(siteContext, { email: appConf.localDevAccount.email, name: appConf.localDevAccount.name, loginMethod: "localDev", }); siteContext.logger.info(`Local dev account created: ${email}`); recordEvent(siteContext, { eventType: "account.create", actorId: accountId, targetType: "account", targetId: accountId, eventData: { accountId, email: appConf.localDevAccount.email, name: appConf.localDevAccount.name, }, }); const account = await findAccountByIdAndEmail(siteContext, { id: accountId, email }); if (!account) throw new ApiError("Failed to create special account", 500); return account; } async function createPlatformAdminAccountInDatabase(siteContext, email, accountIndex) { const platformAdminAccounts = appConf.platformAdminAccounts; if (!platformAdminAccounts) throw new ApiError("Platform admin accounts not configured", 500); const accountId = await insertSpecialAccount(siteContext, { email, name: platformAdminAccounts[accountIndex].name, loginMethod: "platformAdmin", }); siteContext.logger.info(`Platform admin account created: ${email}`); recordEvent(siteContext, { eventType: "account.create", actorId: accountId, targetType: "account", targetId: accountId, eventData: { accountId, email, name: platformAdminAccounts[accountIndex].name, }, }); const account = await findAccountByIdAndEmail(siteContext, { id: accountId, email }); if (!account) throw new ApiError("Failed to create special account", 500); return account; } //# sourceMappingURL=auth.service.js.map