diff --git a/package-lock.json b/package-lock.json index 73b29b9..2a43ffd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@tbd54566975/dwn-sdk-js": "0.2.4", "@tbd54566975/dwn-sql-store": "0.2.1", "better-sqlite3": "^8.5.0", + "body-parser": "^1.20.2", "bytes": "3.1.2", "cors": "2.8.5", "express": "4.18.2", @@ -2070,12 +2071,12 @@ "dev": true }, "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dependencies": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -2083,7 +2084,7 @@ "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", - "raw-body": "2.5.1", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -4292,6 +4293,29 @@ "node": ">= 0.10.0" } }, + "node_modules/express/node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -4305,6 +4329,20 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/express/node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/express/node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -8251,9 +8289,9 @@ } }, "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", diff --git a/package.json b/package.json index 91326c4..44a02ee 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@tbd54566975/dwn-sdk-js": "0.2.4", "@tbd54566975/dwn-sql-store": "0.2.1", "better-sqlite3": "^8.5.0", + "body-parser": "^1.20.2", "bytes": "3.1.2", "cors": "2.8.5", "express": "4.18.2", diff --git a/src/http-api.ts b/src/http-api.ts index d40d90e..57ddd1b 100644 --- a/src/http-api.ts +++ b/src/http-api.ts @@ -22,6 +22,7 @@ import { import { jsonRpcApi } from './json-rpc-api.js'; import { requestCounter, responseHistogram } from './metrics.js'; +import { getChallenge, verifyChallenge } from './pow.js'; export class HttpApi { #api: Express; @@ -47,6 +48,7 @@ export class HttpApi { #setupMiddleware(): void { this.#api.use(cors({ exposedHeaders: 'dwn-response' })); + this.#api.use(express.json()); this.#api.use( responseTime((req: Request, res: Response, time) => { @@ -181,6 +183,9 @@ export class HttpApi { return res.json(jsonRpcResponse); } }); + + this.#api.get('/register', getChallenge); + this.#api.post('/register', verifyChallenge); } #listen(port: number, callback?: () => void): void { diff --git a/src/pow.ts b/src/pow.ts new file mode 100644 index 0000000..782a0cc --- /dev/null +++ b/src/pow.ts @@ -0,0 +1,40 @@ +import { createHash } from 'crypto'; +import type { Request, Response } from 'express'; + +const outstandingHashes: { [challenge: string]: Date } = {}; + +export async function getChallenge( + _req: Request, + res: Response, +): Promise { + // sign a JWT with an expiration date shortly in the future (1-2 min) and a complexity + // make the complexity go up when get challenge requests increase in frequency + + res.status(500).json({ error: 'unimplemented' }); +} + +export async function verifyChallenge( + req: Request, + res: Response, +): Promise { + console.log(req.body); + const body: { + challenge: string; + response: string; + } = req.body; + + const hash = createHash('sha1'); + hash.update(body.challenge); + hash.update(body.response); + + const hex = hash.digest('hex'); + const complexity = Object.keys(outstandingHashes).length; + for (let i = 0; i < complexity; i++) { + if (hex[i] != '0') { + res.status(401).json({ success: false }); + return; + } + } + + res.json({ success: true }); +}