378 lines
13 KiB
JavaScript
378 lines
13 KiB
JavaScript
|
// @ts-check
|
||
|
|
||
|
/**
|
||
|
* ==============================================================================
|
||
|
* Imports
|
||
|
* ==============================================================================
|
||
|
*/
|
||
|
const fs = require("fs");
|
||
|
const http = require("http");
|
||
|
const varDatabaseDbHandler = require("../../../engine/utils/varDatabaseDbHandler");
|
||
|
const addDbEntry = require("../../../query/utils/addDbEntry");
|
||
|
const encrypt = require("../../../../functions/encrypt");
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////////
|
||
|
//////////////////////////////////////////////////////////////////////////////////
|
||
|
//////////////////////////////////////////////////////////////////////////////////
|
||
|
//////////////////////////////////////////////////////////////////////////////////
|
||
|
//////////////////////////////////////////////////////////////////////////////////
|
||
|
//////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
/**
|
||
|
* @typedef {object} FunctionReturn
|
||
|
* @property {boolean} success - Did the operation complete successfully or not?
|
||
|
* @property {{
|
||
|
* id: number,
|
||
|
* first_name: string,
|
||
|
* last_name: string,
|
||
|
* }|null} user - User payload object: or "null"
|
||
|
* @property {string} [msg] - Message
|
||
|
* @property {string} [error] - Error Message
|
||
|
* @property {string | number} [social_id] - Social Id
|
||
|
* @property {string} [social_platform] - Social Platform
|
||
|
* @property {object} [payload] - Payload
|
||
|
* @property {boolean} [alert] - Alert
|
||
|
* @property {*} [newUser] - New User
|
||
|
*/
|
||
|
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
|
||
|
const database = process.env.DSQL_DB_NAME || "";
|
||
|
const encryptionKey = process.env.DSQL_ENCRYPTION_KEY || "";
|
||
|
const encryptionSalt = process.env.DSQL_ENCRYPTION_SALT || "";
|
||
|
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
|
||
|
/**
|
||
|
* Handle Social User Auth on Datasquirel Database
|
||
|
* ==============================================================================
|
||
|
*
|
||
|
* @description This function handles all social login logic after the social user
|
||
|
* has been authenticated and userpayload is present. The payload MUST contain the
|
||
|
* specified fields because this funciton will create a new user if the authenticated
|
||
|
* user does not exist.
|
||
|
*
|
||
|
* @param {{
|
||
|
* database: string|null|undefined,
|
||
|
* social_id: string|number,
|
||
|
* email: string,
|
||
|
* social_platform: string,
|
||
|
* payload: {
|
||
|
* social_id: string | number,
|
||
|
* email: string,
|
||
|
* social_platform: string,
|
||
|
* first_name: string,
|
||
|
* last_name: string,
|
||
|
* image: string,
|
||
|
* image_thumbnail: string,
|
||
|
* username: string,
|
||
|
* },
|
||
|
* res: object|null,
|
||
|
* invitation?: object|null,
|
||
|
* supEmail?: string | null,
|
||
|
* additionalFields?: object,
|
||
|
* dbSchema: import("../../../../types/database-schema.td").DSQL_DatabaseSchemaType | undefined
|
||
|
* }} params - function parameters inside an object
|
||
|
*
|
||
|
* @returns {Promise<FunctionReturn>} - Response object
|
||
|
*/
|
||
|
async function handleSocialDb({ social_id, email, social_platform, payload, res, invitation, supEmail, additionalFields, dbSchema }) {
|
||
|
const tableSchema = dbSchema?.tables.find((tb) => tb?.tableName === "users");
|
||
|
|
||
|
try {
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
|
||
|
let existingSocialIdUser = await varDatabaseDbHandler({
|
||
|
database: database ? database : "datasquirel",
|
||
|
queryString: `SELECT * FROM users WHERE social_id = ? AND social_login='1' AND social_platform = ? `,
|
||
|
queryValuesArray: [social_id.toString(), social_platform],
|
||
|
});
|
||
|
|
||
|
if (existingSocialIdUser && existingSocialIdUser[0]) {
|
||
|
return await loginSocialUser({
|
||
|
user: existingSocialIdUser[0],
|
||
|
social_platform,
|
||
|
res,
|
||
|
invitation,
|
||
|
database,
|
||
|
additionalFields,
|
||
|
});
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
|
||
|
const finalEmail = email ? email : supEmail ? supEmail : null;
|
||
|
|
||
|
if (!finalEmail) {
|
||
|
return {
|
||
|
success: false,
|
||
|
user: null,
|
||
|
msg: "No Email Present",
|
||
|
social_id,
|
||
|
social_platform,
|
||
|
payload,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
|
||
|
let existingEmailOnly = await varDatabaseDbHandler({
|
||
|
database: database ? database : "datasquirel",
|
||
|
queryString: `SELECT * FROM users WHERE email='${finalEmail}'`,
|
||
|
});
|
||
|
|
||
|
if (existingEmailOnly && existingEmailOnly[0]) {
|
||
|
return {
|
||
|
success: false,
|
||
|
user: null,
|
||
|
msg: "This Email is already taken",
|
||
|
alert: true,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
|
||
|
const foundUser = await varDatabaseDbHandler({
|
||
|
database: database ? database : "datasquirel",
|
||
|
queryString: `SELECT * FROM users WHERE email='${finalEmail}' AND social_login='1' AND social_platform='${social_platform}' AND social_id='${social_id}'`,
|
||
|
});
|
||
|
|
||
|
if (foundUser && foundUser[0]) {
|
||
|
return await loginSocialUser({
|
||
|
user: payload,
|
||
|
social_platform,
|
||
|
res,
|
||
|
invitation,
|
||
|
database,
|
||
|
additionalFields,
|
||
|
});
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
|
||
|
const socialHashedPassword = await encrypt({
|
||
|
data: social_id.toString(),
|
||
|
encryptionKey,
|
||
|
encryptionSalt,
|
||
|
});
|
||
|
|
||
|
const data = {
|
||
|
social_login: "1",
|
||
|
verification_status: supEmail ? "0" : "1",
|
||
|
password: socialHashedPassword,
|
||
|
};
|
||
|
|
||
|
Object.keys(payload).forEach((key) => {
|
||
|
data[key] = payload[key];
|
||
|
});
|
||
|
|
||
|
const newUser = await addDbEntry({
|
||
|
dbFullName: database ? database : "datasquirel",
|
||
|
tableName: "users",
|
||
|
duplicateColumnName: "email",
|
||
|
duplicateColumnValue: finalEmail,
|
||
|
data: {
|
||
|
...data,
|
||
|
email: finalEmail,
|
||
|
},
|
||
|
encryptionKey,
|
||
|
encryptionSalt,
|
||
|
tableSchema,
|
||
|
});
|
||
|
|
||
|
if (newUser?.insertId) {
|
||
|
const newUserQueried = await varDatabaseDbHandler({
|
||
|
database: database ? database : "datasquirel",
|
||
|
queryString: `SELECT * FROM users WHERE id='${newUser.insertId}'`,
|
||
|
});
|
||
|
|
||
|
if (!newUserQueried || !newUserQueried[0])
|
||
|
return {
|
||
|
success: false,
|
||
|
user: null,
|
||
|
msg: "User Insertion Failed!",
|
||
|
};
|
||
|
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
|
||
|
if (supEmail && database?.match(/^datasquirel$/)) {
|
||
|
/**
|
||
|
* Send email Verification
|
||
|
*
|
||
|
* @description Send verification email to newly created agent
|
||
|
*/
|
||
|
let generatedToken = encrypt({
|
||
|
data: JSON.stringify({
|
||
|
id: newUser.insertId,
|
||
|
email: supEmail,
|
||
|
dateCode: Date.now(),
|
||
|
}),
|
||
|
encryptionKey,
|
||
|
encryptionSalt,
|
||
|
});
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
|
||
|
return await loginSocialUser({
|
||
|
user: newUserQueried[0],
|
||
|
social_platform,
|
||
|
res,
|
||
|
invitation,
|
||
|
database,
|
||
|
additionalFields,
|
||
|
});
|
||
|
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
} else {
|
||
|
console.log("Social User Failed to insert in 'handleSocialDb.js' backend function =>", newUser);
|
||
|
|
||
|
return {
|
||
|
success: false,
|
||
|
user: null,
|
||
|
msg: "Social User Failed to insert in 'handleSocialDb.js' backend function => ",
|
||
|
newUser: newUser,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
} catch (error) {
|
||
|
console.log("ERROR in 'handleSocialDb.js' backend function =>", error.message);
|
||
|
|
||
|
return {
|
||
|
success: false,
|
||
|
user: null,
|
||
|
error: error.message,
|
||
|
};
|
||
|
|
||
|
// serverError({
|
||
|
// component: "/functions/backend/social-login/handleSocialDb.js - main-catch-error",
|
||
|
// message: error.message,
|
||
|
// user: { first_name, last_name },
|
||
|
// });
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////////
|
||
|
//////////////////////////////////////////////////////////////////////////////////
|
||
|
//////////////////////////////////////////////////////////////////////////////////
|
||
|
//////////////////////////////////////////////////////////////////////////////////
|
||
|
//////////////////////////////////////////////////////////////////////////////////
|
||
|
//////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
/**
|
||
|
* Function to login social user
|
||
|
* ==============================================================================
|
||
|
* @description This function logs in the user after 'handleSocialDb' function finishes
|
||
|
* the user creation or confirmation process
|
||
|
*
|
||
|
* @async
|
||
|
*
|
||
|
* @param {object} params - function parameters inside an object
|
||
|
* @param {{
|
||
|
* first_name: string,
|
||
|
* last_name: string,
|
||
|
* email: string,
|
||
|
* social_id: string|number,
|
||
|
* }} params.user - user object
|
||
|
* @param {string} params.social_platform - Whether its "google" or "facebook" or "github"
|
||
|
* @param {http.ServerResponse} params.res - Https response object
|
||
|
* @param {object} params.invitation - A query object if user was invited
|
||
|
* @param {string|null} params.database - Target Database
|
||
|
* @param {object} [params.additionalFields] - Additional fields to be added to the user payload
|
||
|
*
|
||
|
* @returns {Promise<{
|
||
|
* success: boolean,
|
||
|
* user: { id: number, first_name: string, last_name: string } | null
|
||
|
* msg?: string
|
||
|
* }>}
|
||
|
*/
|
||
|
async function loginSocialUser({ user, social_platform, res, invitation, database, additionalFields }) {
|
||
|
const foundUser = await varDatabaseDbHandler({
|
||
|
database: database ? database : "datasquirel",
|
||
|
queryString: `SELECT * FROM users WHERE email='${user.email}' AND social_id='${user.social_id}' AND social_platform='${social_platform}'`,
|
||
|
});
|
||
|
|
||
|
let csrfKey = Math.random().toString(36).substring(2) + "-" + Math.random().toString(36).substring(2);
|
||
|
|
||
|
if (!foundUser?.[0]) {
|
||
|
return {
|
||
|
success: false,
|
||
|
user: null,
|
||
|
msg: "User Not Found",
|
||
|
};
|
||
|
}
|
||
|
|
||
|
let userPayload = {
|
||
|
id: foundUser[0].id,
|
||
|
type: foundUser[0].type || "",
|
||
|
stripe_id: foundUser[0].stripe_id || "",
|
||
|
first_name: foundUser[0].first_name,
|
||
|
last_name: foundUser[0].last_name,
|
||
|
username: foundUser[0].username,
|
||
|
email: foundUser[0].email,
|
||
|
social_id: foundUser[0].social_id,
|
||
|
image: foundUser[0].image,
|
||
|
image_thumbnail: foundUser[0].image_thumbnail,
|
||
|
verification_status: foundUser[0].verification_status,
|
||
|
social_login: foundUser[0].social_login,
|
||
|
social_platform: foundUser[0].social_platform,
|
||
|
csrf_k: csrfKey,
|
||
|
logged_in_status: true,
|
||
|
date: Date.now(),
|
||
|
};
|
||
|
|
||
|
if (additionalFields && Object.keys(additionalFields).length > 0) {
|
||
|
Object.keys(additionalFields).forEach((key) => {
|
||
|
userPayload[key] = foundUser[0][key];
|
||
|
});
|
||
|
}
|
||
|
|
||
|
let encryptedPayload = encrypt({
|
||
|
data: JSON.stringify(userPayload),
|
||
|
encryptionKey,
|
||
|
encryptionSalt,
|
||
|
});
|
||
|
|
||
|
if (res?.setHeader) {
|
||
|
res.setHeader("Set-Cookie", [`datasquirelAuthKey=${encryptedPayload};samesite=strict;path=/;HttpOnly=true;Secure=true`, `csrf=${csrfKey};samesite=strict;path=/;HttpOnly=true`]);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////
|
||
|
|
||
|
return {
|
||
|
success: true,
|
||
|
user: userPayload,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
module.exports = handleSocialDb;
|