@paroicms/server
Version:
The ParoiCMS server
219 lines • 8.81 kB
JavaScript
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