@toruslabs/customauth
Version:
CustomAuth login with torus to get user private key
372 lines (368 loc) • 12 kB
JavaScript
'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;