Feat: create SSO user account on platform

This commit is contained in:
prafull-opensignlabs
2024-05-31 18:22:16 +05:30
parent bc5c8cac33
commit afb4de9d0c
9 changed files with 423 additions and 160 deletions
+5 -1
View File
@@ -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"
+6 -2
View File
@@ -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(
+6 -1
View File
@@ -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.");
+332 -134
View File
@@ -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 <div>Verifying SSO....</div>;
// `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 (
<div>
<div className="w-full h-screen flex justify-center items-center text-sm md:text-xl ">
{ssoErrMsg}
</div>
<ModalUi isOpen={isModal} title="Additional Info" showClose={false}>
<div className="relative">
{isLoader && (
<div className="absolute w-full h-full bg-black bg-opacity-25 flex justify-center items-center">
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
)}
<form className="px-4 py-3" onSubmit={handleSubmitbtn}>
<div className="mb-3">
<label
htmlFor="Company"
style={{ display: "flex" }}
className="block text-xs text-gray-700 font-semibold"
>
Phone <span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="tel"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
id="Phone"
value={userDetails.phone}
onChange={(e) =>
setUserDetails({
...userDetails,
phone: e.target.value
})
}
disabled={isLoader}
required
/>
<p className="text-[10px] text-[red] ml-2">
{!validateInput(userDetails.phone) && "Invalid phone value"}
</p>
</div>
<div className="mb-3">
<label
htmlFor="Company"
style={{ display: "flex" }}
className="block text-xs text-gray-700 font-semibold"
>
Company <span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="text"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
id="Company"
value={userDetails.Company}
onChange={(e) =>
setUserDetails({
...userDetails,
Company: e.target.value
})
}
disabled={isLoader}
required
/>
</div>
<div className="mb-3">
<label
htmlFor="JobTitle"
style={{ display: "flex" }}
className="block text-xs text-gray-700 font-semibold"
>
Job Title
<span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="text"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
id="JobTitle"
value={userDetails.Destination}
onChange={(e) =>
setUserDetails({
...userDetails,
Destination: e.target.value
})
}
disabled={isLoader}
required
/>
</div>
<div className="mt-4">
<button
type="submit"
className="px-3 py-1.5 text-white rounded shadow-md text-center focus:outline-none "
style={{
marginRight: 10,
backgroundColor: modalSubmitBtnColor
}}
>
Login
</button>
<button
type="button"
className="py-1.5 text-black border-[1px] border-[#ccc] shadow-md rounded focus:outline-none"
onClick={handleCloseModal}
style={{ width: 75, backgroundColor: modalCancelBtnColor }}
>
Cancel
</button>
</div>
</form>
</div>
</ModalUi>
</div>
);
};
export default SSOVerify;
+5 -4
View File
@@ -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.');
}
},
+2
View File
@@ -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);
@@ -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);
}
}
@@ -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);
}
}
}
@@ -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) {