UNPKG

@toruslabs/customauth

Version:

CustomAuth login with torus to get user private key

372 lines (368 loc) 12 kB
'use strict'; var _objectSpread = require('@babel/runtime/helpers/objectSpread2'); var _objectWithoutProperties = require('@babel/runtime/helpers/objectWithoutProperties'); var _defineProperty = require('@babel/runtime/helpers/defineProperty'); var fetchNodeDetails = require('@toruslabs/fetch-node-details'); var torus_js = require('@toruslabs/torus.js'); var HandlerFactory = require('./handlers/HandlerFactory.js'); var registerServiceWorker = require('./registerServiceWorker.js'); var sentry = require('./sentry.js'); var enums = require('./utils/enums.js'); var error = require('./utils/error.js'); var helpers = require('./utils/helpers.js'); var loglevel = require('./utils/loglevel.js'); var StorageHelper = require('./utils/StorageHelper.js'); const _excluded = ["access_token", "id_token", "tgAuthResult"], _excluded2 = ["args"]; class CustomAuth { constructor({ baseUrl, network, enableLogging = false, redirectToOpener = false, redirectPathName = "redirect", apiKey = "torus-default", uxMode = enums.UX_MODE.POPUP, locationReplaceOnRedirect = false, popupFeatures, storageServerUrl = "https://session.web3auth.io", sentry: sentry$1, enableOneKey = false, web3AuthClientId, useDkg, metadataUrl = "https://metadata.tor.us", keyType = "secp256k1", serverTimeOffset = 0, nodeDetails, checkCommitment = true }) { _defineProperty(this, "isInitialized", void 0); _defineProperty(this, "config", void 0); _defineProperty(this, "torus", void 0); _defineProperty(this, "nodeDetailManager", void 0); _defineProperty(this, "storageHelper", void 0); _defineProperty(this, "sentryHandler", void 0); if (!web3AuthClientId) throw new Error("Please provide a valid web3AuthClientId in constructor"); if (!network) throw new Error("Please provide a valid network in constructor"); this.isInitialized = false; const baseUri = new URL(baseUrl); this.config = { baseUrl: helpers.padUrlString(baseUri), get redirect_uri() { return `${this.baseUrl}${redirectPathName}`; }, redirectToOpener, uxMode, locationReplaceOnRedirect, popupFeatures, useDkg, web3AuthClientId, web3AuthNetwork: network, keyType, nodeDetails, checkCommitment }; const torus = new torus_js.Torus({ network, enableOneKey, serverTimeOffset, clientId: web3AuthClientId, legacyMetadataHost: metadataUrl, keyType }); torus_js.Torus.setAPIKey(apiKey); this.torus = torus; this.nodeDetailManager = new fetchNodeDetails.NodeDetailManager({ network }); if (enableLogging) loglevel.enableAll();else loglevel.disableAll(); this.storageHelper = new StorageHelper.StorageHelper(storageServerUrl); this.sentryHandler = new sentry(sentry$1); } async init({ skipSw = false, skipInit = false, skipPrefetch = false } = {}) { this.storageHelper.init(); if (skipInit) { this.isInitialized = true; return; } if (!skipSw) { const fetchSwResponse = await fetch(`${this.config.baseUrl}sw.js`, { cache: "reload" }); if (fetchSwResponse.ok) { try { await registerServiceWorker.registerServiceWorker(this.config.baseUrl); this.isInitialized = true; return; } catch (error) { loglevel.warn(error); } } else { throw new Error("Service worker is not being served. Please serve it"); } } if (!skipPrefetch) { // Skip the redirect check for firefox if (helpers.isFirefox()) { this.isInitialized = true; return; } await this.handlePrefetchRedirectUri(); return; } this.isInitialized = true; } async triggerLogin(args) { const { authConnectionId, authConnection, clientId, jwtParams, hash, queryParameters, customState, groupedAuthConnectionId } = args; if (!this.isInitialized) { throw new Error("Not initialized yet"); } const loginHandler = HandlerFactory.createHandler({ authConnection, clientId, authConnectionId, groupedAuthConnectionId, redirect_uri: this.config.redirect_uri, redirectToOpener: this.config.redirectToOpener, jwtParams, uxMode: this.config.uxMode, customState, web3AuthClientId: this.config.web3AuthClientId, web3AuthNetwork: this.config.web3AuthNetwork }); let loginParams; if (hash && queryParameters) { const { error, hashParameters, instanceParameters } = helpers.handleRedirectParameters(hash, queryParameters); if (error) throw new Error(error); const { access_token: accessToken, id_token: idToken, tgAuthResult } = hashParameters, rest = _objectWithoutProperties(hashParameters, _excluded); // State has to be last here otherwise it will be overwritten loginParams = _objectSpread(_objectSpread({ accessToken, idToken: idToken || tgAuthResult || "" }, rest), {}, { state: instanceParameters }); } else { this.storageHelper.clearOrphanedData(`torus_login_`); if (this.config.uxMode === enums.UX_MODE.REDIRECT) { await this.storageHelper.storeData(`torus_login_${loginHandler.nonce}`, { args }); } loginParams = await loginHandler.handleLoginWindow({ locationReplaceOnRedirect: this.config.locationReplaceOnRedirect, popupFeatures: this.config.popupFeatures }); if (this.config.uxMode === enums.UX_MODE.REDIRECT) return null; } const userInfo = await loginHandler.getUserInfo(loginParams); const torusKey = await this.getTorusKey({ authConnectionId, userId: userInfo.userId, idToken: loginParams.idToken || loginParams.accessToken, additionalParams: userInfo.extraConnectionParams, groupedAuthConnectionId }); return _objectSpread(_objectSpread({}, torusKey), {}, { userInfo: _objectSpread(_objectSpread({}, userInfo), loginParams) }); } async getTorusKey(params) { const { authConnectionId, userId, idToken, additionalParams, groupedAuthConnectionId } = params; const verifier = groupedAuthConnectionId || authConnectionId; const verifierId = userId; const verifierParams = { verifier_id: userId }; let aggregateIdToken = ""; const finalIdToken = idToken; if (groupedAuthConnectionId) { verifierParams["verify_params"] = [{ verifier_id: userId, idtoken: finalIdToken }]; verifierParams["sub_verifier_ids"] = [authConnectionId]; aggregateIdToken = torus_js.keccak256(Buffer.from(finalIdToken, "utf8")).slice(2); } const nodeDetails = await this.sentryHandler.startSpan({ name: enums.SENTRY_TXNS.FETCH_NODE_DETAILS }, async () => { if (this.config.nodeDetails) { return this.config.nodeDetails; } return this.nodeDetailManager.getNodeDetails({ verifier, verifierId }); }); loglevel.debug("torus-direct/getTorusKey", { torusNodeEndpoints: nodeDetails.torusNodeEndpoints }); const sharesResponse = await this.sentryHandler.startSpan({ name: enums.SENTRY_TXNS.FETCH_SHARES }, async () => { return this.torus.retrieveShares({ endpoints: nodeDetails.torusNodeEndpoints, indexes: nodeDetails.torusIndexes, verifier, verifierParams, idToken: aggregateIdToken || finalIdToken, nodePubkeys: nodeDetails.torusNodePub, extraParams: _objectSpread({}, additionalParams), useDkg: this.config.useDkg, checkCommitment: this.config.checkCommitment }); }); loglevel.debug("torus-direct/getTorusKey", { retrieveShares: sharesResponse }); return sharesResponse; } async getRedirectResult({ replaceUrl = true, clearLoginDetails = true, storageData = undefined } = {}) { await this.init({ skipInit: true }); const url = new URL(window.location.href); const hash = url.hash.substring(1); const queryParams = {}; url.searchParams.forEach((value, key) => { queryParams[key] = value; }); if (!hash && Object.keys(queryParams).length === 0) { throw new Error("Found Empty hash and query parameters. This can happen if user reloads the page"); } const { error: error$1, instanceParameters, hashParameters } = helpers.handleRedirectParameters(hash, queryParams); const { instanceId } = instanceParameters; loglevel.info(instanceId, "instanceId"); const loginDetails = storageData || (await this.storageHelper.retrieveData(`torus_login_${instanceId}`)); const _ref = loginDetails || {}, { args } = _ref, rest = _objectWithoutProperties(_ref, _excluded2); loglevel.info(args, "args", this.storageHelper.storageMethodUsed); let result; if (error$1) { return { error: error$1, state: instanceParameters || {}, result: {}, hashParameters, args }; } try { args.hash = hash; args.queryParameters = queryParams; result = await this.triggerLogin(args); } catch (err) { const serializedError = await error.serializeError(err); loglevel.error(serializedError); if (clearLoginDetails) { this.storageHelper.clearStorage(`torus_login_${instanceId}`); } return _objectSpread({ error: `${serializedError.message || ""}`, state: instanceParameters || {}, result: {}, hashParameters, args }, rest); } if (!result) return _objectSpread({ error: `Init parameters not found. It might be because storage is not available. Please retry the login in a different browser. Used storage method: ${this.storageHelper.storageMethodUsed}`, state: instanceParameters || {}, result: {}, hashParameters, args }, rest); if (replaceUrl) { const cleanUrl = window.location.origin + window.location.pathname; window.history.replaceState(_objectSpread(_objectSpread({}, window.history.state), {}, { as: cleanUrl, url: cleanUrl }), "", cleanUrl); } if (clearLoginDetails) { this.storageHelper.clearStorage(`torus_login_${instanceId}`); } return _objectSpread({ result, state: instanceParameters || {}, hashParameters, args }, rest); } async handlePrefetchRedirectUri() { if (!document) return Promise.resolve(); return new Promise((resolve, reject) => { const redirectHtml = document.createElement("link"); redirectHtml.href = this.config.redirect_uri; if (window.location.origin !== new URL(this.config.redirect_uri).origin) redirectHtml.crossOrigin = "anonymous"; redirectHtml.type = "text/html"; redirectHtml.rel = "prefetch"; const resolveFn = () => { this.isInitialized = true; resolve(); }; try { if (redirectHtml.relList && redirectHtml.relList.supports) { if (redirectHtml.relList.supports("prefetch")) { redirectHtml.onload = resolveFn; redirectHtml.onerror = () => { reject(new Error(`Please serve redirect.html present in serviceworker folder of this package on ${this.config.redirect_uri}`)); }; document.head.appendChild(redirectHtml); } else { // Link prefetch is not supported. pass through resolveFn(); } } else { // Link prefetch is not detectable. pass through resolveFn(); } } catch { resolveFn(); } }); } } exports.CustomAuth = CustomAuth;