diff --git a/client/auth/github/getAccessToken.js b/client/auth/github/getAccessToken.js new file mode 100644 index 0000000..c0b7cad --- /dev/null +++ b/client/auth/github/getAccessToken.js @@ -0,0 +1,34 @@ +// @ts-check + +/** + * Login with Github Function + * =============================================================================== + * @description This function uses google identity api to login a user with datasquirel + * + * @async + * + * @param {object} params - Single object passed + * @param {string} params.clientId - Github app client ID: {@link https://datasquirel.com/docs} + * @param {string} params.redirectUrl - Github Redirect URL as listed in your oauth app settings: {@link https://datasquirel.com/docs} + * @param {function(boolean): void} [params.setLoading] - React setState Function: sets whether the google login button is ready or not + * + * @returns {void} - Return + */ +module.exports = function getAccessToken({ clientId, redirectUrl, setLoading }) { + /** + * == Initialize + * + * @description Initialize + */ + if (setLoading) setLoading(true); + + const fetchUrl = `https://github.com/login/oauth/authorize?client_id=${clientId}&scope=user&redirect_uri=${redirectUrl}${window.location.pathname}`; + window.location.assign(fetchUrl); + + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// +}; diff --git a/client/index.js b/client/index.js index 4107ff1..34ff5bd 100644 --- a/client/index.js +++ b/client/index.js @@ -1,3 +1,5 @@ +// @ts-check + /** * Imports */ @@ -5,6 +7,7 @@ const imageInputFileToBase64 = require("./media/imageInputFileToBase64"); const imageInputToBase64 = require("./media/imageInputToBase64"); const inputFileToBase64 = require("./media/inputFileToBase64"); const getAccessToken = require("./auth/google/getAccessToken"); +const getGithubAccessToken = require("./auth/github/getAccessToken"); const logout = require("./auth/logout"); //////////////////////////////////////// @@ -27,6 +30,9 @@ const auth = { google: { getAccessToken: getAccessToken, }, + github: { + getAccessToken: getGithubAccessToken, + }, logout: logout, }; diff --git a/index.js b/index.js index 9067b4e..d279b71 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,5 @@ +// @ts-check + /** * Imports */ @@ -12,6 +14,7 @@ const userAuth = require("./users/user-auth"); const reAuthUser = require("./users/reauth-user"); const getUser = require("./users/get-user"); const loginWithGoogle = require("./users/social/google-auth"); +const loginWithGithub = require("./users/social/github-auth"); const sanitizeSql = require("./utils/functions/sanitizeSql"); //////////////////////////////////////// @@ -31,6 +34,7 @@ const user = { getUser: getUser, social: { loginWithGoogle: loginWithGoogle, + loginWithGithub: loginWithGithub, }, }; @@ -57,13 +61,3 @@ module.exports = datasquirel; //////////////////////////////////////// //////////////////////////////////////// //////////////////////////////////////// - -exports.get = datasquirel.get; -exports.post = datasquirel.post; -exports.media = datasquirel.media; -exports.user = datasquirel.user; -exports.sanitizeSql = datasquirel.sanitizeSql; - -//////////////////////////////////////// -//////////////////////////////////////// -//////////////////////////////////////// diff --git a/package.json b/package.json index 0ae9de0..1b0a474 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "datasquirel", - "version": "1.1.93", + "version": "1.1.94", "description": "Cloud-based SQL data management tool", "main": "index.js", "scripts": { diff --git a/users/social/github-auth.js b/users/social/github-auth.js new file mode 100644 index 0000000..cf52754 --- /dev/null +++ b/users/social/github-auth.js @@ -0,0 +1,199 @@ +// @ts-check + +/** + * ============================================================================== + * Imports + * ============================================================================== + */ +const https = require("https"); +const encrypt = require("../../functions/encrypt"); + +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ + +/** + * @typedef {object} FunctionReturn + * @property {boolean} success - Did the function run successfully? + * @property {{id: number, first_name: string, last_name: string, csrf_k: string, social_id: string} | null} user - Returned User + * @property {number} [dsqlUserId] - Dsql User Id + * @property {string} [msg] - Response message + */ + +/** + * SERVER FUNCTION: Login with google Function + * ============================================================================== + * + * @async + * + * @param {object} params - main params object + * @param {string} params.key - API full access key + * @param {string} params.code - Github access code gotten from the client side + * @param {string?} params.email - Email gotten from the client side if available + * @param {string} params.database - Target database name(slug) + * @param {string} params.clientId - Github client id + * @param {string} params.clientSecret - Github client Secret + * @param {object} params.response - HTTPS response object + * @param {string} params.encryptionKey - Encryption key + * @param {string} params.encryptionSalt - Encryption salt + * + * @returns { Promise } + */ +async function githubAuth({ key, code, email, database, clientId, clientSecret, response, encryptionKey, encryptionSalt }) { + /** + * Check inputs + * + * @description Check inputs + */ + if (!key || key?.match(/ /)) { + return { + success: false, + user: null, + msg: "Please enter API full access Key", + }; + } + + if (!code || code?.match(/ /)) { + return { + success: false, + user: null, + msg: "Please enter Github Access Token", + }; + } + + if (!database || database?.match(/ /)) { + return { + success: false, + user: null, + msg: "Please provide database slug name you want to access", + }; + } + + if (!clientId || clientId?.match(/ /)) { + return { + success: false, + user: null, + msg: "Please enter Github OAUTH client ID", + }; + } + + if (!response || !response?.setHeader) { + return { + success: false, + user: null, + msg: "Please provide a valid HTTPS response object", + }; + } + + if (!encryptionKey || encryptionKey?.match(/ /)) { + return { + success: false, + user: null, + msg: "Please provide a valid encryption key", + }; + } + + if (!encryptionSalt || encryptionSalt?.match(/ /)) { + return { + success: false, + user: null, + msg: "Please provide a valid encryption salt", + }; + } + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + /** + * Make https request + * + * @description make a request to datasquirel.com + * @type {FunctionReturn} - Https response object + */ + const httpResponse = await new Promise((resolve, reject) => { + const reqPayload = JSON.stringify({ + code, + email, + clientId, + clientSecret, + database, + }); + + const httpsRequest = https.request( + { + method: "POST", + headers: { + "Content-Type": "application/json", + "Content-Length": Buffer.from(reqPayload).length, + Authorization: key, + }, + port: 443, + hostname: "datasquirel.com", + path: `/api/user/github-login`, + }, + + /** + * Callback Function + * + * @description https request callback + */ + (response) => { + var str = ""; + + response.on("data", function (chunk) { + str += chunk; + }); + + response.on("end", function () { + resolve(JSON.parse(str)); + }); + + response.on("error", (err) => { + reject(err); + }); + } + ); + httpsRequest.write(reqPayload); + httpsRequest.end(); + }); + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + /** + * Make https request + * + * @description make a request to datasquirel.com + */ + if (httpResponse?.success && httpResponse?.user) { + let encryptedPayload = encrypt({ + data: JSON.stringify(httpResponse.user), + encryptionKey, + encryptionSalt, + }); + + const { user, dsqlUserId } = httpResponse; + + 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}=${user.csrf_k};samesite=strict;path=/;HttpOnly=true`, `dsqluid=${dsqlUserId};samesite=strict;path=/;HttpOnly=true`, `datasquirel_social_id=${user.social_id};samesite=strict;path=/`]); + } + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + return httpResponse; +} + +//////////////////////////////////////// +//////////////////////////////////////// +//////////////////////////////////////// + +module.exports = githubAuth; diff --git a/users/social/google-auth.js b/users/social/google-auth.js index a6c8f85..1d4e5d8 100644 --- a/users/social/google-auth.js +++ b/users/social/google-auth.js @@ -17,6 +17,7 @@ const encrypt = require("../../functions/encrypt"); * @typedef {object} FunctionReturn * @property {boolean} success - Did the function run successfully? * @property {{id: number, first_name: string, last_name: string}|null} user - Returned User + * @property {number} dsqlUserId - Dsql User Id * @property {string} [msg] - Response message */