@tomo-inc/cubist-wallet-sdk 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # @tomo-inc/cubist-wallet-sdk
2
+
3
+ ## 0.0.1
4
+
5
+ ## 0.0.2
6
+
7
+ 1.passkey relay add origin for postMessage
8
+
9
+ ## 0.0.3
10
+
11
+ 1. passkey relay in rn
package/README.md ADDED
@@ -0,0 +1,14 @@
1
+ # @tomo-inc/cubist-wallet-sdk
2
+
3
+ ## support
4
+
5
+ 1. new user register
6
+ 2. user login: get tomoToken + accountAddressList
7
+ 3. mfa: config + approval
8
+ 4. seed phrase export
9
+
10
+ ## todo
11
+
12
+ 1. passkey + rpId
13
+ 2. integrate more oidc provider
14
+ 3. session manage + keep login status
package/RN.md ADDED
@@ -0,0 +1,54 @@
1
+ # Use passkeyRelay in RN
2
+
3
+ ## passkeyRelay compatible
4
+
5
+ 1. waiting for passkeyRelay info
6
+
7
+ ```
8
+ const onMessage = (event) => {
9
+ const data = JSON.parse(event.nativeEvent.data);
10
+
11
+ if (data.type === 'OPEN_PASSKEY_RELAY') {
12
+ const { url, name } = data.message;
13
+ openPasskeyModal(url);
14
+ }
15
+ };
16
+ ```
17
+
18
+ 2. open passkeyRelay url in RN
19
+
20
+ > origin not allow \*
21
+
22
+ ```
23
+ const openPasskeyModal = (url) => {
24
+ //just for example
25
+ this.setState({
26
+ showPasskeyModal: true,
27
+ passkeyUrl: `${url}?origin=${origin}`
28
+ });
29
+ };
30
+ ```
31
+
32
+ 3. waiting for passkeyRelay result
33
+
34
+ ```
35
+ const onMessage = (event) => {
36
+ const data = JSON.parse(event.nativeEvent.data);
37
+
38
+ if(data.type === "PASSKEY_RELAY_MESSAGE" && event.origin === origin){
39
+ const result = data.message;
40
+ }
41
+ };
42
+ ```
43
+
44
+ ## approvalFido
45
+
46
+ ```
47
+ const receipt = await cubeMfa.approvalFido(mfaId: string);
48
+ ```
49
+
50
+ ## addFido
51
+
52
+ ```
53
+ const res = await cubeMfa.addFido(fidoName, receipt);
54
+ ```
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@tomo-inc/cubist-wallet-sdk",
3
+ "version": "0.0.4",
4
+ "author": "tomo.inc",
5
+ "license": "MIT",
6
+ "private": false,
7
+ "type": "module",
8
+ "main": "./dist/index.cjs",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js",
15
+ "require": "./dist/index.cjs"
16
+ }
17
+ },
18
+ "dependencies": {
19
+ "@cubist-labs/cubesigner-sdk": "0.4.98-0",
20
+ "@tomo-inc/cubist-sig-sdk": "1.1.0",
21
+ "axios": "^1.11.0",
22
+ "crypto-js": "^4.2.0",
23
+ "@tomo-inc/wallet-utils": "0.0.2"
24
+ },
25
+ "devDependencies": {
26
+ "@types/crypto-js": "^4.2.2",
27
+ "@types/node": "^20.0.0",
28
+ "@types/supertest": "^2.0.12",
29
+ "supertest": "^6.3.0",
30
+ "tsup": "^8.0.0",
31
+ "tsx": "^4.19.2",
32
+ "typescript": "^5.0.0",
33
+ "@vitest/browser": "^3.2.4",
34
+ "playwright": "^1.44.1",
35
+ "vitest": "^3.2.4"
36
+ },
37
+ "scripts": {
38
+ "build": "tsup"
39
+ }
40
+ }
package/project.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "cubist-wallet-sdk",
3
+ "sourceRoot": "packages/cubist-wallet-sdk/src",
4
+ "projectType": "library",
5
+ "targets": {
6
+ "build": {
7
+ "executor": "nx:run-commands",
8
+ "outputs": ["{projectRoot}/dist"],
9
+ "options": {
10
+ "command": "tsup src/index.ts --format esm,cjs --dts --treeshake",
11
+ "cwd": "packages/cubist-wallet-sdk"
12
+ }
13
+ },
14
+ "dev": {
15
+ "executor": "nx:run-commands",
16
+ "options": {
17
+ "command": "tsup src/index.ts --format esm,cjs --watch --dts",
18
+ "cwd": "packages/cubist-wallet-sdk"
19
+ }
20
+ },
21
+ "lint": {
22
+ "executor": "nx:run-commands",
23
+ "options": {
24
+ "command": "eslint src/**/*.ts",
25
+ "cwd": "packages/cubist-wallet-sdk"
26
+ }
27
+ },
28
+ "lint:fix": {
29
+ "executor": "nx:run-commands",
30
+ "options": {
31
+ "command": "eslint src/**/*.ts --fix",
32
+ "cwd": "packages/cubist-wallet-sdk"
33
+ }
34
+ },
35
+ "format": {
36
+ "executor": "nx:run-commands",
37
+ "options": {
38
+ "command": "prettier --write \"src/**/*.{ts,tsx}\"",
39
+ "cwd": "packages/cubist-wallet-sdk"
40
+ }
41
+ },
42
+ "test": {
43
+ "executor": "nx:run-commands",
44
+ "outputs": ["{projectRoot}/coverage"],
45
+ "options": {
46
+ "command": "vitest run",
47
+ "cwd": "packages/cubist-wallet-sdk"
48
+ }
49
+ },
50
+ "test:watch": {
51
+ "executor": "nx:run-commands",
52
+ "options": {
53
+ "command": "vitest",
54
+ "cwd": "packages/cubist-wallet-sdk"
55
+ }
56
+ }
57
+ },
58
+ "tags": ["npm:private", "scope:cubist-wallet-sdk", "type:library"]
59
+ }
@@ -0,0 +1,64 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { CubeMfaService } from "../cube-mfa";
3
+
4
+ describe("CubeMfaService", () => {
5
+ describe("extractSecretFromOtpUri", () => {
6
+ it("should extract secret from valid OTP URI", () => {
7
+ const otpUri = "otpauth://totp/productName:?secret=6JOGJWLATLOOJB7EFULZF73DDLQGUE4F&issuer=productName";
8
+ const secret = CubeMfaService.extractSecretFromOtpUri(otpUri);
9
+ expect(secret).toBe("6JOGJWLATLOOJB7EFULZF73DDLQGUE4F");
10
+ });
11
+
12
+ it("should extract secret from OTP URI with different format", () => {
13
+ const otpUri = "otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example";
14
+ const secret = CubeMfaService.extractSecretFromOtpUri(otpUri);
15
+ expect(secret).toBe("JBSWY3DPEHPK3PXP");
16
+ });
17
+
18
+ it("should extract secret from OTP URI with only secret parameter", () => {
19
+ const otpUri = "otpauth://totp/productName:?secret=ABCDEFGHIJKLMNOP";
20
+ const secret = CubeMfaService.extractSecretFromOtpUri(otpUri);
21
+ expect(secret).toBe("ABCDEFGHIJKLMNOP");
22
+ });
23
+
24
+ it("should return null for invalid OTP URI", () => {
25
+ const otpUri = "https://example.com?secret=123";
26
+ const secret = CubeMfaService.extractSecretFromOtpUri(otpUri);
27
+ expect(secret).toBeNull();
28
+ });
29
+
30
+ it("should return null for OTP URI without secret parameter", () => {
31
+ const otpUri = "otpauth://totp/productName:?issuer=productName";
32
+ const secret = CubeMfaService.extractSecretFromOtpUri(otpUri);
33
+ expect(secret).toBeNull();
34
+ });
35
+
36
+ it("should return null for empty string", () => {
37
+ const secret = CubeMfaService.extractSecretFromOtpUri("");
38
+ expect(secret).toBeNull();
39
+ });
40
+
41
+ it("should return null for null input", () => {
42
+ const secret = CubeMfaService.extractSecretFromOtpUri(null as any);
43
+ expect(secret).toBeNull();
44
+ });
45
+
46
+ it("should return null for undefined input", () => {
47
+ const secret = CubeMfaService.extractSecretFromOtpUri(undefined as any);
48
+ expect(secret).toBeNull();
49
+ });
50
+
51
+ it("should handle malformed URI with regex fallback", () => {
52
+ const otpUri = "otpauth://totp/productName:?secret=6JOGJWLATLOOJB7EFULZF73DDLQGUE4F&issuer=productName";
53
+ const secret = CubeMfaService.extractSecretFromOtpUri(otpUri);
54
+ expect(secret).toBe("6JOGJWLATLOOJB7EFULZF73DDLQGUE4F");
55
+ });
56
+
57
+ it("should handle URI with multiple parameters", () => {
58
+ const otpUri =
59
+ "otpauth://totp/productName:?algorithm=SHA1&digits=6&period=30&secret=6JOGJWLATLOOJB7EFULZF73DDLQGUE4F&issuer=productName";
60
+ const secret = CubeMfaService.extractSecretFromOtpUri(otpUri);
61
+ expect(secret).toBe("6JOGJWLATLOOJB7EFULZF73DDLQGUE4F");
62
+ });
63
+ });
64
+ });
package/src/api.ts ADDED
@@ -0,0 +1,64 @@
1
+ import { TomoApiDomains } from "@tomo-inc/wallet-utils";
2
+ import axios from "axios";
3
+ import { generateCubeSignature } from "./signature";
4
+ import { CubeConfig } from "./types";
5
+
6
+ function getTomoApi(config: CubeConfig) {
7
+ const { tomoStage = "dev", tomoClientId, jwtToken } = config;
8
+ const tomoApi = axios.create({
9
+ baseURL: `${TomoApiDomains[tomoStage] || TomoApiDomains.dev}/user`,
10
+ timeout: 20000,
11
+ });
12
+
13
+ tomoApi.interceptors.request.use(
14
+ async (config) => {
15
+ config.headers["client-id"] = tomoClientId;
16
+ if (jwtToken) {
17
+ config.headers["Authorization"] = `Bearer ${jwtToken}`;
18
+ }
19
+ return config;
20
+ },
21
+ (error) => {
22
+ if (error?.response?.status === 401) {
23
+ return Promise.reject(error);
24
+ }
25
+ },
26
+ );
27
+
28
+ return tomoApi;
29
+ }
30
+
31
+ export const addUserToCube = async (req: any, config: CubeConfig) => {
32
+ // sign for api
33
+ const { iss, sub } = req;
34
+ const cubeSalt = config.cubeSalt || "";
35
+ if (!cubeSalt) {
36
+ throw new Error("tomoStage error.");
37
+ }
38
+ const { timestamp, signature } = await generateCubeSignature({ iss, sub }, cubeSalt);
39
+ const signedReq = { ...req, timestamp, signature };
40
+
41
+ // send signatrue
42
+ const tomoApi = getTomoApi(config);
43
+ const res = await tomoApi.post("/api/login/loginByCubistV2", signedReq);
44
+ return res?.data?.data || {};
45
+ };
46
+
47
+ export const updateUserInfo = async (
48
+ userInfo: { nickname: string; avatar: string },
49
+ config: CubeConfig,
50
+ ): Promise<boolean> => {
51
+ // send signatrue
52
+ const tomoApi = getTomoApi(config);
53
+
54
+ const res = await tomoApi.post("/api/user/getUserInfo");
55
+ const userInfoBefore = res?.data?.data.user || {};
56
+
57
+ const nickname = userInfo.nickname.trim();
58
+ userInfo = { ...userInfoBefore, ...userInfo, nickname, username: nickname };
59
+
60
+ const result = await tomoApi.post("/api/setting/updateUserInfo", userInfo, {
61
+ headers: {},
62
+ });
63
+ return !!result?.data?.success;
64
+ };
package/src/const.ts ADDED
@@ -0,0 +1,96 @@
1
+ import { CubeStage } from "@tomo-inc/wallet-utils";
2
+ import { AccountType, BizType } from "types";
3
+
4
+ // export const CUBE_RP_ID = "cubist.dev";
5
+ export const SEED_PHRASE_EXPORT_DURATION = 48; //hour
6
+ export const EXPORT_TIME_FORMAT = "yyyy-MM-dd hh:mm:ss";
7
+ export const OIDC_TOKEN_LIFETIME = 30 * 24 * 60 * 60; //seconds
8
+ export const CUBE_LIFETIME = {
9
+ auth: OIDC_TOKEN_LIFETIME,
10
+ refresh: OIDC_TOKEN_LIFETIME * 2,
11
+ };
12
+
13
+ export const CUBE_SCOPES = [
14
+ "sign:*",
15
+ "export:*",
16
+ "manage:session:extend",
17
+
18
+ "manage:mfa:*",
19
+ "manage:mfa:register:email",
20
+ "manage:mfa:register:fido",
21
+ "manage:mfa:unregister:fido",
22
+ "manage:mfa:register:totp",
23
+ "manage:mfa:unregister:totp",
24
+
25
+ "manage:export:user:delete",
26
+ "manage:export:user:list",
27
+ ];
28
+
29
+ export const EXPORT_KEY_PREFIX = "Key#Mnemonic_";
30
+
31
+ export const ISS_MAP: Record<string, AccountType> = {
32
+ "https://accounts.google.com": "google",
33
+ "https://shim.oauth2.cubist.dev/twitter": "x",
34
+ };
35
+
36
+ export const CUBE_SALTS = {
37
+ prod: "xnPWRJT5XG2WyevuydMjMpZq",
38
+ pre: "xnPWRJT5XG2WyevuydMjMpZq",
39
+ dev: "dev@tomo",
40
+ };
41
+
42
+ export const getMfaInfoConfig = (cubeStage: CubeStage) => {
43
+ const lastTime = cubeStage === "prod" ? 172800 : 300;
44
+ const AllowedMfaTypes = {
45
+ Login: ["Fido", "EmailOtp", "Totp"],
46
+ AddIdentity: ["Fido", "EmailOtp", "Totp"],
47
+ RegisterMfa: ["Fido", `EmailOtp#${lastTime}`, "Totp"],
48
+ Export: ["Fido", "Totp"],
49
+ };
50
+
51
+ //bindSocailAccount: = ["Fido", "EmailOtp", "Totp"]
52
+ const MfaRequestConfig: Record<BizType, { method: string; path: string; _allowed_mfa_types: string[] }> = {
53
+ createSession: {
54
+ method: "POST",
55
+ path: "/session/",
56
+ _allowed_mfa_types: AllowedMfaTypes.Login,
57
+ },
58
+ addFido: {
59
+ method: "POST",
60
+ path: "/user/me/fido/",
61
+ _allowed_mfa_types: AllowedMfaTypes.RegisterMfa,
62
+ },
63
+ deleteFido: {
64
+ method: "DELETE",
65
+ path: "/user/me/fido/",
66
+ _allowed_mfa_types: AllowedMfaTypes.RegisterMfa,
67
+ },
68
+ registerTotp: {
69
+ method: "POST",
70
+ path: "/user/me/totp/",
71
+ _allowed_mfa_types: AllowedMfaTypes.RegisterMfa,
72
+ },
73
+ deleteTotp: {
74
+ method: "DELETE",
75
+ path: "/user/me/totp/",
76
+ _allowed_mfa_types: AllowedMfaTypes.RegisterMfa,
77
+ },
78
+ registerEmailOtp: {
79
+ method: "POST",
80
+ path: "/user/me/email/",
81
+ _allowed_mfa_types: AllowedMfaTypes.RegisterMfa,
82
+ },
83
+ initExport: {
84
+ method: "POST",
85
+ path: "/user/me/export/",
86
+ _allowed_mfa_types: AllowedMfaTypes.Export,
87
+ },
88
+ completeExport: {
89
+ method: "PATCH",
90
+ path: "/user/me/export/",
91
+ _allowed_mfa_types: AllowedMfaTypes.Export,
92
+ },
93
+ };
94
+
95
+ return MfaRequestConfig;
96
+ };