UNPKG

@salad-labs/loopz-typescript

Version:
958 lines 52.2 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { Client } from "./core/client"; import { Crypto } from "./core"; import { CLIENT_DB_KEY_LAST_USER_LOGGED } from "./constants/app"; import { getAccessToken } from "@privy-io/react-auth"; import { Account, Chat, Serpens } from "."; /** * Represents an authentication client that interacts with a backend server for user authentication. */ export class Auth { static set authToken(authToken) { Auth._authToken = authToken; Auth._realtimeAuthorizationToken = authToken ? `${Auth._apiKey}##${authToken}` : null; } static set account(account) { Auth._account = account; if (account) Auth._emit("__onAccountReady"); } static get realtimeAuthorizationToken() { return Auth._realtimeAuthorizationToken; } static get apiKey() { return Auth._apiKey; } static get authToken() { return Auth._authToken; } static get account() { return Auth._account; } static get MAX_ATTEMPTS_REALTIME_FETCH_AUTH_TOKEN() { return 2; } constructor() { if (!Auth._config) throw new Error("Auth must be configured before getting the instance"); Auth._storage = Auth._config.storage; Auth._apiKey = Auth._config.apiKey; Auth._client = new Client(Auth._config.devMode); //OAuth providers like Google, Instagram etc bring the user from the current web application page to //their authentication pages. When the user is redirect from their auth pages to the web application page again //this event is fired. this.on("__onOAuthAuthenticatedDesktop", Auth._callBackendAuthAfterOAuthRedirect); // same but for linking an account to a user already registered this.on("__onOAuthLinkAuthenticatedDesktop", Auth._callBackendLinkAfterOAuthRedirect); //OAuth providers login error handling this.on("__onLoginError", (error) => { Auth._emit("onAuthError", error); }); this.on("__onLinkAccountError", (error) => { Auth._emit("onLinkError", error); }); Auth._instance = this; } /** static methods */ static _generateKeys() { return __awaiter(this, void 0, void 0, function* () { try { const keys = yield Crypto.generateKeys("HIGH"); if (!keys) throw new Error("Error during generation of public/private keys."); return keys; } catch (error) { throw error; } }); } static _getOrCreateUserKeys(did, organizationId) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => { Serpens.addAction(() => { Auth._storage.user .where("[did+organizationId]") .equals([did, organizationId]) .first() .then((existingUser) => __awaiter(this, void 0, void 0, function* () { if (existingUser) { resolve({ e2ePublicKeyPem: existingUser.e2ePublicKey, e2eEncryptedPrivateKey: existingUser.e2eEncryptedPrivateKey, e2ePrivateKeyPem: null, }); } else { const keys = yield Auth._generateKeys(); const publicKeyPem = Crypto.convertRSAPublicKeyToPem(keys.publicKey); const privateKeyPem = Crypto.convertRSAPrivateKeyToPem(keys.privateKey); resolve({ e2ePublicKeyPem: publicKeyPem, e2ePrivateKeyPem: privateKeyPem, e2eEncryptedPrivateKey: null, }); } })) .catch(reject); }); }); }); } static _storeUserOnLocalDB(keys) { return __awaiter(this, void 0, void 0, function* () { try { if (!Auth._account) throw new Error("Account is not set"); const existingUser = yield new Promise((resolve, reject) => { Serpens.addAction(() => { if (!Auth._account) throw new Error("Account is not setup correctly."); Auth._storage.user .where("[did+organizationId]") .equals([Auth._account.did, Auth._account.organizationId]) .first() .then(resolve) .catch(reject); }); }); if (existingUser) { if (existingUser.firstLogin) { yield new Promise((resolve, reject) => { Serpens.addAction(() => { if (!Auth._account) throw new Error("Account is not setup correctly."); Auth._storage.user .update(existingUser, { firstLogin: false, }) .then(resolve) .catch(reject); }); }); } return; } //save all the data related to this user into the db yield new Promise((resolve, reject) => { Serpens.addAction(() => { if (!Auth._account) throw new Error("Account is not setup correctly."); Auth._storage.user .add({ did: Auth._account.did, organizationId: Auth._account.organizationId, dynamoDBUserID: Auth._account.dynamoDBUserID, username: Auth._account.username, email: Auth._account.email, bio: Auth._account.bio, instagramPublicUrl: Auth._account.instagramPublicUrl, xPublicUrl: Auth._account.xPublicUrl, tiktokPublicUrl: Auth._account.tiktokPublicUrl, personalWebsiteUrl: Auth._account.personalWebsiteUrl, avatarUrl: Auth._account.avatarUrl, bannerImageUrl: Auth._account.bannerImageUrl, imageSettings: Auth._account.imageSettings ? Auth._account.imageSettings : null, city: Auth._account.city, country: Auth._account.country, lat: Auth._account.lat, lng: Auth._account.lng, isCreator: Auth._account.isCreator, gender: Auth._account.gender, isVerified: Auth._account.isVerified, signupCompleted: Auth._account.signupCompleted, wallet: { address: Auth._account.walletAddress, connectorType: Auth._account.walletConnectorType, imported: Auth._account.walletImported, recoveryMethod: Auth._account.walletRecoveryMethod, clientType: Auth._account.walletClientType, }, apple: Auth._account.appleSubject ? { subject: Auth._account.appleSubject, email: Auth._account.email, } : null, discord: Auth._account.discordSubject ? { subject: Auth._account.discordSubject, email: Auth._account.discordEmail, username: Auth._account.username, } : null, farcaster: Auth._account.farcasterFid ? { fid: Auth._account.farcasterFid, displayName: Auth._account.farcasterDisplayName, ownerAddress: Auth._account.farcasterOwnerAddress, pfp: Auth._account.farcasterPfp, username: Auth._account.farcasterUsername, signerPublicKey: Auth._account.farcasterSignerPublicKey, } : null, github: Auth._account.githubSubject ? { subject: Auth._account.githubSubject, email: Auth._account.githubEmail, name: Auth._account.githubName, username: Auth._account.githubUsername, } : null, google: Auth._account.googleSubject ? { subject: Auth._account.googleSubject, email: Auth._account.googleEmail, name: Auth._account.googleName, } : null, instagram: Auth._account.instagramSubject ? { subject: Auth._account.instagramSubject, username: Auth._account.instagramUsername, } : null, linkedin: Auth._account.linkedinSubject ? { subject: Auth._account.linkedinSubject, email: Auth._account.linkedinEmail, name: Auth._account.linkedinName, vanityName: Auth._account.linkedinVanityName, } : null, spotify: Auth._account.spotifySubject ? { subject: Auth._account.spotifySubject, email: Auth._account.spotifyEmail, name: Auth._account.spotifyName, } : null, telegram: Auth._account.telegramUserId ? { firstName: Auth._account.telegramFirstName, lastName: Auth._account.telegramLastName, photoUrl: Auth._account.telegramPhotoUrl, userId: Auth._account.telegramUserId, username: Auth._account.telegramUsername, } : null, tiktok: Auth._account.tiktokSubject ? { name: Auth._account.tiktokName, subject: Auth._account.tiktokSubject, username: Auth._account.tiktokUsername, } : null, twitter: Auth._account.twitterSubject ? { name: Auth._account.twitterName, subject: Auth._account.twitterSubject, profilePictureUrl: Auth._account.twitterProfilePictureUrl, username: Auth._account.twitterUsername, } : null, firstLogin: Auth._account.firstLogin, proposalNotificationPush: Auth._account.proposalNotificationPush, proposalNotificationSystem: Auth._account.proposalNotificationSystem, orderNotificationPush: Auth._account.orderNotificationPush, orderNotificationSystem: Auth._account.orderNotificationSystem, followNotificationPush: Auth._account.followNotificationPush, followNotificationSystem: Auth._account.followNotificationSystem, collectionNotificationPush: Auth._account.collectionNotificationPush, collectionNotificationSystem: Auth._account.collectionNotificationSystem, generalNotificationPush: Auth._account.generalNotificationPush, generalNotificationSystem: Auth._account.generalNotificationSystem, accountSuspended: Auth._account.accountSuspended, allowNotification: Auth._account.allowNotification, allowNotificationSound: Auth._account.allowNotificationSound, visibility: Auth._account.visibility, onlineStatus: Auth._account.onlineStatus, allowReadReceipt: Auth._account.allowReadReceipt, allowReceiveMessageFrom: Auth._account.allowReceiveMessageFrom, allowAddToGroupsFrom: Auth._account.allowAddToGroupsFrom, allowGroupsSuggestion: Auth._account.allowGroupsSuggestion, e2ePublicKey: keys.e2ePublicKeyPem, e2eEncryptedPrivateKey: keys.e2eEncryptedPrivateKey ? keys.e2eEncryptedPrivateKey : "", createdAt: Auth._account.createdAt, updatedAt: Auth._account.updatedAt, lastSyncAt: null, }) .then(resolve) .catch(reject); }); }); } catch (error) { console.error(error); throw new Error("Error during setup of the local keys. Check the console to have more information."); } }); } static _callBackendAuthAfterOAuthRedirect(authInfo) { return __awaiter(this, void 0, void 0, function* () { if (!Auth._config || !Auth._instance || !Auth._client) throw new Error("Auth has not been configured"); Auth.authToken = authInfo.authToken; try { const keys = yield Auth._getOrCreateUserKeys(authInfo.user.id, Auth._apiKey); const { response } = yield Auth._client.fetch(Auth._client.backendUrl("/auth"), { method: "POST", body: Object.assign(Object.assign({}, Auth._formatAuthParams(authInfo)), { e2ePublicKey: keys.e2ePublicKeyPem }), }); if (!response || !response.data) return Auth._emit("onAuthError", new Error("No response from backend during authentication.")); const { user } = response.data[0]; if (!user) return Auth._emit("onAuthError", new Error("Access not granted.")); //let's check if it's the first login of the user. //this is needed to understand if the user is doing the login on a different device //this is needed to understand if the user is doing the login on a different device if (user.e2ePublicKey.toLowerCase() === keys.e2ePublicKeyPem.toLowerCase()) { //this means the user is accessing from the same device/the devices it owns share the same keys OR he's/she's doing the signup if (keys.e2eEncryptedPrivateKey) { //it means i've recovered the private key from the local database. So the user has the possibility to have chats Chat.getInstance().setCanChat(true); //so now we have the following situation: //keys.e2eEncryptedPrivateKey = string //keys.e2ePrivateKeyPem = string //keys.e2ePublicKeyPem = string } else { //in the case of a signup, i generate now the private key so the user can chat Chat.getInstance().setCanChat(true); const encryptedPrivateKey = Crypto.encryptAES_CBC(keys.e2ePrivateKeyPem, Buffer.from(user.e2eSecret, "hex").toString("base64"), Buffer.from(user.e2eSecretIV, "hex").toString("base64")); keys.e2eEncryptedPrivateKey = encryptedPrivateKey; //so now we have the following situation: //keys.e2eEncryptedPrivateKey = string //keys.e2ePrivateKeyPem = string //keys.e2ePublicKeyPem = string } } else { //this means the user is accessing from another device, so the keys needs to be overwritten Chat.getInstance().setCanChat(false); keys.e2ePublicKeyPem = user.e2ePublicKey; keys.e2ePrivateKeyPem = null; //so now we have the following situation: //keys.e2eEncryptedPrivateKey = null //keys.e2ePrivateKeyPem = null //keys.e2ePublicKeyPem = string } Auth._isAuthenticated = true; Auth.account = new Account(Object.assign({ enableDevMode: Auth._config.devMode, storage: Auth._config.storage }, user)); //store the key of the last user logged in the local storage, this allow to recover the user and rebuild the account object when //the user refresh the page Auth._account.storeLastUserLoggedKey(); //generation of the table and local keys for e2e encryption yield Auth._storeUserOnLocalDB(keys); //clear all the internal callbacks connected to the authentication... Auth._clearEventsCallbacks([ "__onOAuthAuthenticatedDesktop", "__onLoginError", ]); } catch (error) { //clear all the internal callbacks connected to the authentication... this._clearEventsCallbacks([ "__onOAuthAuthenticatedDesktop", "__onLoginError", ]); yield Auth._instance.logout(); Auth._emit("onAuthError", error); } }); } static _callBackendAuth(authInfo) { return __awaiter(this, void 0, void 0, function* () { if (!Auth._config || !Auth._instance || !Auth._client) throw new Error("Auth has not been configured"); Auth.authToken = authInfo.authToken; try { const keys = yield Auth._getOrCreateUserKeys(authInfo.user.id, Auth._apiKey); const { response } = yield Auth._client.fetch(Auth._client.backendUrl("/auth"), { method: "POST", body: Object.assign(Object.assign({}, Auth._formatAuthParams(authInfo)), { e2ePublicKey: keys.e2ePublicKeyPem }), }); if (!response || !response.data) throw new Error("Invalid response."); const { user } = response.data[0]; if (!user) throw new Error("Access not granted."); //this is needed to understand if the user is doing the login on a different device if (user.e2ePublicKey.toLowerCase() === keys.e2ePublicKeyPem.toLowerCase()) { //this means the user is accessing from the same device/the devices it owns share the same keys OR he's/she's doing the signup if (keys.e2eEncryptedPrivateKey) { //it means i've recovered the private key from the local database. So the user has the possibility to chat Chat.getInstance().setCanChat(true); //so now we have the following situation: //keys.e2eEncryptedPrivateKey = string //keys.e2ePrivateKeyPem = string //keys.e2ePublicKeyPem = string } else { //in the case of a signup, i generate now the private key so the user can chat Chat.getInstance().setCanChat(true); const encryptedPrivateKey = Crypto.encryptAES_CBC(keys.e2ePrivateKeyPem, Buffer.from(user.e2eSecret, "hex").toString("base64"), Buffer.from(user.e2eSecretIV, "hex").toString("base64")); keys.e2eEncryptedPrivateKey = encryptedPrivateKey; //so now we have the following situation: //keys.e2eEncryptedPrivateKey = string //keys.e2ePrivateKeyPem = string //keys.e2ePublicKeyPem = string } } else { //this means the user is accessing from another device, so the keys need to be overwritten Chat.getInstance().setCanChat(false); keys.e2ePublicKeyPem = user.e2ePublicKey; keys.e2ePrivateKeyPem = null; //so now we have the following situation: //keys.e2eEncryptedPrivateKey = null //keys.e2ePrivateKeyPem = null //keys.e2ePublicKeyPem = string } Auth._isAuthenticated = true; Auth._authInfo = Object.assign({ isConnected: true }, authInfo); Auth.account = new Account(Object.assign({ enableDevMode: Auth._config.devMode, storage: Auth._config.storage }, user)); //store the key of the last user logged in the local storage, this allow to recover the user and rebuild the account object when //the user refresh the page Auth._account.storeLastUserLoggedKey(); //generation of the record in user table and local keys for e2e encryption yield Auth._storeUserOnLocalDB(keys); //clear all the internal callbacks connected to the authentication... Auth._clearEventsCallbacks(["__onLoginComplete", "__onLoginError"]); return { auth: Auth._authInfo, account: Auth._account, }; } catch (error) { console.error(error); //clear all the internal callbacks connected to the authentication... Auth._clearEventsCallbacks(["__onLoginComplete", "__onLoginError"]); yield Auth._instance.logout(); Auth._emit("onAuthError", error); throw error; } }); } static _callBackendLinkAfterOAuthRedirect(authInfo) { return __awaiter(this, void 0, void 0, function* () { if (!Auth._config || !Auth._instance || !Auth._client) throw new Error("Auth has not been configured"); Auth.authToken = authInfo.authToken; try { const { response } = yield Auth._client.fetch(Auth._client.backendUrl("/user/link/account"), { method: "POST", body: Object.assign({}, Auth._formatAuthParams(authInfo)), }); if (!response || !response.data) throw new Error("Invalid response."); const { link } = response.data[0]; const { status } = link; if (!link || !status) throw new Error("An error occured while updating the account."); //clear all the internal callbacks connected to the authentication... Auth._clearEventsCallbacks([ "__onOAuthLinkAuthenticatedDesktop", "__onLinkAccountError", ]); Auth._emit("link", authInfo); } catch (error) { console.error(error); if ("statusCode" in error && error.statusCode === 401) { Auth._clearEventsCallbacks([ "__onOAuthLinkAuthenticatedDesktop", "__onLinkAccountError", "__onLoginComplete", "__onLoginError", ]); Auth._emit("onLinkError", error); yield Auth._instance.logout(); Auth._emit("onAuthError", error); } else { Auth._clearEventsCallbacks([ "__onOAuthLinkAuthenticatedDesktop", "__onLinkAccountError", ]); Auth._emit("onLinkError", error); } } }); } static _callBackendLink(authInfo) { return __awaiter(this, void 0, void 0, function* () { if (!Auth._config || !Auth._instance || !Auth._client) throw new Error("Auth has not been configured"); Auth.authToken = authInfo.authToken; try { const { response } = yield Auth._client.fetch(Auth._client.backendUrl("/user/link/account"), { method: "POST", body: Object.assign({}, Auth._formatAuthParams(authInfo)), }); if (!response || !response.data) throw new Error("Invalid response."); const { link } = response.data[0]; const { status } = link; if (!link || !status) throw new Error("An error occured while updating the account."); //clear all the internal callbacks connected to the link... Auth._clearEventsCallbacks([ "__onLinkAccountComplete", "__onLinkAccountError", ]); return authInfo; } catch (error) { if ("statusCode" in error && error.statusCode === 401) { Auth._clearEventsCallbacks([ "__onLinkAccountComplete", "__onLinkAccountError", "__onLoginComplete", "__onLoginError", ]); yield Auth._instance.logout(); Auth._emit("onAuthError", error); } else { Auth._clearEventsCallbacks([ "__onLinkAccountComplete", "__onLinkAccountError", ]); } throw error; } }); } static _formatAuthParams(authInfo) { return { did: authInfo.user.id, walletAddress: authInfo.user.wallet.address, walletConnectorType: authInfo.user.wallet.connectorType, walletImported: authInfo.user.wallet.imported ? authInfo.user.wallet.imported : false, walletRecoveryMethod: authInfo.user.wallet.recoveryMethod ? authInfo.user.wallet.recoveryMethod : "", walletClientType: authInfo.user.wallet.walletClientType ? authInfo.user.wallet.walletClientType : "", appleSubject: authInfo.user.apple ? authInfo.user.apple.subject : null, appleEmail: authInfo.user.apple ? authInfo.user.apple.email : null, discordSubject: authInfo.user.discord ? authInfo.user.discord.subject : null, discordEmail: authInfo.user.discord ? authInfo.user.discord.email : null, discordUsername: authInfo.user.discord ? authInfo.user.discord.username : null, farcasterFid: authInfo.user.farcaster ? authInfo.user.farcaster.fid : null, farcasterDisplayName: authInfo.user.farcaster ? authInfo.user.farcaster.displayName : null, farcasterOwnerAddress: authInfo.user.farcaster ? authInfo.user.farcaster.ownerAddress : null, farcasterPfp: authInfo.user.farcaster ? authInfo.user.farcaster.pfp : null, farcasterSignerPublicKey: authInfo.user.farcaster ? authInfo.user.farcaster.signerPublicKey : null, farcasterUrl: authInfo.user.farcaster ? authInfo.user.farcaster.url : null, farcasterUsername: authInfo.user.farcaster ? authInfo.user.farcaster.username : null, githubSubject: authInfo.user.github ? authInfo.user.github.subject : null, githubEmail: authInfo.user.github ? authInfo.user.github.email : null, githubName: authInfo.user.github ? authInfo.user.github.name : null, githubUsername: authInfo.user.github ? authInfo.user.github.username : null, googleEmail: authInfo.user.google ? authInfo.user.google.email : null, googleName: authInfo.user.google ? authInfo.user.google.name : null, googleSubject: authInfo.user.google ? authInfo.user.google.subject : null, instagramSubject: authInfo.user.instagram ? authInfo.user.instagram.subject : null, instagramUsername: authInfo.user.instagram ? authInfo.user.instagram.username : null, linkedinEmail: authInfo.user.linkedin ? authInfo.user.linkedin.email : null, linkedinName: authInfo.user.linkedin ? authInfo.user.linkedin.name : null, linkedinSubject: authInfo.user.linkedin ? authInfo.user.linkedin.subject : null, linkedinVanityName: authInfo.user.linkedin ? authInfo.user.linkedin.vanityName : null, spotifyEmail: authInfo.user.spotify ? authInfo.user.spotify.email : null, spotifyName: authInfo.user.spotify ? authInfo.user.spotify.name : null, spotifySubject: authInfo.user.spotify ? authInfo.user.spotify.subject : null, telegramFirstName: authInfo.user.telegram ? authInfo.user.telegram.firstName : null, telegramLastName: authInfo.user.telegram ? authInfo.user.telegram.lastName : null, telegramPhotoUrl: authInfo.user.telegram ? authInfo.user.telegram.photoUrl : null, telegramUserId: authInfo.user.telegram ? authInfo.user.telegram.telegramUserId : null, telegramUsername: authInfo.user.telegram ? authInfo.user.telegram.username : null, tiktokName: authInfo.user.tiktok ? authInfo.user.tiktok.name : null, tiktokSubject: authInfo.user.tiktok ? authInfo.user.tiktok.subject : null, tiktokUsername: authInfo.user.tiktok ? authInfo.user.tiktok.username : null, twitterName: authInfo.user.twitter ? authInfo.user.twitter.name : null, twitterSubject: authInfo.user.twitter ? authInfo.user.twitter.subject : null, twitterProfilePictureUrl: authInfo.user.twitter ? authInfo.user.twitter.profilePictureUrl : null, twitterUsername: authInfo.user.twitter ? authInfo.user.twitter.username : null, phone: authInfo.user.phone ? authInfo.user.phone.number : null, email: authInfo.user.email ? authInfo.user.email.address : null, }; } static _clearEventsCallbacks(events) { for (const event of events) { const index = Auth._eventsCallbacks.findIndex((item) => item.event === event); if (index < 0) return; Auth._eventsCallbacks[index].callbacks = []; } } static _handleDesktopAuthentication() { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => { if (!Auth._config || !Auth._instance) throw new Error("Auth has not been configured"); Auth._instance.on("__onLoginComplete", (authInfo) => { Auth._callBackendAuth(authInfo).then(resolve).catch(reject); }); Auth._instance.on("__onLoginError", reject); Auth._emit("__authenticate"); }); }); } static _handleDesktopLink(method) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => { if (!Auth._config || !Auth._instance) throw new Error("Auth has not been configured"); Auth._instance.on("__onLinkAccountComplete", (authInfo) => { Auth._callBackendLink(authInfo).then(resolve).catch(reject); }); Auth._instance.on("__onLinkAccountError", reject); Auth._emit("__link", method); }); }); } static _emit(event, params) { const index = Auth._eventsCallbacks.findIndex((item) => item.event === event); if (index < 0) return; Auth._eventsCallbacks[index].callbacks.forEach((callback) => callback(params)); } static config(config) { if (Auth._config) throw new Error("Auth already configured"); Auth._config = config; } static getInstance() { var _a; return (_a = Auth._instance) !== null && _a !== void 0 ? _a : new Auth(); } /** public instance methods */ /** * Add a new event and the associated callback. * @param event - The event to listen. * @param callback - The callback related to this event. * @param onlyOnce - An optional flag, it allows the adding of only one callback associated to this event. */ on(event, callback, onlyOnce) { const index = Auth._eventsCallbacks.findIndex((item) => item.event === event); if (index < 0) Auth._eventsCallbacks.push({ event, callbacks: [callback], }); else { // this is wrong, if i flag onlyOnce: true i still want my callback to be executed, just once tho if (onlyOnce) return; Auth._eventsCallbacks[index].callbacks.push(callback); } } /** * Remove an event and the associated callback or all the callbacks associated to that event. * @param event - The event to unlisten. * @param callback - The callback related to this event. * @returns None */ off(event, callback) { const index = Auth._eventsCallbacks.findIndex((item) => { return item.event === event; }); if (index < 0) return; Auth._eventsCallbacks[index].callbacks = callback ? Auth._eventsCallbacks[index].callbacks.filter((cb) => cb !== callback) : []; } authenticate() { return Auth._handleDesktopAuthentication(); } sendEmailOTPCode(email) { return new Promise((resolve, reject) => { this.on("__onEmailOTPCodeSent", (email) => resolve({ email })); this.on("__onEmailOTPCodeSentError", reject); Auth._emit("__sendEmailOTPCode", email); }); } sendPhoneOTPCode(phone) { return new Promise((resolve, reject) => { this.on("__onSMSOTPCodeSent", (phone) => resolve({ phone })); this.on("__onSMSOTPCodeSentError", reject); Auth._emit("__sendSMSOTPCode", phone); }); } sendEmailOTPCodeAfterAuth(email) { return new Promise((resolve, reject) => { this.on("__onEmailOTPCodeAfterAuthSent", (email) => resolve({ email })); this.on("__onEmailOTPCodeAfterAuthSentError", reject); Auth._emit("__sendEmailOTPCodeAfterAuth", email); }); } sendPhoneOTPCodeAfterAuth(phone) { return new Promise((resolve, reject) => { this.on("__onSMSOTPCodeAfterAuthSent", (phone) => resolve({ phone })); this.on("__onSMSOTPCodeSentAfterAuthError", reject); Auth._emit("__sendSMSOTPCodeAfterAuth", phone); }); } /** * call this if you want to auth the user automatically without the need of a button to authenticate. * * e.g. auth.ready().then(() => auth.authenticate()) */ ready() { return new Promise((resolve) => this.on("__onPrivyReady", () => resolve(true))); } logout() { var _a, _b; Auth._isAuthenticated = false; Auth._clearEventsCallbacks(["__onLoginComplete", "__onLoginError"]); (_a = Auth._account) === null || _a === void 0 ? void 0 : _a.destroyLastUserLoggedKey(); (_b = Auth._account) === null || _b === void 0 ? void 0 : _b.emptyActiveWallets(); Auth._account = null; Auth.authToken = null; Chat.getInstance().disconnect(); Auth._emit("__logout"); //tells to Privy to logout Auth._emit("logout"); //tells to Loopz listeners to logout } isAuthenticated() { return Auth._isAuthenticated; } getAuthInfo() { return Auth._authInfo; } getCurrentAccount() { return Auth.account; } link(method) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => { Auth._handleDesktopLink(method).then(resolve).catch(reject); }); }); } unlink(method) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => { this.on("__onUnlinkAccountComplete", (status) => { // TODO aggiungere await per chiamata lato server per aggiornare backend resolve(status); }); this.on("__onUnlinkAccountError", reject); Auth._emit("__unlink", method); }); }); } static recoverAccountFromLocalDB() { return __awaiter(this, void 0, void 0, function* () { if (!Auth._config || !Auth._instance || !Auth._client) throw new Error("Auth has not been configured"); if (Auth._account) return; const lastUserLoggedKey = window.localStorage.getItem(CLIENT_DB_KEY_LAST_USER_LOGGED); if (!lastUserLoggedKey) return; try { const { did, organizationId, token } = JSON.parse(lastUserLoggedKey); const user = yield Auth._storage.get("user", "[did+organizationId]", [ did, organizationId, ]); Chat.getInstance().setCanChat(user.e2ePublicKey && user.e2ePublicKey.length > 0 && user.e2eEncryptedPrivateKey && user.e2eEncryptedPrivateKey.length > 0); Auth.authToken = token; const { response } = yield Auth._client.fetch(Auth._client.backendUrl("/user/secrets")); if (!response || !response.data || typeof response.data[0] === "undefined") return; const { secrets } = response.data[0]; if (!secrets) return; const { e2eSecret, e2eSecretIV } = secrets; Auth._isAuthenticated = true; Auth.account = new Account({ enableDevMode: Auth._config.devMode, storage: Auth._config.storage, did: user.did, organizationId: user.organizationId, token: Auth.authToken, //we use Auth.authToken and not token because in case we have a 401 error, variable 'token' has the value of the previous token. Instead Auth.authToken has the updated value. walletAddress: user.wallet.address, walletConnectorType: user.wallet.connectorType, walletImported: user.wallet.imported, walletRecoveryMethod: user.wallet.recoveryMethod, walletClientType: user.wallet.clientType, appleSubject: user.apple ? user.apple.subject : null, appleEmail: user.apple ? user.apple.email : null, discordSubject: user.discord ? user.discord.subject : null, discordEmail: user.discord ? user.discord.email : null, discordUsername: user.discord ? user.discord.username : null, farcasterFid: user.farcaster ? user.farcaster.fid : null, farcasterDisplayName: user.farcaster ? user.farcaster.displayName : null, farcasterOwnerAddress: user.farcaster ? user.farcaster.ownerAddress : null, farcasterPfp: user.farcaster ? user.farcaster.pfp : null, farcasterSignerPublicKey: user.farcaster ? user.farcaster.signerPublicKey : null, farcasterUrl: user.farcaster ? user.farcaster.url : null, farcasterUsername: user.farcaster ? user.farcaster.username : null, githubSubject: user.github ? user.github.subject : null, githubEmail: user.github ? user.github.email : null, githubName: user.github ? user.github.name : null, githubUsername: user.github ? user.github.username : null, googleEmail: user.google ? user.google.email : null, googleName: user.github ? user.google.name : null, googleSubject: user.github ? user.google.subject : null, instagramSubject: user.instagram ? user.instagram.subject : null, instagramUsername: user.instagram ? user.instagram.username : null, linkedinEmail: user.linkedin ? user.linkedin.email : null, linkedinName: user.linkedin ? user.linkedin.name : null, linkedinSubject: user.linkedin ? user.linkedin.subject : null, linkedinVanityName: user.linkedin ? user.linkedin.vanityName : null, spotifyEmail: user.spotify ? user.spotify.email : null, spotifyName: user.spotify ? user.spotify.name : null, spotifySubject: user.spotify ? user.spotifySubject : null, telegramFirstName: user.telegram ? user.telegram.firstName : null, telegramLastName: user.telegram ? user.telegram.lastName : null, telegramPhotoUrl: user.telegram ? user.telegram.photoUrl : null, telegramUserId: user.telegram ? user.telegram.userId : null, telegramUsername: user.telegram ? user.telegram.username : null, tiktokName: user.tiktok ? user.tiktok.name : null, tiktokSubject: user.tiktok ? user.tiktok.subject : null, tiktokUsername: user.tiktok ? user.tiktok.username : null, twitterName: user.twitter ? user.twitter.name : null, twitterSubject: user.twitter ? user.twitter.subject : null, twitterProfilePictureUrl: user.twitter ? user.twitter.profilePictureUrl : null, twitterUsername: user.twitter ? user.twitter.username : null, dynamoDBUserID: user.dynamoDBUserID, username: user.username, email: user.email, bio: user.bio, instagramPublicUrl: user.instagramPublicUrl, xPublicUrl: user.xPublicUrl, tiktokPublicUrl: user.tiktokPublicUrl, personalWebsiteUrl: user.personalWebsiteUrl, firstLogin: user.firstLogin, avatarUrl: user.avatarUrl, bannerImageUrl: user.bannerImageUrl, imageSettings: user.imageSettings, city: user.city, country: user.country, lat: user.lat, lng: user.lng, isCreator: user.isCreator, gender: user.gender, phone: user.phone ? user.phone : null, isVerified: user.isVerified, signupCompleted: user.signupCompleted, isPfpNft: user.isPfpNft, pfp: user.pfp ? user.pfp : null, proposalNotificationPush: user.proposalNotificationPush, proposalNotificationSystem: user.proposalNotificationSystem, orderNotificationPush: user.orderNotificationPush, orderNotificationSystem: user.orderNotificationSystem, followNotificationPush: user.followNotificationPush, followNotificationSystem: user.followNotificationSystem, collectionNotificationPush: user.collectionNotificationPush, collectionNotificationSystem: user.collectionNotificationSystem, generalNotificationPush: user.generalNotificationPush, generalNotificationSystem: user.generalNotificationSystem, accountSuspended: user.accountSuspended, e2ePublicKey: user.e2ePublicKey, e2eSecret, //this info comes from the backend. This data is never stored in the local DB, it exists only in memory to make harder the access to it e2eSecretIV, //this info comes from the backend. This data is never stored in the local DB, it exists only in memory to make harder the access to it createdAt: user.createdAt, updatedAt: user.updatedAt ? user.updatedAt : null, deletedAt: user.deletedAt ? user.deletedAt : null, allowNotification: user.allowNotification, allowNotificationSound: user.allowNotificationSound, visibility: user.visibility, onlineStatus: user.onlineStatus, allowReadReceipt: user.allowReadReceipt, allowReceiveMessageFrom: user.allowReceiveMessageFrom, allowAddToGroupsFrom: user.allowAddToGroupsFrom, allowGroupsSuggestion: user.allowGroupsSuggestion, }); } catch (error) { console.log("Error during rebuilding phase for account."); console.error(error); if ("statusCode" in error && error.statusCode === 401) yield Auth._instance.logout();