From bc5c8cac33fa692d828f4edd071564705ebef276 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs Date: Fri, 31 May 2024 11:41:53 +0530 Subject: [PATCH] feat: existing user can sign in with sso --- apps/OpenSign/src/App.js | 2 + apps/OpenSign/src/components/Header.js | 4 +- apps/OpenSign/src/pages/Login.js | 225 +++++++++--------- apps/OpenSign/src/pages/SSOVerify.js | 208 ++++++++++++++++ apps/OpenSignServer/auth/authadapter.js | 27 +++ apps/OpenSignServer/cloud/main.js | 2 + .../cloud/parsefunction/SendMailOTPv1.js | 2 +- .../cloud/parsefunction/UserGroups.js | 52 ++-- .../cloud/parsefunction/ssoSignin.js | 115 +++++++++ apps/OpenSignServer/index.js | 11 +- 10 files changed, 497 insertions(+), 151 deletions(-) create mode 100644 apps/OpenSign/src/pages/SSOVerify.js create mode 100644 apps/OpenSignServer/auth/authadapter.js create mode 100644 apps/OpenSignServer/cloud/parsefunction/ssoSignin.js diff --git a/apps/OpenSign/src/App.js b/apps/OpenSign/src/App.js index 40bebb3de..eb6f3dbda 100644 --- a/apps/OpenSign/src/App.js +++ b/apps/OpenSign/src/App.js @@ -16,6 +16,7 @@ import PlaceHolderSign from "./pages/PlaceHolderSign"; import PdfRequestFiles from "./pages/PdfRequestFiles"; import LazyPage from "./primitives/LazyPage"; import { isEnableSubscription } from "./constant/const"; +import SSOVerify from "./pages/SSOVerify"; const DebugPdf = lazy(() => import("./pages/DebugPdf")); const ForgetPassword = lazy(() => import("./pages/ForgetPassword")); const GuestLogin = lazy(() => import("./pages/GuestLogin")); @@ -181,6 +182,7 @@ function App() { element={} /> + } /> } /> diff --git a/apps/OpenSign/src/components/Header.js b/apps/OpenSign/src/components/Header.js index 2b1a477cb..90563aac5 100644 --- a/apps/OpenSign/src/components/Header.js +++ b/apps/OpenSign/src/components/Header.js @@ -44,7 +44,9 @@ const Header = ({ showSidebar }) => { const closeDropdown = () => { setIsOpen(false); - Parse.User.logOut(); + if (Parse?.User?.current()) { + Parse.User.logOut(); + } let appdata = localStorage.getItem("userSettings"); let applogo = localStorage.getItem("appLogo"); let appName = localStorage.getItem("appName"); diff --git a/apps/OpenSign/src/pages/Login.js b/apps/OpenSign/src/pages/Login.js index 24ef4f7b8..c5251ed27 100644 --- a/apps/OpenSign/src/pages/Login.js +++ b/apps/OpenSign/src/pages/Login.js @@ -19,7 +19,7 @@ import Alert from "../primitives/Alert"; import { appInfo } from "../constant/appinfo"; import { fetchAppInfo } from "../redux/reducers/infoReducer"; import { showTenant } from "../redux/reducers/ShowTenant"; -import { fetchSubscription, getAppLogo } from "../constant/Utils"; +import { fetchSubscription, getAppLogo, openInNewTab } from "../constant/Utils"; function Login() { const navigate = useNavigate(); const location = useLocation(); @@ -1016,8 +1016,9 @@ function Login() { const handleCloseModal = () => { setIsModal(false); - Parse.User.logOut(); - + if (Parse?.User?.current()) { + Parse.User.logOut(); + } let appdata = localStorage.getItem("userSettings"); let applogo = localStorage.getItem("appLogo"); let appName = localStorage.getItem("appName"); @@ -1040,6 +1041,15 @@ function Login() { localStorage.setItem("baseUrl", baseUrl); localStorage.setItem("parseAppId", appid); }; + const handleSignInWithSSO = () => { + if (state?.email) { + openInNewTab( + `https://osl-jacksonv2.vercel.app/api/oauth/authorize?response_type=code&provider=saml&tenant=Okta-dev-nxglabs-in&product=OpenSign&redirect_uri=http://localhost:3000/sso&state=${state.email}` + ); + } else { + alert("Please provide email."); + } + }; return (
@@ -1087,136 +1097,123 @@ function Login() { </div> <div className="grid grid-cols-1 md:grid-cols-2 gap-x-2"> <div> - <div> - <form onSubmit={handleSubmit} aria-label="Login Form"> - <h1 className="text-[30px] mt-6">Welcome Back!</h1> - <fieldset> - <legend className="text-[12px] text-[#878787]"> - Login to your account - </legend> - <div className="px-6 py-4 outline outline-1 outline-slate-300/50 my-2 rounded shadow-md"> - <label className="block text-xs" htmlFor="email"> - Username - </label> + <form onSubmit={handleSubmit} aria-label="Login Form"> + <h1 className="text-[30px] mt-6">Welcome Back!</h1> + <fieldset> + <legend className="text-[12px] text-[#878787]"> + Login to your account + </legend> + <div className="px-6 py-4 outline outline-1 outline-slate-300/50 my-2 rounded shadow-md"> + <label className="block text-xs" htmlFor="email"> + Email + </label> + <input + id="email" + type="text" + className="px-3 py-2 w-full border-[1px] border-gray-300 rounded text-xs" + name="email" + value={state.email} + onChange={handleChange} + required + /> + <hr className="my-2 border-none" /> + <label className="block text-xs" htmlFor="password"> + Password + </label> + <div className="relative"> <input - id="email" - type="text" + id="password" + type={state.passwordVisible ? "text" : "password"} className="px-3 py-2 w-full border-[1px] border-gray-300 rounded text-xs" - name="email" - value={state.email} + name="password" + value={state.password} onChange={handleChange} required /> - <hr className="my-2 border-none" /> - <label className="block text-xs" htmlFor="password"> - Password - </label> - <div className="relative"> - <input - id="password" - type={state.passwordVisible ? "text" : "password"} - className="px-3 py-2 w-full border-[1px] border-gray-300 rounded text-xs" - name="password" - value={state.password} - onChange={handleChange} - required - /> - <span - className={`absolute top-[50%] right-[10px] -translate-y-[50%] cursor-pointer ${ - state.passwordVisible - ? "text-[#007bff]" - : "text-black" - }`} - onClick={togglePasswordVisibility} - > - {state.passwordVisible ? ( - <i className="fa fa-eye-slash text-xs pb-1" /> // Close eye icon - ) : ( - <i className="fa fa-eye text-xs pb-1 " /> // Open eye icon - )} - </span> - </div> - <div className="relative mt-1"> - <NavLink - to="/forgetpassword" - className="text-[13px] text-[#002864] hover:underline underline-offset-1 focus:outline-none cursor-pointer ml-1" - > - Forgot Password? - </NavLink> - </div> + <span + className={`absolute top-[50%] right-[10px] -translate-y-[50%] cursor-pointer ${ + state.passwordVisible + ? "text-[#007bff]" + : "text-black" + }`} + onClick={togglePasswordVisibility} + > + {state.passwordVisible ? ( + <i className="fa fa-eye-slash text-xs pb-1" /> // Close eye icon + ) : ( + <i className="fa fa-eye text-xs pb-1 " /> // Open eye icon + )} + </span> + </div> + <div className="relative mt-1"> + <NavLink + to="/forgetpassword" + className="text-[13px] text-[#002864] hover:underline underline-offset-1 focus:outline-none cursor-pointer ml-1" + > + Forgot Password? + </NavLink> </div> - </fieldset> - <div className="flex flex-col md:flex-row justify-between items-stretch gap-8 text-center text-xs font-bold mt-2"> - <button - type="submit" - className="rounded-sm bg-[#3ac9d6] text-white w-full py-3 shadow outline-none uppercase focus:ring-2 focus:ring-blue-600" - disabled={state.loading} - > - {state.loading ? "Loading..." : "Login"} - </button> - <NavLink - className="rounded-sm cursor-pointer bg-white border-[1px] border-[#15b4e9] text-[#15b4e9] w-full py-3 shadow uppercase" - to={ - location.search - ? "/signup" + location.search - : "/signup" - } - style={width < 768 ? { textAlign: "center" } : {}} - > - Create Account - </NavLink> </div> - </form> - <br /> - {(appInfo.fbAppId || appInfo.googleClietId) && ( - <div className="text-sm flex justify-center items-center"> - <hr className="border-[1px] border-gray-300 w-full" /> - <span className="px-2 text-gray-500 cursor-default"> - OR - </span> - <hr className="border-[1px] border-gray-300 w-full" /> - </div> - )} - <br /> - <div - style={{ - textAlign: "center", - display: "flex", - alignItems: "center", - justifyContent: "center" - }} - > - {/* {appInfo.fbAppId && appInfo.fbAppId !== "" ? ( + </fieldset> + <div className="flex flex-col md:flex-row justify-between items-stretch gap-8 text-center text-xs font-bold mt-2"> + <button + type="submit" + className="rounded-sm bg-[#3ac9d6] text-white w-full py-3 shadow outline-none uppercase focus:ring-2 focus:ring-blue-600" + disabled={state.loading} + > + {state.loading ? "Loading..." : "Login"} + </button> + <NavLink + className="rounded-sm cursor-pointer bg-white border-[1px] border-[#15b4e9] text-[#15b4e9] w-full py-3 shadow uppercase" + to={ + location.search + ? "/signup" + location.search + : "/signup" + } + style={width < 768 ? { textAlign: "center" } : {}} + > + Create Account + </NavLink> + </div> + </form> + <br /> + {appInfo.googleClietId && ( + <div className="text-sm flex justify-center items-center"> + <hr className="border-[1px] border-gray-300 w-full" /> + <span className="px-2 text-gray-500 cursor-default"> + OR + </span> + <hr className="border-[1px] border-gray-300 w-full" /> + </div> + )} + <br /> + <div className="flex flex-col justify-center items-center gap-y-3"> + {/* {appInfo?.fbAppId && ( <LoginFacebook FBCred={appInfo.fbAppId} thirdpartyLoginfn={thirdpartyLoginfn} thirdpartyLoader={state.thirdpartyLoader} setThirdpartyLoader={setThirdpartyLoader} /> - ) : null} */} - </div> - <div style={{ margin: "10px 0" }}></div> + )} */} + {appInfo?.googleClietId && ( + <GoogleSignInBtn + GoogleCred={appInfo.googleClietId} + thirdpartyLoginfn={thirdpartyLoginfn} + thirdpartyLoader={state.thirdpartyLoader} + setThirdpartyLoader={setThirdpartyLoader} + /> + )} <div - style={{ - textAlign: "center", - display: "flex", - alignItems: "center", - justifyContent: "center" - }} + className="cursor-pointer border-[1px] border-gray-300 rounded px-[40px] py-2 font-semibold text-sm hover:border-[#d2e3fc] hover:bg-[#ecf3feb7]" + onClick={() => handleSignInWithSSO()} > - {appInfo.googleClietId && appInfo.googleClietId !== "" ? ( - <GoogleSignInBtn - GoogleCred={appInfo.googleClietId} - thirdpartyLoginfn={thirdpartyLoginfn} - thirdpartyLoader={state.thirdpartyLoader} - setThirdpartyLoader={setThirdpartyLoader} - /> - ) : null} + Sign in with SSO </div> </div> </div> {width >= 768 && ( - <div className="self-center"> + <div className="place-self-center"> <div className="mx-auto md:w-[300px] lg:w-[400px] xl:w-[500px]"> <img src={login_img} diff --git a/apps/OpenSign/src/pages/SSOVerify.js b/apps/OpenSign/src/pages/SSOVerify.js new file mode 100644 index 000000000..e811b2f08 --- /dev/null +++ b/apps/OpenSign/src/pages/SSOVerify.js @@ -0,0 +1,208 @@ +// import axios from "axios"; +import React, { useEffect } from "react"; +import { useLocation, useNavigate } from "react-router-dom"; +import Parse from "parse"; +import axios from "axios"; +import { appInfo } from "../constant/appinfo"; +import { isEnableSubscription } from "../constant/const"; +import { fetchSubscription } from "../constant/Utils"; +import { useDispatch } from "react-redux"; +import { showTenant } from "../redux/reducers/ShowTenant"; +const SSOVerify = () => { + const location = useLocation(); + const navigate = useNavigate(); + const dispatch = useDispatch(); + + useEffect(() => { + getAccesstoken(); + // eslint-disable-next-line + }, []); + const getAccesstoken = async () => { + const param = new URLSearchParams(location.search); + const code = param?.get("code"); + const state = param?.get("state"); + // console.log("code ", code); + try { + const ssosign = await Parse.Cloud.run("ssosign", { + code: code, + email: state + }); + await checkExtUser(ssosign); + // console.log("ssores ", ssosign); + } catch (err) { + console.log("err", err); + } + }; + const checkExtUser = async (ssosign) => { + // const extUser = new Parse.Query("contracts_Users"); + // extUser.equalTo("Email", details.Gmail); + // const extRes = await extUser.first(); + const params = { email: ssosign?.email }; + const extRes = await Parse.Cloud.run("getUserDetails", params); + // console.log("extRes ", extRes); + if (extRes) { + if (ssosign && ssosign.sessiontoken) { + const LocalUserDetails = { + name: extRes.Name, + email: extRes.email, + phone: extRes?.get("Phone") || "", + company: extRes.get("Company") + }; + localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails)); + thirdpartyLoginfn(ssosign.sessiontoken); + } + return { msg: "exist" }; + } else { + // setIsModal(true); + // setThirdpartyLoader(false); + console.log("not exist"); + return { msg: "notexist" }; + } + }; + const thirdpartyLoginfn = async (sessionToken) => { + const baseUrl = localStorage.getItem("baseUrl"); + const parseAppId = localStorage.getItem("parseAppId"); + try { + const validUser = await Parse.User.become(sessionToken); + if (validUser) { + localStorage.setItem("accesstoken", sessionToken); + const _user = JSON.parse(JSON.stringify(validUser)); + // console.log("_user ", _user); + localStorage.setItem("UserInformation", JSON.stringify(_user)); + if (_user.ProfilePic) { + localStorage.setItem("profileImg", _user.ProfilePic); + } else { + localStorage.setItem("profileImg", ""); + } + // Check extended class user role and tenentId + try { + let userRoles = []; + if (appInfo.settings) { + const userSettings = appInfo.settings; + + //Get Current user roles + let url = `${baseUrl}functions/UserGroups`; + const headers = { + "Content-Type": "application/json", + "X-Parse-Application-Id": parseAppId, + sessionToken: _user.sessionToken + }; + const body = { appname: appInfo.appname }; + const UserGroupsRes = await axios.post(url, JSON.stringify(body), { + headers: headers + }); + userRoles = (UserGroupsRes.data && UserGroupsRes.data.result) || []; + if (userRoles) { + let _currentRole = ""; + // const excludeRoles = ["contracts_Guest", `contracts_appeditor`]; + // const filteredRole = userRoles.filter( + // (x) => !excludeRoles.includes(x) + // ); + // if (filteredRole?.length > 1) { + // _currentRole = userRoles.filter((x) => x === "contracts_User"); + // } else { + // _currentRole = rolesfiltered[0]; + // } + _currentRole = userRoles?.find((x) => x === "contracts_User"); + if (_currentRole) { + const roleSetting = userSettings?.find( + (setting) => setting.role === _currentRole + ); + const redirectUrl = + location?.state?.from || + `/${roleSetting.pageType}/${roleSetting.pageId}`; + const _role = _currentRole.replace(`${appInfo.appname}_`, ""); + localStorage.setItem("_user_role", _role); + // Get TenentID from Extendend Class + localStorage.setItem( + "extended_class", + roleSetting.extended_class + ); + try { + const extUser = await Parse.Cloud.run("getUserDetails", { + email: _user.email + }); + if (extUser) { + const _extUser = JSON.parse(JSON.stringify(extUser)); + localStorage.setItem("userEmail", _extUser.Email); + localStorage.setItem("username", _extUser.Name); + localStorage.setItem("scriptId", true); + // console.log("_extUser", _extUser); + let tenentInfo = []; + const results = [extUser]; + if (results) { + let extendedInfo_stringify = JSON.stringify(results); + localStorage.setItem( + "Extand_Class", + extendedInfo_stringify + ); + if (_extUser.TenantId) { + const obj = { + tenentId: _extUser.TenantId.objectId, + tenentName: _extUser.TenantId.TenantName || "" + }; + localStorage.setItem("TenantId", obj.tenentId); + tenentInfo.push(obj); + dispatch(showTenant(obj.tenentName || "")); + localStorage.setItem( + "TenantName", + obj.tenentName || "" + ); + } + localStorage.setItem("PageLanding", roleSetting.pageId); + localStorage.setItem("defaultmenuid", roleSetting.menuId); + localStorage.setItem("pageType", roleSetting.pageType); + if (isEnableSubscription) { + const res = await fetchSubscription(); + const plan = res.plan; + const billingDate = res.billingDate; + if (plan === "freeplan") { + navigate(redirectUrl); + } else if (billingDate) { + if (new Date(billingDate) > new Date()) { + localStorage.removeItem("userDetails"); + navigate(redirectUrl); + } else { + navigate(`/subscription`, { + replace: true + }); + } + } else { + navigate(`/subscription`, { + replace: true + }); + } + } else { + navigate(redirectUrl); + } + } + } + } catch (err) { + // const payload = { + // sessionToken: sessionToken + // }; + // setThirdpartyLoader(false); + // handleSubmitbtn(payload); + + alert("ext user not exist."); + console.log("err in get extUser", err); + } + } else { + alert("contracts_User role not exist"); + } + } else { + alert("contracts_User role not exist"); + } + } + } catch (err) { + console.log("err in usergroups", err); + } + } + } catch (err) { + console.log("Err in me call", err); + } + }; + return <div>Verifying SSO....</div>; +}; + +export default SSOVerify; diff --git a/apps/OpenSignServer/auth/authadapter.js b/apps/OpenSignServer/auth/authadapter.js new file mode 100644 index 000000000..a48b3cb76 --- /dev/null +++ b/apps/OpenSignServer/auth/authadapter.js @@ -0,0 +1,27 @@ +import axios from 'axios'; + +export const SSOAuth = { + // Returns a promise that fulfills if this user mail is valid. + validateAuthData: async authData => { + try { + const response = await axios.get('https://osl-jacksonv2.vercel.app/api/oauth/userinfo', { + headers: { + Authorization: `Bearer ${authData.access_token}`, + }, + }); + // console.log('response.data.id ', response.data.email); + // console.log('authData.id', authData.id); + if (response.data && response.data.id && response.data.email === authData.id) { + return; + } + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'SSO auth is invalid for this user.'); + } catch (error) { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'SSO auth is invalid for this user.'); + } + }, + + // Returns a promise that fulfills if this app id is valid. + validateAppId: () => { + return Promise.resolve(); + }, +}; diff --git a/apps/OpenSignServer/cloud/main.js b/apps/OpenSignServer/cloud/main.js index 6de2c181a..1bbd9feea 100644 --- a/apps/OpenSignServer/cloud/main.js +++ b/apps/OpenSignServer/cloud/main.js @@ -42,6 +42,7 @@ import linkContactToDoc from './parsefunction/linkContactToDoc.js'; import CreatePublicTemplate from './parsefunction/CreatePublicTemplate.js'; import GetPublicUserName from './parsefunction/GetPublicUserName.js'; import GetPublicTemplate from './parsefunction/GetPublicTemplate.js'; +import ssoSignin from './parsefunction/ssoSignin.js'; // This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic. Parse.Cloud.afterSave('contracts_Document', DocumentAftersave); @@ -94,3 +95,4 @@ Parse.Cloud.define('linkcontacttodoc', linkContactToDoc); Parse.Cloud.define('createpublictemplate', CreatePublicTemplate); Parse.Cloud.define('getpublicusername', GetPublicUserName); Parse.Cloud.define('getpublictemplate', GetPublicTemplate); +Parse.Cloud.define('ssosign', ssoSignin); diff --git a/apps/OpenSignServer/cloud/parsefunction/SendMailOTPv1.js b/apps/OpenSignServer/cloud/parsefunction/SendMailOTPv1.js index 290ff0c3a..52054688b 100644 --- a/apps/OpenSignServer/cloud/parsefunction/SendMailOTPv1.js +++ b/apps/OpenSignServer/cloud/parsefunction/SendMailOTPv1.js @@ -39,7 +39,7 @@ async function sendMailOTPv1(request) { code + '</p></div> </div> </div></body></html>', }); - console.log('OTP sent'); + console.log('OTP sent', code); if (request.params?.docId) { const extUserId = await getDocument(request.params?.docId); if (extUserId) { diff --git a/apps/OpenSignServer/cloud/parsefunction/UserGroups.js b/apps/OpenSignServer/cloud/parsefunction/UserGroups.js index 3e3dc0cb6..5ab2f4724 100644 --- a/apps/OpenSignServer/cloud/parsefunction/UserGroups.js +++ b/apps/OpenSignServer/cloud/parsefunction/UserGroups.js @@ -2,41 +2,41 @@ //-- Export Modules import dotenv from 'dotenv'; -dotenv.config() -import axios from "axios"; +dotenv.config(); +import axios from 'axios'; export async function getUserGroups(request) { try { var appname = request.params.appname; - if (appname == "") { - return Promise.reject("Error:please provide appname"); + if (appname == '') { + return Promise.reject('Error:please provide appname'); } var response = {}; var rolelist = {}; - appname = appname + "_"; + appname = appname + '_'; //--function to get the userid from session token function getuserid(request) { return new Promise(function (resolve, reject) { const options = { - url: process.env.SERVER_URL + "/users/me", - method: "get", + url: process.env.SERVER_URL + '/users/me', + method: 'get', headers: { - "X-Parse-Application-Id": process.env.APP_ID, - "X-Parse-Session-Token": request.headers["sessiontoken"], + 'X-Parse-Application-Id': process.env.APP_ID, + 'X-Parse-Session-Token': request.headers['sessiontoken'], }, }; axios(options) - .then((x) => { + .then(x => { const body = x.data; - var error = body == "" ? true : false; + var error = body == '' ? true : false; if (error) { - reject("result not found!"); + reject('result not found!'); } else { resolve(body); } }) - .catch((err) => { + .catch(err => { if (err) { console.error(err); return; @@ -46,7 +46,7 @@ export async function getUserGroups(request) { } var userData = await getuserid(request); var userid = userData.objectId; - console.log("userid " + userid); + // console.log("userid " + userid); var url = process.env.SERVER_URL + '/roles?where={"users":{"__type":"Pointer","className":"_User","objectId":"' + @@ -60,29 +60,29 @@ export async function getUserGroups(request) { return new Promise(function (resolve, reject) { const options = { url: url, - method: "get", + method: 'get', headers: { - "X-Parse-Application-Id": process.env.APP_ID, + 'X-Parse-Application-Id': process.env.APP_ID, }, }; axios(options) - .then((x) => { + .then(x => { const body = x.data; var roleres = []; - for (var i = 0; i < body["results"].length; i++) { - var rolename = body["results"][i]["name"]; + for (var i = 0; i < body['results'].length; i++) { + var rolename = body['results'][i]['name']; //var roleprefix = rolename.split("_")[0]; roleres.push(rolename); } - var error = roleres == "" ? true : false; + var error = roleres == '' ? true : false; if (error) { - reject("result not found!"); + reject('result not found!'); } else { resolve(roleres); } }) - .catch((err) => { + .catch(err => { if (err) { console.error(err); return; @@ -92,7 +92,7 @@ export async function getUserGroups(request) { } rolelist = await getRoleList(request); - console.log(rolelist); + // console.log(rolelist); //--check user roles according to appId var rolesInapp = []; for (let i = 0; i < rolelist.length; i++) { @@ -102,11 +102,11 @@ export async function getUserGroups(request) { rolesInapp.push(rolelist[i]); } } - console.log(rolesInapp); + // console.log(rolesInapp); return rolesInapp; } catch (err) { - console.log("err in usergroup"); + console.log('err in usergroup'); console.log(err); - return Promise.reject("Error:Result not found"); + return Promise.reject('Error:Result not found'); } } diff --git a/apps/OpenSignServer/cloud/parsefunction/ssoSignin.js b/apps/OpenSignServer/cloud/parsefunction/ssoSignin.js new file mode 100644 index 000000000..ca243cd4f --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/ssoSignin.js @@ -0,0 +1,115 @@ +import axios from 'axios'; + +const serverUrl = process.env.SERVER_URL; +const APPID = process.env.APP_ID; +const masterKEY = process.env.MASTER_KEY; + +/** + * ssoSign is function which is used to sign up/sign in with SSO + * @param code It is code return by jackson using authorize endpoint + * @param email It is user's email with user sign in/sign up + * @returns if success {email, message, sessiontoken} else on reject {message} + */ + +export default async function ssoSignin(request) { + const code = request.params.code; + const userEmail = request.params.email; + + // console.log('code ', code); + try { + const headers = { 'content-type': 'application/x-www-form-urlencoded' }; + const axiosRes = await axios.post( + 'https://osl-jacksonv2.vercel.app/api/oauth/token', + { + grant_type: 'authorization_code', + client_id: 'dummy', + tenant: 'Okta-dev-nxglabs-in', + product: 'OpenSign', + client_secret: 'dummy', + redirect_uri: 'http://localhost:3000/sso', + code: code, + }, + { headers: headers } + ); + const ssoAccessToken = axiosRes.data && axiosRes.data.access_token; + // console.log('ssoAccessToken ', ssoAccessToken); + const authData = { sso: { id: userEmail, access_token: ssoAccessToken } }; + const userQuery = new Parse.Query(Parse.User); + userQuery.equalTo('email', userEmail); + const res = await userQuery.first({ useMasterKey: true }); + if (res) { + try { + const SignIn = await axios.put( + serverUrl + '/users/' + res.id, + { authData: authData }, + { + headers: { + 'X-Parse-Application-Id': APPID, + 'X-Parse-Master-key': masterKEY, + }, + } + ); + + if (SignIn.data) { + const sessiontoken = SignIn.data.sessionToken; + // console.log('sso sessiontoken', sessiontoken); + return { + email: userEmail, + message: 'User Sign In', + sessiontoken: sessiontoken, + }; + } + } catch (err) { + console.log('err in user sso sign in', err); + throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Internal server error.'); + } + } else { + // console.log("in sign up condition"); + const response = await axios.get('https://osl-jacksonv2.vercel.app/api/oauth/userinfo', { + headers: { + Authorization: `Bearer ${ssoAccessToken}`, + }, + }); + if (response.data && response.data.id) { + try { + const SignUp = await axios.post( + serverUrl + '/users', + { + authData: authData, + username: response.data.email, + email: response.data.email, + phone: response.data?.phone, + name: response.data?.firstName + response.data?.lastName, + }, + { + headers: { + 'X-Parse-Application-Id': APPID, + 'X-Parse-Revocable-Session': '1', + }, + } + ); + + // console.log("SignUp", SignUp); + + if (SignUp.data) { + const sessiontoken = SignUp.data.sessionToken; + const payload = { + email: userEmail, + message: 'User Sign Up', + sessiontoken: sessiontoken, + }; + return payload; + } + } catch (err) { + console.log('err in user sso sign up', err); + throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Internal server error.'); + } + } + } + } catch (err) { + const errCode = err?.response?.status || err?.code || 400; + const message = err?.response?.data || err?.message || 'Internal server error.'; + console.log('err in ssoSign', errCode, message); + throw new Parse.Error(errCode, message); + } +} diff --git a/apps/OpenSignServer/index.js b/apps/OpenSignServer/index.js index 9f7ab88ac..139f65bcf 100644 --- a/apps/OpenSignServer/index.js +++ b/apps/OpenSignServer/index.js @@ -18,7 +18,7 @@ import { createTransport } from 'nodemailer'; import { app as v1 } from './cloud/customRoute/v1/apiV1.js'; import { PostHog } from 'posthog-node'; import { useLocal } from './Utils.js'; - +import { SSOAuth } from './auth/authadapter.js'; let fsAdapter; if (useLocal !== 'true') { try { @@ -147,14 +147,7 @@ export const config = { google: { enabled: true, }, - ldap: { - enabled: true, - url: 'ldap://ldap.forumsys.com:389', - suffix: 'dc=example,dc=com', - // dn: 'ou=mathematicians, dc=example, dc=com', - groupCn: 'mathematicians', - groupFilter: '(&(uniqueMember=uid=,dc=example,dc=com)(objectClass=groupOfUniqueNames))', - }, + sso: SSOAuth, }, }; // Client-keys like the javascript key or the .NET key are not necessary with parse-server