Skip to content

Commit

Permalink
import sfa key
Browse files Browse the repository at this point in the history
  • Loading branch information
himanshuchawla009 committed Dec 10, 2024
1 parent a14138b commit ed54e8c
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 28 deletions.
19 changes: 18 additions & 1 deletion src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,18 @@ export type MPCKeyDetails = {
tssPubKey?: TkeyPoint;
};

export type OAuthLoginParams = (SubVerifierDetailsParams | AggregateVerifierLoginParams) & { importTssKey?: string };
export type OAuthLoginParams = (SubVerifierDetailsParams | AggregateVerifierLoginParams) & {
/**
* Key to import key into Tss during first time login.
*/
importTssKey?: string;

/**
* For new users, use SFA key if user was registered with SFA before.
* Useful when you created the user with SFA before and now want to convert it to TSS.
*/
registerExistingSFAKey?: boolean;
};
export type UserInfo = TorusVerifierResponse & LoginWindowResponse;

export interface EnableMFAParams {
Expand Down Expand Up @@ -146,6 +157,12 @@ export interface JWTLoginParams {
*/
importTssKey?: string;

/**
* For new users, use SFA key if user was registered with SFA before.
* Useful when you created the user with SFA before and now want to convert it to TSS.
*/
registerExistingSFAKey?: boolean;

/**
* Number of TSS public keys to prefetch. For the best performance, set it to
* the number of factors you want to create. Set it to 0 for an existing user.
Expand Down
73 changes: 50 additions & 23 deletions src/mpcCoreKit.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BNString, KeyType, Point, secp256k1, SHARE_DELETED, ShareStore, StringifiedType } from "@tkey/common-types";
import { BNString, KeyType, ONE_KEY_DELETE_NONCE, Point, secp256k1, SHARE_DELETED, ShareStore, StringifiedType } from "@tkey/common-types";
import { CoreError } from "@tkey/core";
import { ShareSerializationModule } from "@tkey/share-serialization";
import { TorusStorageLayer } from "@tkey/storage-layer-torus";
Expand Down Expand Up @@ -318,16 +318,19 @@ export class Web3AuthMPCCoreKit implements ICoreKit {
if (this.isNodejsOrRN(this.options.uxMode)) {
throw CoreKitError.oauthLoginUnsupported(`Oauth login is NOT supported in ${this.options.uxMode} mode.`);
}
const { importTssKey } = params;
const { importTssKey, registerExistingSFAKey } = params;
const tkeyServiceProvider = this.torusSp;

if (this.isRedirectMode && (importTssKey || registerExistingSFAKey)) {
throw CoreKitError.invalidConfig("key import is not supported in redirect mode");
}
try {
// oAuth login.
const verifierParams = params as SubVerifierDetailsParams;
const aggregateParams = params as AggregateVerifierLoginParams;
let loginResponse: TorusLoginResponse | TorusAggregateLoginResponse;
if (verifierParams.subVerifierDetails) {
// single verifier login.
const loginResponse = await tkeyServiceProvider.triggerLogin((params as SubVerifierDetailsParams).subVerifierDetails);
loginResponse = await tkeyServiceProvider.triggerLogin((params as SubVerifierDetailsParams).subVerifierDetails);

if (this.isRedirectMode) return;

Expand All @@ -338,7 +341,7 @@ export class Web3AuthMPCCoreKit implements ICoreKit {
signatures: this._getSignatures(loginResponse.sessionData.sessionTokenData),
});
} else if (aggregateParams.subVerifierDetailsArray) {
const loginResponse = await tkeyServiceProvider.triggerAggregateLogin({
loginResponse = await tkeyServiceProvider.triggerAggregateLogin({
aggregateVerifierType: aggregateParams.aggregateVerifierType || AGGREGATE_VERIFIER.SINGLE_VERIFIER_ID,
verifierIdentifier: aggregateParams.aggregateVerifierIdentifier as string,
subVerifierDetailsArray: aggregateParams.subVerifierDetailsArray,
Expand All @@ -354,7 +357,15 @@ export class Web3AuthMPCCoreKit implements ICoreKit {
});
}

await this.setupTkey(importTssKey);
if (loginResponse && registerExistingSFAKey && loginResponse.finalKeyData.privKey) {
if (loginResponse.metadata.typeOfUser === "v1") {
throw CoreKitError.invalidConfig("Cannot register existing SFA key for v1 users, please contact web3auth support.");
}
const existingSFAKey = loginResponse.finalKeyData.privKey.padStart(64, "0");
await this.setupTkey(existingSFAKey, true);
} else {
await this.setupTkey(importTssKey, false);
}
} catch (err: unknown) {
log.error("login error", err);
if (err instanceof CoreError) {
Expand All @@ -373,10 +384,14 @@ export class Web3AuthMPCCoreKit implements ICoreKit {
throw CoreKitError.prefetchValueExceeded(`The prefetch value '${prefetchTssPublicKeys}' exceeds the maximum allowed limit of 3.`);
}

const { verifier, verifierId, idToken, importTssKey } = params;
const { verifier, verifierId, idToken, importTssKey, registerExistingSFAKey } = params;
this.torusSp.verifierName = verifier;
this.torusSp.verifierId = verifierId;

if (registerExistingSFAKey && importTssKey) {
throw CoreKitError.invalidConfig("Cannot import TSS key and register SFA key at the same time.");
}

try {
// prefetch tss pub keys.
const prefetchTssPubs = [];
Expand Down Expand Up @@ -412,7 +427,15 @@ export class Web3AuthMPCCoreKit implements ICoreKit {
userInfo: { ...parseToken(idToken), verifier, verifierId },
signatures: this._getSignatures(loginResponse.sessionData.sessionTokenData),
});
await this.setupTkey(importTssKey);
if (registerExistingSFAKey && loginResponse.finalKeyData.privKey) {
if (loginResponse.metadata.typeOfUser === "v1") {
throw CoreKitError.invalidConfig("Cannot register existing SFA key for v1 users, please contact web3auth support.");
}
const existingSFAKey = loginResponse.finalKeyData.privKey.padStart(64, "0");
await this.setupTkey(existingSFAKey, true);
} else {
await this.setupTkey(importTssKey, false);
}
} catch (err: unknown) {
log.error("login error", err);
if (err instanceof CoreError) {
Expand All @@ -433,30 +456,30 @@ export class Web3AuthMPCCoreKit implements ICoreKit {

try {
const result = await this.torusSp.customAuthInstance.getRedirectResult();

let loginResponse: TorusLoginResponse | TorusAggregateLoginResponse;
if (result.method === TORUS_METHOD.TRIGGER_LOGIN) {
const data = result.result as TorusLoginResponse;
if (!data) {
loginResponse = result.result as TorusLoginResponse;
if (!loginResponse) {
throw CoreKitError.invalidTorusLoginResponse();
}
this.updateState({
postBoxKey: this._getPostBoxKey(data),
postboxKeyNodeIndexes: data.nodesData?.nodeIndexes || [],
userInfo: data.userInfo,
signatures: this._getSignatures(data.sessionData.sessionTokenData),
postBoxKey: this._getPostBoxKey(loginResponse),
postboxKeyNodeIndexes: loginResponse.nodesData?.nodeIndexes || [],
userInfo: loginResponse.userInfo,
signatures: this._getSignatures(loginResponse.sessionData.sessionTokenData),
});
const userInfo = this.getUserInfo();
this.torusSp.verifierName = userInfo.verifier;
} else if (result.method === TORUS_METHOD.TRIGGER_AGGREGATE_LOGIN) {
const data = result.result as TorusAggregateLoginResponse;
if (!data) {
loginResponse = result.result as TorusAggregateLoginResponse;
if (!loginResponse) {
throw CoreKitError.invalidTorusAggregateLoginResponse();
}
this.updateState({
postBoxKey: this._getPostBoxKey(data),
postboxKeyNodeIndexes: data.nodesData?.nodeIndexes || [],
userInfo: data.userInfo[0],
signatures: this._getSignatures(data.sessionData.sessionTokenData),
postBoxKey: this._getPostBoxKey(loginResponse),
postboxKeyNodeIndexes: loginResponse.nodesData?.nodeIndexes || [],
userInfo: loginResponse.userInfo[0],
signatures: this._getSignatures(loginResponse.sessionData.sessionTokenData),
});
const userInfo = this.getUserInfo();
this.torusSp.verifierName = userInfo.aggregateVerifier;
Expand Down Expand Up @@ -984,7 +1007,7 @@ export class Web3AuthMPCCoreKit implements ICoreKit {
return tssNonce;
}

private async setupTkey(providedImportTssKey?: string): Promise<void> {
private async setupTkey(providedImportTssKey?: string, isSfaKey?: boolean): Promise<void> {
if (!this.state.postBoxKey) {
throw CoreKitError.userNotLoggedIn();
}
Expand All @@ -1003,10 +1026,14 @@ export class Web3AuthMPCCoreKit implements ICoreKit {
}
}
await this.handleNewUser(importTssKey);
} else {
if (importTssKey) {
throw CoreKitError.tssKeyImportNotAllowed();
}
} else {
if (importTssKey && isSfaKey) {
await this.tkey.addLocalMetadataTransitions({ input: [{ message: ONE_KEY_DELETE_NONCE }], privKey: [new BN(this.state.postBoxKey, "hex")] });
if (!this.tkey?.manualSync) await this.tkey?.syncLocalMetadataTransitions();
}
await this.handleExistingUser();
}
}
Expand Down
4 changes: 2 additions & 2 deletions tests/importRecovery.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import test from "node:test";
import { tssLib as tssLibDKLS } from "@toruslabs/tss-dkls-lib";
import { tssLib as tssLibFROST } from "@toruslabs/tss-frost-lib";

import { AsyncStorage, MemoryStorage, TssLib, TssShareType, WEB3AUTH_NETWORK } from "../src";
import { AsyncStorage, MemoryStorage, TssLibType, TssShareType, WEB3AUTH_NETWORK } from "../src";
import { bufferToElliptic, criticalResetAccount, newCoreKitLogInInstance } from "./setup";

type ImportKeyTestVariable = {
manualSync?: boolean;
email: string;
importKeyEmail: string;
tssLib: TssLib;
tssLib: TssLibType;
};

const storageInstance = new MemoryStorage();
Expand Down
59 changes: 57 additions & 2 deletions tests/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import BN from "bn.js";
import jwt, { Algorithm } from "jsonwebtoken";
import { tssLib as tssLibDKLS } from "@toruslabs/tss-dkls-lib";

import { IAsyncStorage, IStorage, parseToken, TssLib, WEB3AUTH_NETWORK_TYPE, Web3AuthMPCCoreKit } from "../src";
import { IAsyncStorage, IStorage, parseToken, TssLibType, WEB3AUTH_NETWORK_TYPE, Web3AuthMPCCoreKit } from "../src";

export const mockLogin2 = async (email: string) => {
const req = new Request("https://li6lnimoyrwgn2iuqtgdwlrwvq0upwtr.lambda-url.eu-west-1.on.aws/", {
Expand Down Expand Up @@ -91,14 +91,16 @@ export const newCoreKitLogInInstance = async ({
email,
storageInstance,
importTssKey,
registerExistingSFAKey,
login,
}: {
network: WEB3AUTH_NETWORK_TYPE;
manualSync: boolean;
email: string;
storageInstance: IStorage | IAsyncStorage;
tssLib?: TssLib;
tssLib?: TssLibType;
importTssKey?: string;
registerExistingSFAKey?: boolean;
login?: LoginFunc;
}) => {
const instance = new Web3AuthMPCCoreKit({
Expand All @@ -118,11 +120,55 @@ export const newCoreKitLogInInstance = async ({
verifierId: parsedToken.email,
idToken,
importTssKey,
registerExistingSFAKey
});

return instance;
};

export const loginWithSFA = async ({
network,
manualSync,
email,
storageInstance,
login,
}: {
network: WEB3AUTH_NETWORK_TYPE;
manualSync: boolean;
email: string;
storageInstance: IStorage | IAsyncStorage;
tssLib?: TssLibType;
login?: LoginFunc;
}) => {
const instance = new Web3AuthMPCCoreKit({
web3AuthClientId: "torus-key-test",
web3AuthNetwork: network,
baseUrl: "http://localhost:3000",
uxMode: "nodejs",
tssLib: tssLib || tssLibDKLS,
storage: storageInstance,
manualSync,
});

const { idToken, parsedToken } = login ? await login(email) : await mockLogin(email);
await instance.init();
const nodeDetails = await instance.torusSp.customAuthInstance.nodeDetailManager.getNodeDetails({
verifier: "torus-key-test",
verifierId: parsedToken.email,
})
return await instance.torusSp.customAuthInstance.torus.retrieveShares({
idToken,
nodePubkeys: nodeDetails.torusNodePub,
verifier: "torus-key-test",
verifierParams: {
verifier_id: parsedToken.email,
},
endpoints: nodeDetails.torusNodeEndpoints,
indexes: nodeDetails.torusIndexes,
})

}

export class AsyncMemoryStorage implements IAsyncStorage {
private _store: Record<string, string> = {};

Expand All @@ -138,3 +184,12 @@ export class AsyncMemoryStorage implements IAsyncStorage {
export function bufferToElliptic(p: Buffer, ec = secp256k1): EllipticPoint {
return ec.keyFromPublic(p).getPublic();
}


export function generateRandomEmail(): string {
const username = stringGen(10);
const domain = stringGen(5);
const tld = stringGen(3);
return `${username}@${domain}.${tld}`;
}

Loading

0 comments on commit ed54e8c

Please sign in to comment.