diff --git a/package-lock.json b/package-lock.json index fd45e99..7885685 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,9 +17,13 @@ "axios": "^1.7.2", "big.js": "^6.2.1", "decimal.js": "^10.4.3", + "node-forge": "^1.3.1", "react": "^18.3.1", "typescript": "^5.5.4", "uuid": "^10.0.0" + }, + "devDependencies": { + "@types/node-forge": "^1.3.11" } }, "node_modules/@types/big.js": { @@ -35,6 +39,15 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/prop-types": { "version": "15.7.12", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", @@ -180,6 +193,14 @@ "node": ">= 0.6" } }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -241,6 +262,15 @@ "undici-types": "~5.26.4" } }, + "@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/prop-types": { "version": "15.7.12", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", @@ -344,6 +374,11 @@ "mime-db": "1.52.0" } }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" + }, "proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", diff --git a/package.json b/package.json index f0a6e69..a9e230e 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "axios": "^1.7.2", "big.js": "^6.2.1", "decimal.js": "^10.4.3", + "node-forge": "^1.3.1", "react": "^18.3.1", "typescript": "^5.5.4", "uuid": "^10.0.0" @@ -55,5 +56,8 @@ "types": "./shared/dist/index.d.ts" } }, - "homepage": "https://github.com/hyle-team/zano_web3#readme" + "homepage": "https://github.com/hyle-team/zano_web3#readme", + "devDependencies": { + "@types/node-forge": "^1.3.11" + } } diff --git a/server/src/server.ts b/server/src/server.ts index 3815885..adaef55 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -12,10 +12,12 @@ import { import { ZANO_ASSET_ID, ZanoError } from "./utils"; import { APIAsset, APIBalance } from "./types"; +import forge from "node-forge"; interface ConstructorParams { walletUrl: string; daemonUrl: string; + walletAuthToken?: string; } interface GetTxsParams { @@ -27,18 +29,71 @@ interface GetTxsParams { update_provision_info?: boolean; } +interface JWTPayload { + body_hash: string, + user: string, + salt: string, + exp: number +} + class ServerWallet { private walletUrl: string; private daemonUrl: string; + private walletAuthToken: string; constructor(params: ConstructorParams) { this.walletUrl = params.walletUrl; this.daemonUrl = params.daemonUrl; + this.walletAuthToken = params.walletAuthToken || ""; } - private async fetchDaemon(method: string, params: any) { - const headers = { "Content-Type": "application/json" }; + private generateRandomString(length: number) { + const bytes = forge.random.getBytesSync(Math.ceil(length / 2)); + const hexString = forge.util.bytesToHex(bytes); + return hexString.substring(0, length); + } + private createJWSToken(payload: JWTPayload, secretStr: string): string { + const header = { alg: "HS256", typ: "JWT" }; + const encodedHeader = Buffer.from(JSON.stringify(header)) + .toString("base64") + .replace(/=/g, ""); + const encodedPayload = Buffer.from(JSON.stringify(payload)) + .toString("base64") + .replace(/=/g, ""); + + const signature = forge.hmac.create(); + signature.start("sha256", secretStr); + signature.update(`${encodedHeader}.${encodedPayload}`); + const encodedSignature = forge.util + .encode64(signature.digest().getBytes()) + .replace(/=/g, ""); + + return `${encodedHeader}.${encodedPayload}.${encodedSignature}`; + } + + + private generateAccessToken(httpBody: string) { + // Calculate the SHA-256 hash of the HTTP body + const md = forge.md.sha256.create(); + md.update(httpBody); + const bodyHash = md.digest().toHex(); + + // Example payload + const payload = { + body_hash: bodyHash, + user: "zano_extension", + salt: this.generateRandomString(64), + exp: Math.floor(Date.now() / 1000) + 60, // Expires in 1 minute + }; + + return this.createJWSToken(payload, this.walletAuthToken); + } + + + private async fetchDaemon(method: string, params: any) { + + const data = { jsonrpc: "2.0", id: 0, @@ -46,11 +101,15 @@ class ServerWallet { params: params, }; + const headers = { + "Content-Type": "application/json", + "Zano-Access-Token": this.generateAccessToken(JSON.stringify(data)), + }; + return axios.post(this.daemonUrl, data, { headers }); } private async fetchWallet(method: string, params: any) { - const headers = { "Content-Type": "application/json" }; const data = { jsonrpc: "2.0", @@ -59,6 +118,11 @@ class ServerWallet { params: params, }; + const headers = { + "Content-Type": "application/json", + "Zano-Access-Token": this.generateAccessToken(JSON.stringify(data)), + }; + return axios.post(this.walletUrl, data, { headers }); }