diff --git a/apps/OpenSign/src/components/sidebar/Menu.js b/apps/OpenSign/src/components/sidebar/Menu.js index 584b6f410..0e42c542f 100644 --- a/apps/OpenSign/src/components/sidebar/Menu.js +++ b/apps/OpenSign/src/components/sidebar/Menu.js @@ -10,7 +10,11 @@ const Menu = ({ item, isOpen, closeSidebar }) => { ? `/${item.pageType}/${item.objectId}` : `/${item.objectId}` } - className="mx-auto flex items-center hover:bg-[#eef1f5] p-3 lg:p-4 cursor-pointer focus:text-[#0056b3] focus:bg-[#eef1f5]" + className={({ isActive }) => + `${ + isActive ? "text-[#0056b3] bg-[#eef1f5]" : "" + } mx-auto flex items-center hover:bg-[#eef1f5] p-3 lg:p-4 cursor-pointer` + } onClick={closeSidebar} tabIndex={isOpen ? 0 : -1} role="menuitem" diff --git a/apps/OpenSign/src/constant/Utils.js b/apps/OpenSign/src/constant/Utils.js index 147d360d2..3a24c4398 100644 --- a/apps/OpenSign/src/constant/Utils.js +++ b/apps/OpenSign/src/constant/Utils.js @@ -12,8 +12,12 @@ export const isMobile = window.innerWidth < 767; export const textInputWidget = "text input"; export const textWidget = "text"; export const radioButtonWidget = "radio button"; -export const openInNewTab = (url) => { - window.open(url, "_blank", "noopener,noreferrer"); +export const openInNewTab = (url, target) => { + if (target) { + window.open(url, target, "noopener,noreferrer"); + } else { + window.open(url, "_blank", "noopener,noreferrer"); + } }; export async function fetchSubscription( diff --git a/apps/OpenSign/src/pages/Login.js b/apps/OpenSign/src/pages/Login.js index c5251ed27..9a4d49e75 100644 --- a/apps/OpenSign/src/pages/Login.js +++ b/apps/OpenSign/src/pages/Login.js @@ -1041,10 +1041,15 @@ function Login() { localStorage.setItem("baseUrl", baseUrl); localStorage.setItem("parseAppId", appid); }; + + // `handleSignInWithSSO` is trigger when user click sign in with sso and open sso authorize endpoint const handleSignInWithSSO = () => { if (state?.email) { + const encodedEmail = encodeURIComponent(state.email); + const clientUrl = window.location.origin; 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}` + `https://osl-jacksonv2.vercel.app/api/oauth/authorize?response_type=code&provider=saml&tenant=Okta-dev-nxglabs-in&product=OpenSign&redirect_uri=${clientUrl}/sso&state=${encodedEmail}`, + "_self" ); } else { alert("Please provide email."); diff --git a/apps/OpenSign/src/pages/SSOVerify.js b/apps/OpenSign/src/pages/SSOVerify.js index e811b2f08..988e1e5f8 100644 --- a/apps/OpenSign/src/pages/SSOVerify.js +++ b/apps/OpenSign/src/pages/SSOVerify.js @@ -1,64 +1,143 @@ -// import axios from "axios"; -import React, { useEffect } from "react"; +import React, { useEffect, useState } 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 { + isEnableSubscription, + modalCancelBtnColor, + modalSubmitBtnColor +} from "../constant/const"; import { fetchSubscription } from "../constant/Utils"; import { useDispatch } from "react-redux"; import { showTenant } from "../redux/reducers/ShowTenant"; +import ModalUi from "../primitives/ModalUi"; + const SSOVerify = () => { const location = useLocation(); const navigate = useNavigate(); const dispatch = useDispatch(); + const [isModal, setIsModal] = useState(false); + const [ssoErrMsg, setSsoErrMsg] = useState("Verifying SSO..."); + const [isLoader, setIsLoader] = useState(false); + const [userDetails, setUserDetails] = useState({ + name: "", + email: "", + phone: "", + Destination: "", + Company: "" + }); useEffect(() => { - getAccesstoken(); + linkUserWithSSO(); // eslint-disable-next-line }, []); - const getAccesstoken = async () => { + + // `linkUserWithSSO` is used to sign in or sign up a user using an SSO code and check if the user is present in the extended class + const linkUserWithSSO = async () => { const param = new URLSearchParams(location.search); const code = param?.get("code"); const state = param?.get("state"); - // console.log("code ", code); try { + // The `ssosign` cloud function is used to sign in or sign up a user const ssosign = await Parse.Cloud.run("ssosign", { code: code, email: state }); + localStorage.setItem("accesstoken", ssosign.sessiontoken); + // `checkExtUser` checks if the user is present in the extended class `contracts_Users` and if not, initiates the new user flow await checkExtUser(ssosign); - // console.log("ssores ", ssosign); } catch (err) { - console.log("err", err); + setSsoErrMsg(err.message); + console.log("err", err.message); } }; 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); + try { + // `isextenduser` checks if the current user is present in the extended class + const extRes = await Parse.Cloud.run("isextenduser", params); + if (extRes?.isUserExist) { + // `getUserDetails` retrieves the current user's details from the extended class + const extRes = await Parse.Cloud.run("getUserDetails", params); + 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); + } + } else { + setIsModal(true); + setUserDetails((prev) => ({ + ...prev, + name: ssosign.name, + email: ssosign.email, + phone: ssosign?.phone || "" + })); } - return { msg: "exist" }; - } else { - // setIsModal(true); - // setThirdpartyLoader(false); - console.log("not exist"); - return { msg: "notexist" }; + } catch (err) { + console.log("Err in isextenduser or getuserdetails cloud function", err); + alert(err.message); } }; + // `handleSubmitbtn` is used to create a user in the extended class + const handleSubmitbtn = async (e) => { + e.preventDefault(); + setIsLoader(true); + let phone; + if (userDetails?.phone) { + phone = validateInput(userDetails?.phone); + } else { + phone = true; + } + if (userDetails.Destination && userDetails.Company && phone) { + const payload = { sessionToken: localStorage.getItem("accesstoken") }; + if (payload && payload.sessionToken) { + const params = { + userDetails: { + name: userDetails.name, + email: userDetails.email, + phone: userDetails?.phone || "", + role: "contracts_User", + company: userDetails.Company, + jobTitle: userDetails.Destination + } + }; + try { + const userSignUp = await Parse.Cloud.run("usersignup", params); + if (userSignUp && userSignUp.sessionToken) { + const LocalUserDetails = params.userDetails; + localStorage.setItem( + "userDetails", + JSON.stringify(LocalUserDetails) + ); + await thirdpartyLoginfn(userSignUp.sessionToken); + setIsLoader(false); + } else { + alert(userSignUp.message); + setIsLoader(false); + } + } catch (err) { + console.log("error in usersignup", err); + localStorage.removeItem("accesstoken"); + alert("something went wrong, please try again later."); + setIsLoader(false); + } + } else { + localStorage.removeItem("accesstoken"); + alert("Internal server error !"); + setIsLoader(false); + } + } else { + alert("Please fill required details correctly."); + setIsLoader(false); + } + }; + // `thirdpartyLoginfn` is used to save necessary parameters locally for the logged-in user const thirdpartyLoginfn = async (sessionToken) => { const baseUrl = localStorage.getItem("baseUrl"); const parseAppId = localStorage.getItem("parseAppId"); @@ -67,7 +146,6 @@ const SSOVerify = () => { 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); @@ -77,132 +155,252 @@ const SSOVerify = () => { // 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") { + const userSettings = appInfo.settings; + //Get Current user roles + const 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) { + console.log("user has two roles"); + // _currentRole = filteredRole.filter((x) => x === "contracts_User"); + } else { + _currentRole = filteredRole?.[0] || ""; + } + 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); + 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 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); + 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"); + } catch (err) { + alert("user not exist."); + console.log("err in get extUser", err); } } else { - alert("contracts_User role not exist"); + alert("Role does not exists."); } + } else { + alert("Role does not exists."); } } catch (err) { console.log("err in usergroups", err); } } } catch (err) { - console.log("Err in me call", err); + console.log("Err in become method", err); } }; - return
Verifying SSO....
; + // `handleCloseModal` is triggered when the user wants to close the new user flow modal + const handleCloseModal = () => { + setIsModal(false); + if (Parse?.User?.current()) { + Parse.User.logOut(); + } + }; + + // `validateInput` is used to verify the phone pattern + function validateInput(input) { + if (input) { + const pattern = /^(?!.*\+.*\+)[\d+-]*$/; + return pattern.test(input); + } else { + return true; + } + } + return ( +
+
+ {ssoErrMsg} +
+ +
+ {isLoader && ( +
+
+
+ )} +
+
+ + + setUserDetails({ + ...userDetails, + phone: e.target.value + }) + } + disabled={isLoader} + required + /> +

+ {!validateInput(userDetails.phone) && "Invalid phone value"} +

+
+
+ + + setUserDetails({ + ...userDetails, + Company: e.target.value + }) + } + disabled={isLoader} + required + /> +
+
+ + + setUserDetails({ + ...userDetails, + Destination: e.target.value + }) + } + disabled={isLoader} + required + /> +
+
+ + +
+
+
+
+
+ ); }; export default SSOVerify; diff --git a/apps/OpenSignServer/auth/authadapter.js b/apps/OpenSignServer/auth/authadapter.js index a48b3cb76..528ea8176 100644 --- a/apps/OpenSignServer/auth/authadapter.js +++ b/apps/OpenSignServer/auth/authadapter.js @@ -1,21 +1,22 @@ import axios from 'axios'; - +import dotenv from 'dotenv'; +dotenv.config(); +const ssoApiUrl = process.env.SSO_API_URL || 'https://osl-jacksonv2.vercel.app/api'; 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', { + const response = await axios.get(ssoApiUrl + '/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) { + console.log('error in sso adapter', error?.response); throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'SSO auth is invalid for this user.'); } }, diff --git a/apps/OpenSignServer/cloud/main.js b/apps/OpenSignServer/cloud/main.js index 1bbd9feea..59a903b1b 100644 --- a/apps/OpenSignServer/cloud/main.js +++ b/apps/OpenSignServer/cloud/main.js @@ -43,6 +43,7 @@ import CreatePublicTemplate from './parsefunction/CreatePublicTemplate.js'; import GetPublicUserName from './parsefunction/GetPublicUserName.js'; import GetPublicTemplate from './parsefunction/GetPublicTemplate.js'; import ssoSignin from './parsefunction/ssoSignin.js'; +import isextenduser from './parsefunction/isextenduser.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); @@ -96,3 +97,4 @@ Parse.Cloud.define('createpublictemplate', CreatePublicTemplate); Parse.Cloud.define('getpublicusername', GetPublicUserName); Parse.Cloud.define('getpublictemplate', GetPublicTemplate); Parse.Cloud.define('ssosign', ssoSignin); +Parse.Cloud.define('isextenduser', isextenduser); diff --git a/apps/OpenSignServer/cloud/parsefunction/isextenduser.js b/apps/OpenSignServer/cloud/parsefunction/isextenduser.js new file mode 100644 index 000000000..84111597e --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/isextenduser.js @@ -0,0 +1,30 @@ +/** + * Checks if a user exists in the extended class 'contracts_Users' based on the provided email. + * @param email - The request contains parameters, such as the user's email. + * @returns {Object} - Returns an object indicating whether the user exists. + */ +export default async function isextenduser(request) { + try { + // Query the 'contracts_Users' class in the database based on the provided email + const userQuery = new Parse.Query('contracts_Users'); + userQuery.equalTo('Email', request.params.email); + + // Execute the query + const res = await userQuery.first({ useMasterKey: true }); + + // Check if a user was found + if (res) { + // If user exists, return object with 'isUserExist' set to true + return { isUserExist: true }; + } else { + // If user does not exist, return object with 'isUserExist' set to false + return { isUserExist: false }; + } + } catch (err) { + // Handle errors + console.log('Error in userexist', err); + const code = err?.code || 400; + const message = err?.message || 'Something went wrong.'; + throw new Parse.Error(code, message); + } +} diff --git a/apps/OpenSignServer/cloud/parsefunction/ssoSignin.js b/apps/OpenSignServer/cloud/parsefunction/ssoSignin.js index ca243cd4f..6e50d0639 100644 --- a/apps/OpenSignServer/cloud/parsefunction/ssoSignin.js +++ b/apps/OpenSignServer/cloud/parsefunction/ssoSignin.js @@ -3,39 +3,37 @@ import axios from 'axios'; const serverUrl = process.env.SERVER_URL; const APPID = process.env.APP_ID; const masterKEY = process.env.MASTER_KEY; - +const clientUrl = process.env.PUBLIC_URL; +const ssoApiUrl = process.env.SSO_API_URL || 'https://osl-jacksonv2.vercel.app/api'; /** * 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} + * @returns if success {email, name, phone message, sessiontoken} else on reject error {code, 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', + ssoApiUrl + '/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', + redirect_uri: clientUrl + '/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); + userQuery.equalTo('username', userEmail); const res = await userQuery.first({ useMasterKey: true }); if (res) { try { @@ -51,17 +49,31 @@ export default async function ssoSignin(request) { ); if (SignIn.data) { + const response = await axios.get(ssoApiUrl + '/oauth/userinfo', { + headers: { + Authorization: `Bearer ${ssoAccessToken}`, + }, + }); const sessiontoken = SignIn.data.sessionToken; // console.log('sso sessiontoken', sessiontoken); - return { + const payload = { email: userEmail, + name: response.data?.firstName + ' ' + response.data?.lastName, + phone: response?.data?.phone || '', message: 'User Sign In', sessiontoken: sessiontoken, }; + return payload; } } catch (err) { - console.log('err in user sso sign in', err); - throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Internal server error.'); + const errCode = err?.response?.data?.code || err?.response?.status || err?.code || 400; + const message = + err?.response?.data?.error || + err?.response?.data || + err?.message || + 'Internal server error.'; + console.log('err in user sso sign in', errCode, message); + throw new Parse.Error(errCode, message); } } else { // console.log("in sign up condition"); @@ -79,7 +91,7 @@ export default async function ssoSignin(request) { username: response.data.email, email: response.data.email, phone: response.data?.phone, - name: response.data?.firstName + response.data?.lastName, + name: response.data?.firstName + ' ' + response.data?.lastName, }, { headers: { @@ -88,21 +100,26 @@ export default async function ssoSignin(request) { }, } ); - - // console.log("SignUp", SignUp); - if (SignUp.data) { const sessiontoken = SignUp.data.sessionToken; const payload = { email: userEmail, + name: SignUp?.data?.name, + phone: SignUp?.data?.phone || '', 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.'); + const errCode = err?.response?.data?.code || err?.response?.status || err?.code || 400; + const message = + err?.response?.data?.error || + err?.response?.data || + err?.message || + 'Internal server error.'; + console.log('err in user sso sign up', errCode, message); + throw new Parse.Error(errCode, message); } } } diff --git a/apps/OpenSignServer/cloud/parsefunction/usersignup.js b/apps/OpenSignServer/cloud/parsefunction/usersignup.js index 84a7a101e..2d4338a5f 100644 --- a/apps/OpenSignServer/cloud/parsefunction/usersignup.js +++ b/apps/OpenSignServer/cloud/parsefunction/usersignup.js @@ -132,7 +132,9 @@ export default async function usersignup(request) { newObj.set('JobTitle', userDetails.jobTitle); } const extRes = await newObj.save(null, { useMasterKey: true }); - await saveSubscription(extRes.id, user.id, tenantRes.id, subscription); + if (subscription) { + await saveSubscription(extRes.id, user.id, tenantRes.id, subscription); + } return { message: 'User sign up', sessionToken: user.sessionToken }; } } catch (err) {