Skip to content

Commit

Permalink
Merge pull request tkey#238 from tkey/sfa-return
Browse files Browse the repository at this point in the history
Add SFA Service Provider
  • Loading branch information
himanshuchawla009 authored Jun 12, 2024
2 parents dcfc9b2 + 479f667 commit 7bfbe73
Show file tree
Hide file tree
Showing 16 changed files with 339 additions and 14 deletions.
47 changes: 47 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions packages/common-types/src/baseTypes/commonTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ export interface IServiceProvider extends ISerializable {

serviceProviderName: string;

migratableKey?: BN | null;

encrypt(msg: Buffer): Promise<EncryptedMessage>;
decrypt(msg: EncryptedMessage): Promise<Buffer>;
retrievePubKey(type: PubKeyType): Buffer;
Expand Down
28 changes: 22 additions & 6 deletions packages/core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,13 +267,29 @@ class ThresholdKey implements ITKey {
if (neverInitializeNewKey) {
throw CoreError.default("key has not been generated yet");
}

// no metadata set, assumes new user
await this._initializeNewKey({
initializeModules: true,
importedKey: importKey,
importEd25519Seed: params?.importEd25519Seed,
delete1OutOf1: p.delete1OutOf1,
});

// check for serviceprovider migratableKey for import key from service provider for new user
// provided no importKey is provided ( importKey take precedent )
if (this.serviceProvider.migratableKey && !importKey) {
// importkey from server provider need to be atomic, hence manual sync is required.
const tempStateManualSync = this.manualSync; // temp store manual sync flag
this.manualSync = true; // Setting this as true since _initializeNewKey has a check where for importkey from server provider need to be atomic, hence manual sync is required.
await this._initializeNewKey({ initializeModules: true, importedKey: this.serviceProvider.migratableKey, delete1OutOf1: true });
if (!tempStateManualSync) await this.syncLocalMetadataTransitions(); // Only sync if we were not in manual sync mode, if manual sync is set by developer, they should handle it themselves
// restore manual sync flag
this.manualSync = tempStateManualSync;
} else {
await this._initializeNewKey({
initializeModules: true,
importedKey: importKey,
delete1OutOf1: p.delete1OutOf1,
importEd25519Seed: params?.importEd25519Seed,
});
}

// return after created new tkey account ( skip other steps)
return this.getKeyDetails();
}
// else we continue with catching up share and metadata
Expand Down
2 changes: 2 additions & 0 deletions packages/service-provider-base/src/ServiceProviderBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ class ServiceProviderBase implements IServiceProvider {

serviceProviderName: string;

migratableKey: BN | null = null;

constructor({ enableLogging = false, postboxKey }: ServiceProviderArgs) {
this.enableLogging = enableLogging;
this.postboxKey = new BN(postboxKey, "hex");
Expand Down
27 changes: 27 additions & 0 deletions packages/service-provider-sfa/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# tKey Single Factor Auth Service Provider

[![npm version](https://img.shields.io/npm/v/@tkey/service-provider-sfa?label=%22%22)](https://www.npmjs.com/package/@tkey/service-provider-sfa/v/latest) [![minzip](https://img.shields.io/bundlephobia/minzip/@tkey/service-provider-sfa?label=%22%22)](https://bundlephobia.com/result?p=@tkey/service-provider-sfa@latest)

Service Provider in `tKey` is used for generating a social login share of the private key share managed by a wallet service provider via
their own authentication flows.

## Installation

```shell
npm install --save @tkey/service-provider-sfa
```

### See the full [SDK Reference](https://web3auth.io/docs/sdk/core-kit/tkey/usage#log-in) on the Web3Auth Documentation

## Example

```js
import SFAServiceProvider from '@tkey/service-provider-sfa';

const web3AuthOptions: any = {
clientId, // Get your Client ID from Web3Auth Dashboard
web3AuthNetwork: 'testnet', // ["cyan", "testnet", "mainnet", "aqua", "sapphire_devnet", "sapphire_mainnet"]
};

const serviceProvider = new SFAServiceProvider({web3AuthOptions});
```
63 changes: 63 additions & 0 deletions packages/service-provider-sfa/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{
"name": "@tkey/service-provider-sfa",
"version": "13.0.0-alpha.4",
"description": "TKey Torus Service Provider Module",
"author": "Torus Labs",
"homepage": "https://github.com/tkey/tkey#readme",
"license": "MIT",
"main": "dist/serviceProviderSfa.cjs.js",
"module": "dist/serviceProviderSfa.esm.js",
"unpkg": "dist/serviceProviderSfa.umd.min.js",
"jsdelivr": "dist/serviceProviderSfa.umd.min.js",
"types": "dist/types/index.d.ts",
"files": [
"dist",
"src"
],
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/tkey/tkey.git"
},
"scripts": {
"test": "cross-env MOCKED=true mocha --config ../../.mocharc.json ",
"coverage": "nyc npm test",
"coverage-production": "nyc npm run test-production",
"test-development": "cross-env MOCKED=false METADATA=http://localhost:5051 mocha --config ../../.mocharc.json ",
"test-production": "cross-env MOCKED=false METADATA=https://metadata.tor.us mocha --config ../../.mocharc.json ",
"test-debugger": "mocha --config ../../.mocharc.json --inspect-brk",
"dev": "rimraf dist/ && cross-env NODE_ENV=development torus-scripts build",
"build": "rimraf dist/ && cross-env NODE_ENV=production torus-scripts build",
"lint": "eslint --fix 'src/**/*.ts'",
"prepack": "npm run build",
"pre-commit": "lint-staged"
},
"peerDependencies": {
"@babel/runtime": "7.x"
},
"dependencies": {
"@tkey/service-provider-base": "^13.0.0-alpha.4",
"@toruslabs/fetch-node-details": "^13.4.0",
"@toruslabs/torus.js": "^12.3.6",
"bn.js": "^5.2.1"
},
"devDependencies": {
"@types/bn.js": "^5.1.5"
},
"bugs": {
"url": "https://github.com/tkey/tkey/issues"
},
"lint-staged": {
"!(*d).ts": [
"npm run lint --",
"prettier --write 'src/**/*.ts'"
]
},
"engines": {
"node": ">=18.x",
"npm": ">=9.x"
},
"gitHead": "9967ce9f795f495f28ef0da1fc50acde31dcc258"
}
100 changes: 100 additions & 0 deletions packages/service-provider-sfa/src/SfaServiceProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { type StringifiedType } from "@tkey/common-types";
import { ServiceProviderBase } from "@tkey/service-provider-base";
import { NodeDetailManager } from "@toruslabs/fetch-node-details";
import Torus, { keccak256, TorusKey } from "@toruslabs/torus.js";
import BN from "bn.js";

import { AggregateVerifierParams, LoginParams, SfaServiceProviderArgs, Web3AuthOptions } from "./interfaces";

class SfaServiceProvider extends ServiceProviderBase {
web3AuthOptions: Web3AuthOptions;

authInstance: Torus;

public torusKey: TorusKey;

public migratableKey: BN | null = null; // Migration of key from SFA to tKey

private nodeDetailManagerInstance: NodeDetailManager;

constructor({ enableLogging = false, postboxKey, web3AuthOptions }: SfaServiceProviderArgs) {
super({ enableLogging, postboxKey });
this.web3AuthOptions = web3AuthOptions;
this.authInstance = new Torus({
clientId: web3AuthOptions.clientId,
enableOneKey: true,
network: web3AuthOptions.network,
});
Torus.enableLogging(enableLogging);
this.serviceProviderName = "SfaServiceProvider";
this.nodeDetailManagerInstance = new NodeDetailManager({ network: web3AuthOptions.network, enableLogging });
}

static fromJSON(value: StringifiedType): SfaServiceProvider {
const { enableLogging, postboxKey, web3AuthOptions, serviceProviderName, torusKey } = value;
if (serviceProviderName !== "SfaServiceProvider") return undefined;

const sfaSP = new SfaServiceProvider({
enableLogging,
postboxKey,
web3AuthOptions,
});

sfaSP.torusKey = torusKey;

return sfaSP;
}

async connect(params: LoginParams): Promise<BN> {
const { verifier, verifierId, idToken, subVerifierInfoArray } = params;
const verifierDetails = { verifier, verifierId };

// fetch node details.
const { torusNodeEndpoints, torusIndexes } = await this.nodeDetailManagerInstance.getNodeDetails(verifierDetails);

if (params.serverTimeOffset) {
this.authInstance.serverTimeOffset = params.serverTimeOffset;
}

let finalIdToken = idToken;
let finalVerifierParams = { verifier_id: verifierId };
if (subVerifierInfoArray && subVerifierInfoArray?.length > 0) {
const aggregateVerifierParams: AggregateVerifierParams = { verify_params: [], sub_verifier_ids: [], verifier_id: "" };
const aggregateIdTokenSeeds = [];
for (let index = 0; index < subVerifierInfoArray.length; index += 1) {
const userInfo = subVerifierInfoArray[index];
aggregateVerifierParams.verify_params.push({ verifier_id: verifierId, idtoken: userInfo.idToken });
aggregateVerifierParams.sub_verifier_ids.push(userInfo.verifier);
aggregateIdTokenSeeds.push(userInfo.idToken);
}
aggregateIdTokenSeeds.sort();

finalIdToken = keccak256(Buffer.from(aggregateIdTokenSeeds.join(String.fromCharCode(29)), "utf8")).slice(2);

aggregateVerifierParams.verifier_id = verifierId;
finalVerifierParams = aggregateVerifierParams;
}

const torusKey = await this.authInstance.retrieveShares(torusNodeEndpoints, torusIndexes, verifier, finalVerifierParams, finalIdToken);
this.torusKey = torusKey;

if (!torusKey.metadata.upgraded) {
const { finalKeyData, oAuthKeyData } = torusKey;
const privKey = finalKeyData.privKey || oAuthKeyData.privKey;
this.migratableKey = new BN(privKey, "hex");
}
const postboxKey = Torus.getPostboxKey(torusKey);
this.postboxKey = new BN(postboxKey, 16);
return this.postboxKey;
}

toJSON(): StringifiedType {
return {
...super.toJSON(),
serviceProviderName: this.serviceProviderName,
web3AuthOptions: this.web3AuthOptions,
};
}
}

export default SfaServiceProvider;
1 change: 1 addition & 0 deletions packages/service-provider-sfa/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default, default as SfaServiceProvider } from "./SfaServiceProvider";
30 changes: 30 additions & 0 deletions packages/service-provider-sfa/src/interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { type ServiceProviderArgs } from "@tkey/common-types";
import { type TORUS_NETWORK_TYPE } from "@toruslabs/constants";

export interface Web3AuthOptions {
clientId: string;
network: TORUS_NETWORK_TYPE;
}
export interface SfaServiceProviderArgs extends ServiceProviderArgs {
web3AuthOptions: Web3AuthOptions;
}

export interface TorusSubVerifierInfo {
verifier: string;
idToken: string;
}

export type AggregateVerifierParams = {
verify_params: { verifier_id: string; idtoken: string }[];
sub_verifier_ids: string[];
verifier_id: string;
};

export type LoginParams = {
verifier: string;
verifierId: string;
idToken: string;
subVerifierInfoArray?: TorusSubVerifierInfo[];
// offset in seconds
serverTimeOffset?: number;
};
7 changes: 7 additions & 0 deletions packages/service-provider-sfa/test/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"rules": {
"prefer-arrow-callback": "off",
"func-names": "off"
}

}
Empty file.
1 change: 1 addition & 0 deletions packages/service-provider-sfa/torus.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require("../../torus.config");
4 changes: 4 additions & 0 deletions packages/service-provider-sfa/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["src", "test"]
}
6 changes: 6 additions & 0 deletions packages/service-provider-sfa/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const generateWebpackConfig = require("../../webpack.config");

const config = generateWebpackConfig({ });

exports.baseConfig = config.baseConfig;
Loading

0 comments on commit 7bfbe73

Please sign in to comment.