From 4b826728abe99f12a0ccd01a74ed72e902ad59e1 Mon Sep 17 00:00:00 2001 From: Tben <52448020+BenjaminToby@users.noreply.github.com> Date: Sat, 24 Jun 2023 13:09:26 +0100 Subject: [PATCH] updates --- .gitignore | 3 + client/auth/loginWithGithub.js | 168 --------------------------- client/auth/loginWithGoogle.js | 179 ----------------------------- client/auth/logout.js | 137 ++++++++++++++++++++++ client/utils/parseClientCookies.js | 65 +++++++++++ package.json | 2 +- users/logout-user.js | 9 +- users/social/google-auth.js | 8 +- users/user-auth.js | 8 +- utils/functions/parseCookies.js | 65 +++++++++++ 10 files changed, 286 insertions(+), 358 deletions(-) delete mode 100644 client/auth/loginWithGithub.js delete mode 100644 client/auth/loginWithGoogle.js create mode 100644 client/auth/logout.js create mode 100644 client/utils/parseClientCookies.js create mode 100644 utils/functions/parseCookies.js diff --git a/.gitignore b/.gitignore index 3f1516e..efcb695 100644 --- a/.gitignore +++ b/.gitignore @@ -131,3 +131,6 @@ dist # typescript tsconfig.json + +# others +deprecated \ No newline at end of file diff --git a/client/auth/loginWithGithub.js b/client/auth/loginWithGithub.js deleted file mode 100644 index 4147856..0000000 --- a/client/auth/loginWithGithub.js +++ /dev/null @@ -1,168 +0,0 @@ -/** - * @typedef {{ - * fileBase64: string, - * fileBase64Full: string, - * fileName: string, - * fileSize: number, - * fileType: string, - * }} FunctionReturn - */ - -/** - * Login with Google Function - * ============================================================================== - * @description This function takes in a *SINGLE* input file from a HTML file input element. - * HTML file input elements usually return an array of input objects, so be sure to select the target - * file from the array. - * - * @async - * - * @param {object} params - Single object passed - * @param {string} params.clientId - Google app client ID {@link https://datasquirel.com/docs} - * @param {HTMLElement} params.element - HTML Element to display google login button - * @param {boolean} params.triggerPrompt - Whether to trigger Google signing popup or not: {@link https://datasquirel.com/docs} - * @param {function(): void} [params.readyStateDispatch] - React setState Function: sets value to "true" - * - * @returns { Promise } - Return - */ -module.exports = async function loginWithGithub({ clientId, element, triggerPrompt, readyStateDispatch }) { - /** - * == Initialize - * - * @description Initialize - */ - const googleScript = document.createElement("script"); - googleScript.src = "https://accounts.google.com/gsi/client"; - googleScript.className = "social-script-tag"; - - document.body.appendChild(googleScript); - - googleScript.onload = function (e) { - if (google) { - if (readyStateDispatch) readyStateDispatch(true); - - //////////////////////////////////////// - //////////////////////////////////////// - //////////////////////////////////////// - - if (element) { - function handleCredentialResponse(response) { - userLoginWithGoogle({ - gUser: null, - tokenRes: response.credential, - setLoading, - }); - } - - google.accounts.id.initialize({ - client_id: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID, - callback: handleCredentialResponse, - }); - - google.accounts.id.renderButton(document.getElementById("google-identity-button"), { - theme: "outline", - size: "large", - logo_alignment: "center", - }); - } - - //////////////////////////////////////// - //////////////////////////////////////// - //////////////////////////////////////// - - if (triggerPrompt) { - google.accounts.id.prompt( - /** - * Google prompt notification callback - * ======================================================== - * @param {object} notification - Notification object - * @param {function(): string} notification.getMomentType - Notification moment type - * @param {function(): string} notification.getDismissedReason - Notification get Dismissed Reason - * @param {function(): string} notification.getNotDisplayedReason - Notification get Not Displayed Reason - * @param {function(): string} notification.getSkippedReason - Notification get Skipped Reason - * @param {function(): boolean} notification.isDismissedMoment - Notification is Dismissed Moment - * @param {function(): boolean} notification.isDisplayMoment - Notification is Display Moment - * @param {function(): boolean} notification.isDisplayed - Notification is Displayed - * @param {function(): boolean} notification.isNotDisplayed - Notification is Not Displayed - * @param {function(): boolean} notification.isSkippedMoment - Notification is Skipped Moment - */ - (notification) => { - notification.isDisplayed(); - } - ); - } - } - }; - - //////////////////////////////////////// - //////////////////////////////////////// - //////////////////////////////////////// - - const currentLocation = window.location.pathname; - - //////////////////////////////////////// - //////////////////////////////////////// - //////////////////////////////////////// -}; - -////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////// - -/** - * Login with google callback function - * ============================================================================== - * @description This function takes in a *SINGLE* input file from a HTML file input element. - * HTML file input elements usually return an array of input objects, so be sure to select the target - * file from the array. - * - * @async - * - * @param {object} params - Single object passed - * @param {string} params.clientId - Google app client ID {@link https://datasquirel.com/docs} - * @param {HTMLElement} params.element - HTML Element to display google login button - * @param {boolean} params.triggerPrompt - Whether to trigger Google signing popup or not: {@link https://datasquirel.com/docs} - * @param {function(): void} [params.readyStateDispatch] - React setState Function: sets value to "true" - * - * @returns { Promise } - Return - */ -function userLoginWithGoogle({ gUser, tokenRes, setLoading }) { - setLoading(true); - - if (!tokenRes) { - console.log("No Token Response received!"); - return closeLoader(); - } - - fetchApi(`/api/social-login/google-auth${window.location.search}`, { - method: "post", - body: { - token: tokenRes, - }, - }) - .then(async (res) => { - if (res.success && res.user) { - localStorage.setItem("csrf", res.user.csrf_k); - localStorage.setItem("user", JSON.stringify(res.user)); - - window.location.reload(); - } else { - console.log(res); - setLoading(false); - - if (res.alert) { - window.alert(res.msg); - } - } - }) - .catch(async (err) => { - alert("Login Failed"); - - console.log("Google login fetch error => ", err); - - setLoading(false); - }); -} diff --git a/client/auth/loginWithGoogle.js b/client/auth/loginWithGoogle.js deleted file mode 100644 index 7ae4f0b..0000000 --- a/client/auth/loginWithGoogle.js +++ /dev/null @@ -1,179 +0,0 @@ -/** - * Type Definitions - * =============================================================================== - */ - -/** - * @typedef {object} GoogleIdentityPromptNotification - * @property {function(): string} getMomentType - Notification moment type - * @property {function(): string} getDismissedReason - Notification get Dismissed Reason - * @property {function(): string} getNotDisplayedReason - Notification get Not Displayed Reason - * @property {function(): string} getSkippedReason - Notification get Skipped Reason - * @property {function(): boolean} isDismissedMoment - Notification is Dismissed Moment - * @property {function(): boolean} isDisplayMoment - Notification is Display Moment - * @property {function(): boolean} isDisplayed - Notification is Displayed - * @property {function(): boolean} isNotDisplayed - Notification is Not Displayed - * @property {function(): boolean} isSkippedMoment - Notification is Skipped Moment - */ - -////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////// - -/** - * Login with Google Function - * =============================================================================== - * @description This function uses google identity api to login a user with datasquirel - * - * @async - * - * @param {object} params - Single object passed - * @param {string} params.username - Username or email - * @param {string} params.database - Target database - * @param {string} params.clientId - Google app client ID: {@link https://datasquirel.com/docs} - * @param {HTMLElement} params.element - HTML Element to display google login button - * @param {boolean} params.triggerPrompt - Whether to trigger Google signing popup or not: {@link https://datasquirel.com/docs} - * @param {function(): void} [params.readyStateDispatch] - React setState Function: sets whether the google login button is ready or not - * - * @returns {Promise} - Return - */ -module.exports = async function loginWithGoogle({ username, database, clientId, element, triggerPrompt, readyStateDispatch }) { - /** - * == Initialize - * - * @description Initialize - */ - const googleScript = document.createElement("script"); - googleScript.src = "https://accounts.google.com/gsi/client"; - googleScript.className = "social-script-tag"; - - document.body.appendChild(googleScript); - - googleScript.onload = function (e) { - if (google) { - if (readyStateDispatch) readyStateDispatch(true); - - //////////////////////////////////////// - //////////////////////////////////////// - //////////////////////////////////////// - - if (element) { - /** - * Handle google credentials response - * ======================================================== - * @param {object} response - Google response with credentials - * @param {string} response.credential - Google access token - */ - function handleCredentialResponse(response) { - userLoginWithGoogle({ - gUser: null, - accessToken: response.credential, - }).then((result) => { - console.log(result); - }); - } - - google.accounts.id.initialize({ - client_id: clientId, - callback: handleCredentialResponse, - }); - - google.accounts.id.renderButton(element, { - theme: "outline", - size: "large", - logo_alignment: "center", - }); - } - - //////////////////////////////////////// - //////////////////////////////////////// - //////////////////////////////////////// - - if (triggerPrompt) { - google.accounts.id.prompt( - /** - * Google prompt notification callback - * ======================================================== - * @param {GoogleIdentityPromptNotification} notification - Notification object - */ - (notification) => { - notification.isDisplayed(); - } - ); - } - } - }; - - ////////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////////// - - /** - * Login with google callback function - * ============================================================================== - * @description This function uses the google token gotten from "loginWithGoogle" function - * and makes the required request to datasquirel - * - * @async - * - * @param {object} params - Single object passed - * @param {string} params.accessToken - Google access token - * - * @returns { Promise } - Return - */ - async function userLoginWithGoogle({ accessToken }) { - if (!accessToken) { - console.log("No Token Response received!"); - return closeLoader(); - } - - return await new Promise((resolve, reject) => { - fetch(`https://datasquirel.com/api/user/google-login`, { - method: "post", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - token: accessToken, - clientId: clientId, - username: username, - database: database, - }), - }) - .then(async (res) => { - if (res.success && res.user) { - localStorage.setItem("csrf", res.user.csrf_k); - localStorage.setItem("user", JSON.stringify(res.user)); - - resolve(true); - } else { - console.log(res); - - if (res.alert) { - window.alert(res.msg); - } - - resolve(false); - } - }) - .catch(async (err) => { - alert("Login Failed"); - console.log("Google login fetch error => ", err); - resolve(false); - }); - }); - } - - ////////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////////// -}; diff --git a/client/auth/logout.js b/client/auth/logout.js new file mode 100644 index 0000000..07738d5 --- /dev/null +++ b/client/auth/logout.js @@ -0,0 +1,137 @@ +/** + * Type Definitions + * =============================================================================== + */ + +const parseClientCookies = require("../utils/parseClientCookies"); + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +/** + * Login with Google Function + * =============================================================================== + * @description This function uses google identity api to login a user with datasquirel + * + * @async + * + * @param {object} params - Single object passed + * @param {string|null} params.googleClientId - Google client Id if applicable + * + * @requires localStorageUser - a "user" JSON string stored in local storage with all + * the necessary user data gotten from the server + * + * @returns {Promise} - Return + */ +module.exports = async function logout({ googleClientId }) { + /** + * == Initialize + * + * @description Initialize + */ + const localUser = localStorage.getItem("user"); + let targetUser; + + try { + targetUser = JSON.parse(localUser); + } catch (error) { + console.log(error); + } + + if (!targetUser) { + window.alert("NO client user object found in localStorage"); + return false; + } + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + const cookies = parseClientCookies(); + const socialId = cookies.datasquirel_social_id; + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + localStorage.setItem("user", "{}"); + localStorage.removeItem("csrf"); + + document.cookie = `datasquirel_social_id=null;samesite=strict;path=/`; + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + const response = await new Promise((resolve, reject) => { + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + + if (googleClientId) { + const googleScript = document.createElement("script"); + googleScript.src = "https://accounts.google.com/gsi/client"; + googleScript.className = "social-script-tag"; + + document.body.appendChild(googleScript); + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + googleScript.onload = function (e) { + if (google) { + if (readyStateDispatch) readyStateDispatch(true); + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + google.accounts.id.initialize({ + client_id: clientId, + }); + + google.accounts.id.revoke(socialId, (done) => { + console.log(done.error); + + resolve(true); + }); + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + } + }; + } + + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + }); + + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + + return response; + + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// +}; diff --git a/client/utils/parseClientCookies.js b/client/utils/parseClientCookies.js new file mode 100644 index 0000000..5938f96 --- /dev/null +++ b/client/utils/parseClientCookies.js @@ -0,0 +1,65 @@ +/** + * ============================================================================== + * Imports + * ============================================================================== + */ + +/** + * Parse request cookies + * ============================================================================== + * + * @description This function takes in a request object and returns the cookies as a JS object + * + * @async + * + * @param {object} params - main params object + * @param {object} params.request - HTTPS request object + * + * @returns {{}|null} + */ +module.exports = function () { + /** + * Check inputs + * + * @description Check inputs + */ + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + /** @type {string|null} */ + const cookieString = document.cookie; + + if (!cookieString || typeof cookieString !== "string") { + return null; + } + + /** @type {string[]} */ + const cookieSplitArray = cookieString.split(";"); + + let cookieObject = {}; + + cookieSplitArray.forEach((keyValueString) => { + const [key, value] = keyValueString.split("="); + if (key && typeof key == "string") { + cookieObject[key.replace(/^ +| +$/, "")] = value && typeof value == "string" ? value.replace(/^ +| +$/, "") : null; + } + }); + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + /** + * Make https request + * + * @description make a request to datasquirel.com + */ + + return cookieObject; +}; + +//////////////////////////////////////// +//////////////////////////////////////// +//////////////////////////////////////// diff --git a/package.json b/package.json index 3468df5..20315f5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "datasquirel", - "version": "1.1.42", + "version": "1.1.43", "description": "Cloud-based SQL data management tool", "main": "index.js", "scripts": { diff --git a/users/logout-user.js b/users/logout-user.js index 9b007bd..cdbfd51 100644 --- a/users/logout-user.js +++ b/users/logout-user.js @@ -1,10 +1,13 @@ +const parseCookies = require("../utils/functions/parseCookies"); + /** - * ============================================================================== - * Main Function + * Logout user * ============================================================================== * @param {object} params - Single Param object containing params * @param {object} params.request - Http request object * @param {object} params.response - Http response object + * + * @returns {{success: boolean, payload: string}} */ module.exports = function ({ request, response }) { /** @@ -13,7 +16,7 @@ module.exports = function ({ request, response }) { * @description Check Encryption Keys */ try { - const cookiesKeys = Object.keys(request.cookies); + const cookiesKeys = Object.keys(parseCookies({ request })); const authKeyName = cookiesKeys.filter((cookieKey) => cookieKey.match(/datasquirel_.*_auth_key/))[0]; const csrfName = cookiesKeys.filter((cookieKey) => cookieKey.match(/datasquirel_.*_csrf/))[0]; diff --git a/users/social/google-auth.js b/users/social/google-auth.js index ef6b064..bb11c56 100644 --- a/users/social/google-auth.js +++ b/users/social/google-auth.js @@ -170,12 +170,12 @@ module.exports = async function ({ key, token, database, clientId, response, enc encryptionSalt, }); - const { user } = httpResponse; + const { user, dsqlUserId } = httpResponse; - const authKeyName = `datasquirel_${user.id}_${database}_auth_key`; - const csrfName = `datasquirel_${user.id}_${database}_csrf`; + const authKeyName = `datasquirel_${dsqlUserId}_${database}_auth_key`; + const csrfName = `datasquirel_${dsqlUserId}_${database}_csrf`; - response.setHeader("Set-Cookie", [`${authKeyName}=${encryptedPayload};samesite=strict;path=/;HttpOnly=true;Secure=true`, `${csrfName}=${httpResponse.user.csrf_k};samesite=strict;path=/;sHttpOnly=true`, `dsqluid=${user.id};samesite=strict;path=/;HttpOnly=true`]); + response.setHeader("Set-Cookie", [`${authKeyName}=${encryptedPayload};samesite=strict;path=/;HttpOnly=true;Secure=true`, `${csrfName}=${user.csrf_k};samesite=strict;path=/;HttpOnly=true`, `dsqluid=${dsqlUserId};samesite=strict;path=/;HttpOnly=true`, `datasquirel_social_id=${user.social_id};samesite=strict;path=/`]); } //////////////////////////////////////// diff --git a/users/user-auth.js b/users/user-auth.js index 3c63515..0151106 100644 --- a/users/user-auth.js +++ b/users/user-auth.js @@ -4,6 +4,7 @@ * ============================================================================== */ const decrypt = require("../functions/decrypt"); +const parseCookies = require("../utils/functions/parseCookies"); /** ****************************************************************************** */ /** ****************************************************************************** */ @@ -56,12 +57,13 @@ module.exports = function ({ request, encryptionKey, encryptionSalt, level, data * * @description Grab the payload */ - const dsqluid = request.cookies.dsqluid; + const cookies = parseCookies({ request }); + const dsqluid = cookies.dsqluid; const authKeyName = `datasquirel_${dsqluid}_${database}_auth_key`; const csrfName = `datasquirel_${dsqluid}_${database}_csrf`; - const key = request.cookies[authKeyName]; - const csrf = request.cookies[csrfName]; + const key = cookies[authKeyName]; + const csrf = cookies[csrfName]; /** * Grab the payload diff --git a/utils/functions/parseCookies.js b/utils/functions/parseCookies.js new file mode 100644 index 0000000..fb7cea4 --- /dev/null +++ b/utils/functions/parseCookies.js @@ -0,0 +1,65 @@ +/** + * ============================================================================== + * Imports + * ============================================================================== + */ + +/** + * Parse request cookies + * ============================================================================== + * + * @description This function takes in a request object and returns the cookies as a JS object + * + * @async + * + * @param {object} params - main params object + * @param {object} params.request - HTTPS request object + * + * @returns {{}|null} + */ +module.exports = function ({ request }) { + /** + * Check inputs + * + * @description Check inputs + */ + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + /** @type {string|null} */ + const cookieString = request.headers.cookie; + + if (!cookieString || typeof cookieString !== "string") { + return null; + } + + /** @type {string[]} */ + const cookieSplitArray = cookieString.split(";"); + + let cookieObject = {}; + + cookieSplitArray.forEach((keyValueString) => { + const [key, value] = keyValueString.split("="); + if (key && typeof key == "string") { + cookieObject[key.replace(/^ +| +$/, "")] = value && typeof value == "string" ? value.replace(/^ +| +$/, "") : null; + } + }); + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + /** + * Make https request + * + * @description make a request to datasquirel.com + */ + + return cookieObject; +}; + +//////////////////////////////////////// +//////////////////////////////////////// +////////////////////////////////////////