292 lines
8.9 KiB
TypeScript
292 lines
8.9 KiB
TypeScript
import http from "http";
|
|
import fs from "fs";
|
|
import path from "path";
|
|
import encrypt from "../../functions/dsql/encrypt";
|
|
import grabHostNames from "../../utils/grab-host-names";
|
|
import apiLoginUser from "../../functions/api/users/api-login";
|
|
import getAuthCookieNames from "../../functions/backend/cookies/get-auth-cookie-names";
|
|
import { writeAuthFile } from "../../functions/backend/auth/write-auth-files";
|
|
import {
|
|
APILoginFunctionReturn,
|
|
DSQL_DatabaseSchemaType,
|
|
PackageUserLoginRequestBody,
|
|
} from "../../types";
|
|
import debugLog from "../../utils/logging/debug-log";
|
|
import grabCookieExpiryDate from "../../utils/grab-cookie-expirt-date";
|
|
import emailRegexCheck from "../../functions/email/verification/email-regex-test";
|
|
import emailMxLookup from "../../functions/email/verification/email-mx-lookup";
|
|
import validateEmail from "../../functions/email/fns/validate-email";
|
|
|
|
type Param = {
|
|
key?: string;
|
|
database: string;
|
|
payload: {
|
|
email?: string;
|
|
username?: string;
|
|
password?: string;
|
|
};
|
|
additionalFields?: string[];
|
|
request?: http.IncomingMessage & { [s: string]: any };
|
|
response?: http.ServerResponse & { [s: string]: any };
|
|
encryptionKey?: string;
|
|
encryptionSalt?: string;
|
|
email_login?: boolean;
|
|
email_login_code?: string;
|
|
temp_code_field?: string;
|
|
token?: boolean;
|
|
user_id?: string | number;
|
|
skipPassword?: boolean;
|
|
debug?: boolean;
|
|
skipWriteAuthFile?: boolean;
|
|
apiUserID?: string | number;
|
|
dbUserId?: string | number;
|
|
cleanupTokens?: boolean;
|
|
secureCookie?: boolean;
|
|
};
|
|
|
|
/**
|
|
* # Login A user
|
|
*/
|
|
export default async function loginUser({
|
|
key,
|
|
payload,
|
|
database,
|
|
additionalFields,
|
|
response,
|
|
encryptionKey,
|
|
encryptionSalt,
|
|
email_login,
|
|
email_login_code,
|
|
temp_code_field,
|
|
token,
|
|
user_id,
|
|
skipPassword,
|
|
apiUserID,
|
|
skipWriteAuthFile,
|
|
dbUserId,
|
|
debug,
|
|
cleanupTokens,
|
|
secureCookie,
|
|
request,
|
|
}: Param): Promise<APILoginFunctionReturn> {
|
|
const grabedHostNames = grabHostNames({ userId: user_id || apiUserID });
|
|
const { host, port, scheme } = grabedHostNames;
|
|
const COOKIE_EXPIRY_DATE = grabCookieExpiryDate();
|
|
|
|
const defaultTempLoginFieldName = "temp_login_code";
|
|
const emailLoginTempCodeFieldName = email_login
|
|
? temp_code_field
|
|
? temp_code_field
|
|
: defaultTempLoginFieldName
|
|
: undefined;
|
|
|
|
const finalEncryptionKey =
|
|
encryptionKey || process.env.DSQL_ENCRYPTION_PASSWORD;
|
|
const finalEncryptionSalt =
|
|
encryptionSalt || process.env.DSQL_ENCRYPTION_SALT;
|
|
|
|
function debugFn(log: any, label?: string) {
|
|
debugLog({ log, addTime: true, title: "loginUser", label });
|
|
}
|
|
|
|
if (!finalEncryptionKey?.match(/.{8,}/)) {
|
|
console.log("Encryption key is invalid");
|
|
return {
|
|
success: false,
|
|
payload: null,
|
|
msg: "Encryption key is invalid",
|
|
};
|
|
}
|
|
|
|
if (!finalEncryptionSalt?.match(/.{8,}/)) {
|
|
console.log("Encryption salt is invalid");
|
|
return {
|
|
success: false,
|
|
payload: null,
|
|
msg: "Encryption salt is invalid",
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Check required fields
|
|
*
|
|
* @description Check required fields
|
|
*/
|
|
const isEmailValid = await validateEmail({ email: payload.email });
|
|
|
|
if (!payload.email || !isEmailValid.isValid) {
|
|
return {
|
|
success: false,
|
|
payload: null,
|
|
msg: isEmailValid.message,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Initialize HTTP response variable
|
|
*/
|
|
|
|
let httpResponse: import("../../types").APILoginFunctionReturn = {
|
|
success: false,
|
|
};
|
|
|
|
/**
|
|
* Check for local DB settings
|
|
*
|
|
* @description Look for local db settings in `.env` file and by pass the http request if available
|
|
*/
|
|
const { DSQL_DB_HOST, DSQL_DB_USERNAME, DSQL_DB_PASSWORD, DSQL_DB_NAME } =
|
|
process.env;
|
|
|
|
if (
|
|
DSQL_DB_HOST?.match(/./) &&
|
|
DSQL_DB_USERNAME?.match(/./) &&
|
|
DSQL_DB_PASSWORD?.match(/./) &&
|
|
DSQL_DB_NAME?.match(/./) &&
|
|
global.DSQL_USE_LOCAL
|
|
) {
|
|
let dbSchema: DSQL_DatabaseSchemaType | undefined;
|
|
|
|
try {
|
|
const localDbSchemaPath = path.resolve(
|
|
process.cwd(),
|
|
"dsql.schema.json"
|
|
);
|
|
dbSchema = JSON.parse(fs.readFileSync(localDbSchemaPath, "utf8"));
|
|
} catch (error) {}
|
|
|
|
httpResponse = await apiLoginUser({
|
|
database: process.env.DSQL_DB_NAME || "",
|
|
email: payload.email,
|
|
username: payload.username,
|
|
password: payload.password,
|
|
skipPassword,
|
|
encryptionKey: finalEncryptionKey,
|
|
additionalFields,
|
|
email_login,
|
|
email_login_code,
|
|
email_login_field: emailLoginTempCodeFieldName,
|
|
token,
|
|
dbUserId,
|
|
debug,
|
|
});
|
|
} else {
|
|
httpResponse = await new Promise((resolve, reject) => {
|
|
const reqPayload: PackageUserLoginRequestBody = {
|
|
encryptionKey: finalEncryptionKey,
|
|
payload,
|
|
database,
|
|
additionalFields,
|
|
email_login,
|
|
email_login_code,
|
|
email_login_field: emailLoginTempCodeFieldName,
|
|
token,
|
|
skipPassword: skipPassword,
|
|
dbUserId: dbUserId || 0,
|
|
};
|
|
|
|
const reqPayloadJSON = JSON.stringify(reqPayload);
|
|
|
|
const httpsRequest = scheme.request(
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
"Content-Length": Buffer.from(reqPayloadJSON).length,
|
|
Authorization:
|
|
key ||
|
|
process.env.DSQL_FULL_ACCESS_API_KEY ||
|
|
process.env.DSQL_API_KEY,
|
|
},
|
|
port,
|
|
hostname: host,
|
|
path: `/api/user/${
|
|
user_id || grabedHostNames.user_id
|
|
}/login-user`,
|
|
},
|
|
|
|
(res) => {
|
|
var str = "";
|
|
|
|
res.on("data", function (chunk) {
|
|
str += chunk;
|
|
});
|
|
|
|
res.on("end", function () {
|
|
resolve(JSON.parse(str));
|
|
});
|
|
|
|
res.on("error", (err) => {
|
|
reject(err);
|
|
});
|
|
}
|
|
);
|
|
|
|
httpsRequest.write(reqPayloadJSON);
|
|
httpsRequest.end();
|
|
});
|
|
}
|
|
|
|
if (debug) {
|
|
debugFn(httpResponse, "httpResponse");
|
|
}
|
|
|
|
if (httpResponse?.success) {
|
|
let encryptedPayload = encrypt({
|
|
data: JSON.stringify(httpResponse.payload),
|
|
encryptionKey: finalEncryptionKey,
|
|
encryptionSalt: finalEncryptionSalt,
|
|
});
|
|
|
|
try {
|
|
if (token && encryptedPayload)
|
|
httpResponse["token"] = encryptedPayload;
|
|
} catch (error) {
|
|
global.ERROR_CALLBACK?.(
|
|
`Login User HTTP Response Error`,
|
|
error as Error
|
|
);
|
|
}
|
|
|
|
const cookieNames = getAuthCookieNames({
|
|
database,
|
|
userId: grabedHostNames.user_id,
|
|
});
|
|
|
|
if (httpResponse.csrf && !skipWriteAuthFile) {
|
|
writeAuthFile(
|
|
httpResponse.csrf,
|
|
JSON.stringify(httpResponse.payload),
|
|
cleanupTokens && httpResponse.payload?.id
|
|
? { userId: httpResponse.payload.id }
|
|
: undefined
|
|
);
|
|
}
|
|
|
|
httpResponse["cookieNames"] = cookieNames;
|
|
httpResponse["key"] = String(encryptedPayload);
|
|
|
|
const authKeyName = cookieNames.keyCookieName;
|
|
const csrfName = cookieNames.csrfCookieName;
|
|
|
|
if (debug) {
|
|
debugFn(authKeyName, "authKeyName");
|
|
debugFn(csrfName, "csrfName");
|
|
debugFn(encryptedPayload, "encryptedPayload");
|
|
}
|
|
|
|
response?.setHeader("Set-Cookie", [
|
|
`${authKeyName}=${encryptedPayload};samesite=strict;path=/;HttpOnly=true;Expires=${COOKIE_EXPIRY_DATE}${
|
|
secureCookie ? ";Secure=true" : ""
|
|
}`,
|
|
`${csrfName}=${httpResponse.payload?.csrf_k};samesite=strict;path=/;HttpOnly=true;Expires=${COOKIE_EXPIRY_DATE}`,
|
|
]);
|
|
|
|
if (debug) {
|
|
debugFn("Response Sent!");
|
|
}
|
|
}
|
|
|
|
return httpResponse;
|
|
}
|