Skip to content

Latest commit

 

History

History
188 lines (144 loc) · 5.93 KB

tkeyflow.md

File metadata and controls

188 lines (144 loc) · 5.93 KB

ok.. the flow goes like this

  1. Decide the modules you want apart from the ones included in default (you can override)
const modules = {
  [SECURITY_QUESTIONS_MODULE_NAME]: new SecurityQuestionsModule(),
  [WEB_STORAGE_MODULE_NAME]: new WebStorageModule(),
};
  1. Create tkey instance (using @tkey-mpc/default)
const customAuthArgs = {
  baseUrl: `${window.location.origin}/serviceworker`,
  network: "testnet",
};

const tKey = new ThresholdKey({
  modules,
  customAuthArgs,
});
  1. Log the user in with google/fb
const verifierParams = {
  verifier: "google-lrc",
  clientId: "221898609709-obfn3p63741l5333093430j3qeiinaa8.apps.googleusercontent.com",
  typeOfLogin: "google",
};

const directAuthResponse = await tKey.serviceProvider.triggerLogin(verifierParams);
const postboxKey = tKey.serviceProvider.postboxKey.toString("hex");
  1. Depending on your UI flows, you may wish to check if user already has a tkey or not This is where you educate an user about tkey and device storage etc.
const shareStore = await tKey.storageLayer.getMetadata({ privKey: postboxKey });
const isNewKey = shareStore.message === "KEY_NOT_FOUND";

The flow is of two parts here: a: creation b: login

Creation Flow:

  1. You may want the user to set a password, add a recovery email or store the device share to FileStorage i.e., add extra authentication factors for better recoverability You can prompt the user for input for all these params
// This function initializes the login/creates tkey using the modules
// Say if webstorage module is used,
// serviceProvider is the first share and localStorage is the second and (2/2) is created
await tKey.initialize({ input: shareStore }); // metadata is from the above step

// If you add password using security questions module
await tKey.modules[SECURITY_QUESTIONS_MODULE_KEY].generateNewShareWithSecurityQuestions(password, "What is your password?");
  1. Reconstruct the tKey
const { privKey } = await tKey.reconstructKey(false);

Login flow:

  1. Initialize tkey first
await tKey.initialize();

This will use the modules and inform if you need any additional shares for tKey reconstruction to complete

// requiredShares is the no.of additional shares you need to input
// You need to take input from the user if any additional shares are required
const { requiredShares } = tKey.getKeyDetails();
  1. If requiredShares === 0, you can reconstruct the key
const { privKey } = await tKey.reconstructKey(false);

Additional Features:

  • To Store share on file storage in addition to localStorage, call
// First get your device share using
const deviceShare = await tKey.modules[WEB_STORAGE_MODULE_KEY].getDeviceShare();
// Now, pass this to store on file storage
await tKey.modules[WEB_STORAGE_MODULE_KEY].storeDeviceShareOnFileStorage(deviceShare.share.shareIndex);
  • To send a recovery share in email
const shareCreated = await tKey.generateNewShare();
const requiredShareStore = shareCreated.newShareStores[shareCreated.newShareIndex.toString("hex")];
// remember to include in initializtion modules
const serializedShare = await tKey.modules[SHARE_SERIALIZATION_MODULE_KEY].serialize(requiredShareStore.share.share, "mnemonic");
// Now, this serializedShare is a mnemonic which you can display to user/send mail
  • To allow for Share Transfer

Add ShareTransferModule to initial set of modules while creating tKey

Start listening on the original device

const TKEY_SHARE_TRANSFER_INTERVAL = 5000; //ms
// we use bowser to parse the userAgent to display in UI which device the share is coming from
async function getPendingShareTransferRequests(tKey) {
  const latestShareTransferStore = await tKey.modules[SHARE_TRANSFER_MODULE_KEY].getShareTransferStore();
  const pendingRequests = Object.keys(latestShareTransferStore).reduce((acc, x) => {
    const browserDetail = bowser.parse(latestShareTransferStore[x].userAgent);
    if (!latestShareTransferStore[x].encShareInTransit) acc.push({ ...latestShareTransferStore[x], browserDetail, encPubKeyX: x });
    return acc;
  }, []);
  return pendingRequests;
}

let requestStatusCheckId;

const checkFn = async () => {
  try {
    const pendingRequests = await getPendingShareTransferRequests(tKey);
    if (Object.keys(pendingRequests).length > 0) {
      // Once we see some pending requests, we stop the interval, display to user for confirmation of share transfer
      clearInterval(requestStatusCheckId);
    }
  } catch (error) {
    clearInterval(requestStatusCheckId);
  }
};
checkFn();
requestStatusCheckId = setInterval(checkFn, TKEY_SHARE_TRANSFER_INTERVAL);

// Once the user approves, call
// encPubKeyX is available in pendingRequests
await tKey.modules[SHARE_TRANSFER_MODULE_KEY].approveRequest(encPubKeyX);
await tKey.syncShareMetadata();
// You can start the interval again
requestStatusCheckId = setInterval(checkFn, TKEY_SHARE_TRANSFER_INTERVAL);

In the new device,

// Make the request for share transfer
const currentEncPubKeyX = await tKey.modules[SHARE_TRANSFER_MODULE_KEY].requestNewShare(window.navigator.userAgent, tKey.getCurrentShareIndexes());
// shareStore is the received share store after approval on the original device
const shareStore = await tKey.modules[SHARE_TRANSFER_MODULE_KEY].startRequestStatusCheck(currentEncPubKeyX, true);
const { privKey } = await tKey.reconstructKey(false);

Post share transfer your tKey on either device maybe of lower nonce. (can be detected from error code) You can fix that by using

let updatedTkey = await tkey.updateMetadata;
  • Export a Share
const shareStore = await tKey.outputShareStore(shareIndex);
const serializedShare = await tKey.modules[SHARE_SERIALIZATION_MODULE_KEY].serialize(shareStore.share.share, "mnemonic");
  • Import a Serialized Share
const deserializedShare = await this.tKey.modules[SHARE_SERIALIZATION_MODULE_KEY].deserialize(shareMnemonic, "mnemonic");
await this.tKey.inputShare(deserializedShare);