Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

master merge- openloginv v6 #447

Merged
merged 20 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,304 changes: 980 additions & 1,324 deletions package-lock.json

Large diffs are not rendered by default.

105 changes: 53 additions & 52 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",
"@toruslabs/broadcast-channel": "^8.0.0",
"@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 @@

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 @@
}
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
Loading