Skip to content

Commit

Permalink
Merge pull request #443 from torusresearch/feat/openlogin-v6
Browse files Browse the repository at this point in the history
openlogin v6 upgrade
  • Loading branch information
ieow authored Jan 3, 2024
2 parents 9425e94 + 4540546 commit 1a18c34
Show file tree
Hide file tree
Showing 71 changed files with 1,792 additions and 1,997 deletions.
12 changes: 2 additions & 10 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,9 @@ module.exports = {
ecmaVersion: 2022,
project: "./tsconfig.json",
},
ignorePatterns: [
"*.cjs",
"*.config.js",
"vite.config.ts",
"importLocales.js",
"__generated__",
".eslintrc.js",
"crisp.js",
"tests/setup.js"
],
ignorePatterns: ["*.cjs", "*.config.js", "vite.config.ts", "importLocales.js", "__generated__", ".eslintrc.js", "crisp.js", "tests/setup.js"],
rules: {
"@typescript-eslint/no-explicit-any": 1,
camelcase: 0,
"no-new": 0,
"vue/multi-word-component-names": 0,
Expand Down
2,298 changes: 977 additions & 1,321 deletions package-lock.json

Large diffs are not rendered by default.

103 changes: 52 additions & 51 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,36 +19,37 @@
"serve:prod": "vue-cli-service serve --mode production"
},
"dependencies": {
"@bonfida/spl-name-service": "^1.0.11",
"@gtm-support/vue-gtm": "^2.1.0",
"@headlessui/vue": "^1.6.7",
"@bonfida/spl-name-service": "^1.5.0",
"@gtm-support/vue-gtm": "^2.2.0",
"@headlessui/vue": "^1.7.16",
"@heroicons/vue": "^1.0.6",
"@project-serum/borsh": "^0.2.5",
"@sentry/browser": "^7.68.0",
"@sentry/tracing": "^7.68.0",
"@sentry/vue": "^7.68.0",
"@sentry/browser": "^7.77.0",
"@sentry/tracing": "^7.77.0",
"@sentry/vue": "^7.77.0",
"@solana/pay": "^0.2.5",
"@solana/qr-code-styling": "^1.6.0",
"@solana/spl-name-service": "^0.1.4",
"@solana/spl-name-service": "^0.2.0",
"@solana/spl-token": "0.3.8",
"@solana/web3.js": "^1.78.4",
"@toruslabs/base-controllers": "^3.0.3",
"@solana/web3.js": "^1.87.3",
"@toruslabs/base-controllers": "^4.11.0",
"@toruslabs/broadcast-channel": "^9.0.1",
"@toruslabs/eccrypto": "^4.0.0",
"@toruslabs/http-helpers": "^5.0.0",
"@toruslabs/http-helpers": "^6.0.0",
"@toruslabs/loglevel-sentry": "^6.0.1",
"@toruslabs/metadata-helpers": "^5.0.0",
"@toruslabs/openlogin": "^4.7.0",
"@toruslabs/openlogin-ed25519": "^2.0.0",
"@toruslabs/openlogin-jrpc": "^4.7.0",
"@toruslabs/openlogin-subkey": "^4.2.0",
"@toruslabs/openlogin-utils": "^4.7.0",
"@toruslabs/solana-controllers": "^3.0.3",
"@toruslabs/openlogin": "^6.0.0",
"@toruslabs/openlogin-ed25519": "^6.0.0",
"@toruslabs/openlogin-jrpc": "^6.1.0",
"@toruslabs/openlogin-session-manager": "^3.0.0",
"@toruslabs/openlogin-subkey": "^6.0.0",
"@toruslabs/openlogin-utils": "^6.1.0",
"@toruslabs/solana-controllers": "^4.11.0",
"@toruslabs/tweetnacl-js": "^1.0.4",
"@toruslabs/vue-components": "^2.0.1",
"@toruslabs/vue-icons": "^2.0.0",
"@types/bn.js": "^5.1.1",
"@types/elliptic": "^6.4.14",
"@types/bn.js": "^5.1.4",
"@types/elliptic": "^6.4.16",
"@vuelidate/core": "^2.0.3",
"@vuelidate/validators": "^2.0.4",
"async-mutex": "^0.4.0",
Expand All @@ -70,68 +71,68 @@
"readable-stream": "^4.4.2",
"register-service-worker": "^1.7.2",
"safe-stable-stringify": "^2.4.3",
"vue": "^3.3.4",
"vue": "^3.3.7",
"vue-gtag": "^2.0.1",
"vue-i18n": "^9.3.0",
"vue-router": "^4.2.4",
"vue-i18n": "^9.6.5",
"vue-router": "^4.2.5",
"vuex": "^4.1.0",
"vuex-module-decorators": "^2.0.0"
},
"devDependencies": {
"@babel/register": "^7.22.15",
"@babel/runtime": "^7.22.15",
"@commitlint/cli": "^17.7.1",
"@commitlint/config-conventional": "^17.7.0",
"@commitlint/is-ignored": "^17.7.0",
"@babel/runtime": "^7.23.2",
"@commitlint/cli": "^18.2.0",
"@commitlint/config-conventional": "^18.1.0",
"@commitlint/is-ignored": "^18.1.0",
"@istanbuljs/nyc-config-babel": "^3.0.0",
"@metaplex-foundation/mpl-token-metadata": "^1.1.0",
"@playwright/test": "^1.37.1",
"@sentry/cli": "^2.20.6",
"@playwright/test": "^1.39.0",
"@sentry/cli": "^2.21.2",
"@tailwindcss/forms": "^0.5.6",
"@toruslabs/config": "^2.0.2",
"@toruslabs/eslint-config-vue": "^3.0.1",
"@toruslabs/eslint-config-vue": "^3.0.2",
"@toruslabs/tweetnacl-js": "^1.0.4",
"@types/bs58": "^4.0.1",
"@types/color": "^3.0.4",
"@types/dateformat": "^5.0.0",
"@types/jest": "^29.5.4",
"@types/json-stable-stringify": "^1.0.34",
"@types/lodash-es": "^4.17.9",
"@types/mocha": "^10.0.1",
"@types/node": "^20.5.9",
"@types/pump": "^1.1.1",
"@types/readable-stream": "^4.0.2",
"@types/sinon": "^10.0.16",
"@typescript-eslint/eslint-plugin": "^6.6.0",
"@typescript-eslint/parser": "^6.6.0",
"@types/bs58": "^4.0.3",
"@types/color": "^3.0.5",
"@types/dateformat": "^5.0.1",
"@types/jest": "^29.5.7",
"@types/json-stable-stringify": "^1.0.35",
"@types/lodash-es": "^4.17.10",
"@types/mocha": "^10.0.3",
"@types/node": "^20.8.10",
"@types/pump": "^1.1.2",
"@types/readable-stream": "^4.0.4",
"@types/sinon": "^17.0.0",
"@typescript-eslint/eslint-plugin": "^6.9.1",
"@typescript-eslint/parser": "^6.9.1",
"@vue/cli-plugin-babel": "~5.0.8",
"@vue/cli-plugin-eslint": "~5.0.8",
"@vue/cli-plugin-pwa": "^5.0.8",
"@vue/cli-plugin-router": "~5.0.8",
"@vue/cli-plugin-typescript": "~5.0.8",
"@vue/cli-plugin-vuex": "^5.0.8",
"@vue/cli-service": "~5.0.8",
"@vue/compiler-sfc": "^3.3.4",
"@vue/compiler-sfc": "^3.3.7",
"assert": "^2.1.0",
"autoprefixer": "^10.4.15",
"axios": "^1.5.0",
"autoprefixer": "^10.4.16",
"axios": "^1.6.0",
"bn.js": "^5.2.1",
"browserstack-local": "^1.5.4",
"browserstack-local": "^1.5.5",
"dotenv": "^16.3.1",
"elliptic": "^6.5.4",
"eslint": "^8.48.0",
"eslint": "^8.53.0",
"husky": "^8.0.3",
"jsdom": "^22.1.0",
"jsdom-global": "^3.0.2",
"lint-staged": "^14.0.1",
"lint-staged": "^15.0.2",
"mocha": "^10.2.0",
"nock": "^13.3.3",
"nock": "^13.3.8",
"nyc": "^15.1.0",
"postcss": "^8.4.29",
"postcss": "^8.4.31",
"prettier": "^3.0.3",
"process": "^0.11.10",
"sinon": "^15.2.0",
"tailwindcss": "^3.3.3",
"sinon": "^17.0.1",
"tailwindcss": "^3.3.5",
"ts-node": "^10.9.1",
"typescript": "^5.2.2"
},
Expand Down
53 changes: 50 additions & 3 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,60 @@
<script setup lang="ts">
import log from "loglevel";
import { onBeforeMount } from "vue";
import { Toast } from "@/components/common";
import ControllerModule from "./modules/controllers";
import OpenLoginFactory from "./auth/OpenLogin";
import ControllerModule, { torus } from "./modules/controllers";
import { hideCrispButton, isMain } from "./utils/helpers";
onBeforeMount(() => {
if (isMain) ControllerModule.init({ origin: window.location.origin });
onBeforeMount(async () => {
if (isMain) {
ControllerModule.setIsRehydrating(true);
try {
const openloginInstance = await OpenLoginFactory.getInstance(true);
ControllerModule.init({ origin: window.location.origin });
const result = await OpenLoginFactory.computeAccount().catch((err) => {
log.error(err);
return null;
});
// rehydration
if (result?.accounts.length) {
const userDapp = new Map();
const addAccountPromises = result.accounts.map(async (account) => {
userDapp.set(account.address, account.app);
const address = await torus.addAccount(
account.solanaPrivKey,
{
email: "",
name: "",
profileImage: "",
...openloginInstance.getUserInfo(),
},
true
);
return address;
});
torus.update({
UserDapp: userDapp,
});
// await Promise.all(addAccountPromises);
try {
const address = await addAccountPromises[result.matchedDappHost];
await ControllerModule.setSelectedAccount(address);
} catch (error) {
log.error(error);
ControllerModule.setIsRehydrating(false);
}
}
} catch (error) {
log.error(error);
}
ControllerModule.setIsRehydrating(false);
}
// hide crispbutton on inital load
hideCrispButton();
Expand Down
129 changes: 128 additions & 1 deletion src/auth/OpenLogin.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import { Keypair } from "@solana/web3.js";
import { get } from "@toruslabs/http-helpers";
import OpenLogin from "@toruslabs/openlogin";
import { getED25519Key } from "@toruslabs/openlogin-ed25519";
import { OpenloginSessionManager } from "@toruslabs/openlogin-session-manager";
import { subkey } from "@toruslabs/openlogin-subkey";
import { Mutex } from "async-mutex";
import base58 from "bs58";
import log from "loglevel";

import config from "@/config";
import { APPLE, ProjectAccountType } from "@/utils/enums";
import { generateTorusAuthHeaders } from "@/utils/helpers";

class OpenLoginFactory {
private static instance: OpenLogin;

private static mutex = new Mutex();

public static async getInstance(): Promise<OpenLogin> {
public static async getInstance(reinitialize = false): Promise<OpenLogin> {
const releaseLock = await this.mutex.acquire();
try {
if (!OpenLoginFactory.instance) {
Expand All @@ -29,6 +37,9 @@ class OpenLoginFactory {

OpenLoginFactory.instance = instance;
}
if (reinitialize) {
await OpenLoginFactory.instance.init();
}
} catch (error) {
log.error(error, "Unable to create openlogin instance");
throw error;
Expand All @@ -37,5 +48,121 @@ class OpenLoginFactory {
}
return OpenLoginFactory.instance;
}

public static async computeAccount() {
const instance = await OpenLoginFactory.getInstance();
if (!instance || !instance.state.sessionId) {
throw new Error("Openlogin instance/session not found");
}

const openLoginState = instance.state;
const { tKey, oAuthPrivateKey, ed25519PrivKey } = openLoginState;

if (!ed25519PrivKey) {
throw new Error("Login unsuccessful");
}

const userInfo = instance.getUserInfo();

// const { sk: secretKey } = getED25519Key(privKey.padStart(64, "0"));
const secretKey = Buffer.from(ed25519PrivKey!, "hex");
const typeOfLoginDisplay = userInfo.typeOfLogin.charAt(0).toUpperCase() + userInfo.typeOfLogin.slice(1);
const accountDisplay = (userInfo.typeOfLogin !== APPLE && userInfo.email) || userInfo.name;
const mainKeyPair = Keypair.fromSecretKey(secretKey);
const accounts: ProjectAccountType[] = [];
accounts.push({
app: `${typeOfLoginDisplay} ${accountDisplay}`,
solanaPrivKey: base58.encode(mainKeyPair.secretKey),
privKey: ed25519PrivKey,
name: `Solana Wallet ${window.location.origin}`,
address: `${mainKeyPair.publicKey.toBase58()}`,
});

// derive app scoped keys from tkey
const userDapps: Record<string, string> = {};

// let matchedDappHost = -1;
let matchedDappHost = 0;
const dappOrigin = sessionStorage.getItem("dappOrigin") || window.location.origin;
const dappHost = new URL(dappOrigin);

if (tKey && oAuthPrivateKey) {
try {
// projects are stored on oAuthPrivateKey but subkey is derived from tkey

const headers = generateTorusAuthHeaders(oAuthPrivateKey);
log.info(headers, "headers");
const response = await get<{ user_projects: [{ last_login: string; project_id: string; hostname: string; name: string }] }>(
`${config.developerDashboardUrl}/projects/user-projects?chain_namespace=solana`,
{
headers,
}
);
log.info(response, "User projects from developer dashboard");
const userProjects = response.user_projects ?? [];
userProjects.sort((a, b) => (a.last_login < b.last_login ? 1 : -1));
userProjects.forEach((project, idx) => {
const subKey = subkey(tKey, Buffer.from(project.project_id, "base64"));
const paddedSubKey = subKey.padStart(64, "0");
const { sk } = getED25519Key(paddedSubKey);
const keyPair = Keypair.fromSecretKey(sk);
userDapps[keyPair.publicKey.toBase58()] = `${project.name} (${project.hostname})`;
accounts.push({
app: `${project.name}`,
solanaPrivKey: base58.encode(keyPair.secretKey),
privKey: paddedSubKey,
name: `${project.name} (${project.hostname})`,
address: keyPair.publicKey.toBase58(),
});

// eslint-disable-next-line no-console
console.log(project);
if (dappHost.host === project.hostname) matchedDappHost = idx + 1;
});
} catch (error2: unknown) {
log.error("Failed to derive app-scoped keys", error2);
}
}

return {
accounts,
userDapps,
matchedDappHost,
};
}
}
export async function updateSession(sessionData: any) {

Check warning on line 134 in src/auth/OpenLogin.ts

View workflow job for this annotation

GitHub Actions / build (20.x, ubuntu-latest)

Unexpected any. Specify a different type

Check warning on line 134 in src/auth/OpenLogin.ts

View workflow job for this annotation

GitHub Actions / build (20.x, ubuntu-latest)

Unexpected any. Specify a different type
try {
const instance = await OpenLoginFactory.getInstance();
if (instance.sessionId) {
const sessionManager = new OpenloginSessionManager({
sessionId: instance.sessionId,
sessionNamespace: instance.sessionNamespace,
});

await sessionManager.updateSession(sessionData);
}
} catch (error) {
log.warn(error);
}
}

export async function createSession(sessionId: string, data: any) {

Check warning on line 150 in src/auth/OpenLogin.ts

View workflow job for this annotation

GitHub Actions / build (20.x, ubuntu-latest)

Unexpected any. Specify a different type

Check warning on line 150 in src/auth/OpenLogin.ts

View workflow job for this annotation

GitHub Actions / build (20.x, ubuntu-latest)

Unexpected any. Specify a different type
if (!sessionId) throw new Error("Session Id is required");
try {
const sessionManager = new OpenloginSessionManager({
sessionId,
sessionNamespace: (await OpenLoginFactory.getInstance()).sessionNamespace,
});

await sessionManager.createSession(data);
} catch (error) {
log.error(error);
}
}

export async function invalidateSession() {
await (await OpenLoginFactory.getInstance()).logout();
}

export default OpenLoginFactory;
Loading

0 comments on commit 1a18c34

Please sign in to comment.