diff options
| author | Pawel Zelawski <pawel@pzelawski.com> | 2026-05-23 10:33:53 +0200 |
|---|---|---|
| committer | Pawel Zelawski <pawel@pzelawski.com> | 2026-05-23 10:33:53 +0200 |
| commit | 376feecb280c28504788c9677c6cb3cc455f00b6 (patch) | |
| tree | ebf3ee7ac5e67560a98f51e3815f9b7a414734df | |
| parent | eafdcd8290700fdeaa6b069c4d52e50a9db6ad94 (diff) | |
chore: upgrade digiid-ts to v3 and stabilize dev/build scripts
| -rw-r--r-- | .eslintrc.cjs | 8 | ||||
| -rw-r--r-- | .nvmrc | 1 | ||||
| -rw-r--r-- | README.md | 4 | ||||
| -rw-r--r-- | package-lock.json | 446 | ||||
| -rw-r--r-- | package.json | 18 | ||||
| -rw-r--r-- | src/client/App.tsx | 103 | ||||
| -rw-r--r-- | src/client/main.tsx | 2 | ||||
| -rw-r--r-- | src/client/utils.ts | 19 | ||||
| -rw-r--r-- | src/server/main.ts | 316 | ||||
| -rw-r--r-- | src/server/utils.ts | 10 |
10 files changed, 712 insertions, 215 deletions
diff --git a/.eslintrc.cjs b/.eslintrc.cjs index e7f58a7..2a228e9 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -10,13 +10,9 @@ module.exports = { ], ignorePatterns: ['dist', '.eslintrc.cjs', 'vite.config.ts', 'node_modules'], parser: '@typescript-eslint/parser', - plugins: ['react-refresh', '@typescript-eslint', 'prettier'], + plugins: ['@typescript-eslint', 'prettier'], rules: { 'prettier/prettier': 'warn', // Show Prettier issues as warnings - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, - ], '@typescript-eslint/no-unused-vars': [ 'warn', { @@ -32,4 +28,4 @@ module.exports = { version: 'detect' // Automatically detect the React version } } -};
\ No newline at end of file +}; @@ -0,0 +1 @@ +20.19.0 @@ -36,7 +36,7 @@ digiid-ts-demo/ ### Prerequisites -- Node.js (v16 or higher) +- Node.js (v20.19.0 or higher) - npm or yarn - A DigiByte wallet that supports Digi-ID (e.g., DigiByte Go) @@ -173,4 +173,4 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file ## Acknowledgments - [DigiByte](https://www.digibyte.org/) - The underlying blockchain technology -- [digiid-ts](https://github.com/pawelzelawski/digiid-ts) - The TypeScript library for Digi-ID authentication
\ No newline at end of file +- [digiid-ts](https://github.com/pawelzelawski/digiid-ts) - The TypeScript library for Digi-ID authentication diff --git a/package-lock.json b/package-lock.json index c037956..5303845 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0", "license": "ISC", "dependencies": { - "digiid-ts": "^2.0.4", + "digiid-ts": "^3.0.0", "dotenv": "^16.4.5", "express": "^4.19.2", "qrcode": "^1.5.3", @@ -31,12 +31,17 @@ "eslint-plugin-prettier": "^5.2.6", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.5.2", "nodemon": "^3.1.9", "npm-run-all": "^4.1.5", "prettier": "^3.5.3", "ts-node": "^10.9.2", "typescript": "^5.8.3", - "vite": "^6.2.6" + "vite": "^6.2.6", + "vitest": "^4.1.7" + }, + "engines": { + "node": ">=20.19.0" } }, "node_modules/@ampproject/remapping": { @@ -1088,9 +1093,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true, "license": "MIT" }, @@ -1106,12 +1111,12 @@ } }, "node_modules/@noble/curves": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-2.0.1.tgz", - "integrity": "sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-2.2.0.tgz", + "integrity": "sha512-T/BoHgFXirb0ENSPBquzX0rcjXeM6Lo892a2jlYJkqk83LqZx0l1Of7DzlKJ6jkpvMrkHSnAcgb5JegL8SeIkQ==", "license": "MIT", "dependencies": { - "@noble/hashes": "2.0.1" + "@noble/hashes": "2.2.0" }, "engines": { "node": ">= 20.19.0" @@ -1120,25 +1125,13 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@noble/curves/node_modules/@noble/hashes": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz", - "integrity": "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==", - "license": "MIT", - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.2.0.tgz", + "integrity": "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==", "license": "MIT", "engines": { - "node": "^14.21.3 || >=16" + "node": ">= 20.19.0" }, "funding": { "url": "https://paulmillr.com/funding/" @@ -1545,6 +1538,13 @@ "win32" ] }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", @@ -1629,6 +1629,17 @@ "@types/node": "*" } }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -1639,6 +1650,13 @@ "@types/node": "*" } }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/dotenv": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-6.1.1.tgz", @@ -1992,6 +2010,119 @@ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" } }, + "node_modules/@vitest/expect": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.7.tgz", + "integrity": "sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.7", + "@vitest/utils": "4.1.7", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.7.tgz", + "integrity": "sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.7", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.7.tgz", + "integrity": "sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.7.tgz", + "integrity": "sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.7", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.7.tgz", + "integrity": "sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.7", + "@vitest/utils": "4.1.7", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.7.tgz", + "integrity": "sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.7.tgz", + "integrity": "sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.7", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -2252,6 +2383,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", @@ -2519,6 +2660,16 @@ ], "license": "CC-BY-4.0" }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2836,16 +2987,16 @@ } }, "node_modules/digiid-ts": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/digiid-ts/-/digiid-ts-2.0.4.tgz", - "integrity": "sha512-CLdU8MNFXG6MM+iZNgOVLxTq4rv+NZvuvemuJfvwJVi6hXspaysGljVrJW9G77xG/uHKZ91aKm3nDIsU6kkpGg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/digiid-ts/-/digiid-ts-3.0.0.tgz", + "integrity": "sha512-bSqW3bRoN7MekkAaXW21m7X/x94YDg+bWflRG2dzpTUh+RHVrZ9gF9UkHR1j7mjmdthCRgJqVtKQ3yZYh0Ahng==", "license": "MIT", "dependencies": { - "@noble/curves": "^2.0.1", - "@noble/hashes": "^1.8.0" + "@noble/curves": "^2.2.0", + "@noble/hashes": "^2.2.0" }, "engines": { - "node": ">=16.0.0" + "node": ">=20.19.0" } }, "node_modules/dijkstrajs": { @@ -3043,6 +3194,13 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -3322,6 +3480,16 @@ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.2.tgz", + "integrity": "sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": "^9 || ^10" + } + }, "node_modules/eslint-plugin-react/node_modules/brace-expansion": { "version": "1.1.14", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", @@ -3490,6 +3658,16 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -3509,6 +3687,16 @@ "node": ">= 0.6" } }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/express": { "version": "4.22.1", "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", @@ -4778,6 +4966,16 @@ "yallist": "^3.0.2" } }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -5396,6 +5594,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -5566,6 +5775,13 @@ "node": ">=4" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -6416,6 +6632,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/simple-update-notifier": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", @@ -6475,6 +6698,13 @@ "dev": true, "license": "CC0-1.0" }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -6484,6 +6714,13 @@ "node": ">= 0.8" } }, + "node_modules/std-env": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "dev": true, + "license": "MIT" + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -6693,6 +6930,23 @@ "url": "https://opencollective.com/synckit" } }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", + "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -6741,6 +6995,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -7180,6 +7444,109 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/vitest": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.7.tgz", + "integrity": "sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.7", + "@vitest/mocker": "4.1.7", + "@vitest/pretty-format": "4.1.7", + "@vitest/runner": "4.1.7", + "@vitest/snapshot": "4.1.7", + "@vitest/spy": "4.1.7", + "@vitest/utils": "4.1.7", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.7", + "@vitest/browser-preview": "4.1.7", + "@vitest/browser-webdriverio": "4.1.7", + "@vitest/coverage-istanbul": "4.1.7", + "@vitest/coverage-v8": "4.1.7", + "@vitest/ui": "4.1.7", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -7291,6 +7658,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", diff --git a/package.json b/package.json index ec37a87..ae6708e 100644 --- a/package.json +++ b/package.json @@ -5,13 +5,14 @@ "description": "", "type": "module", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", + "test": "vitest run --passWithNoTests", "dev:frontend": "vite", + "build:backend": "tsc -p tsconfig.json", "build:backend:watch": "tsc -p tsconfig.json --watch", "dev:backend": "nodemon dist/server/server/main.js", - "dev": "npm-run-all --parallel dev:frontend build:backend:watch dev:backend", - "build": "tsc && vite build", - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "dev": "npm-run-all build:backend --parallel dev:frontend build:backend:watch dev:backend", + "build": "vite build && npm run build:backend", + "lint": "ESLINT_USE_FLAT_CONFIG=false eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview" }, "repository": { @@ -25,6 +26,9 @@ "url": "https://github.com/pawelzelawski/digiid-ts-demo/issues" }, "homepage": "https://github.com/pawelzelawski/digiid-ts-demo#readme", + "engines": { + "node": ">=20.19.0" + }, "devDependencies": { "@types/dotenv": "^6.1.1", "@types/express": "^5.0.1", @@ -40,15 +44,17 @@ "eslint-plugin-prettier": "^5.2.6", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.5.2", "nodemon": "^3.1.9", "npm-run-all": "^4.1.5", "prettier": "^3.5.3", "ts-node": "^10.9.2", "typescript": "^5.8.3", - "vite": "^6.2.6" + "vite": "^6.2.6", + "vitest": "^4.1.7" }, "dependencies": { - "digiid-ts": "^2.0.4", + "digiid-ts": "^3.0.0", "dotenv": "^16.4.5", "express": "^4.19.2", "qrcode": "^1.5.3", diff --git a/src/client/App.tsx b/src/client/App.tsx index b3f0308..ad1e230 100644 --- a/src/client/App.tsx +++ b/src/client/App.tsx @@ -7,7 +7,7 @@ type UiState = 'initial' | 'waiting' | 'success' | 'failed'; // Define the structure for result data (success or failure) interface ResultData { address?: string; // Present on success - error?: string; // Present on failure + error?: string; // Present on failure addressType?: string; // Added later } @@ -30,7 +30,7 @@ function App() { } console.log(`Starting status polling for session: ${sessionId}`); - let intervalId: any = null; // Use 'any' to avoid browser/node type conflict for setInterval return type + let intervalId: ReturnType<typeof setInterval> | null = null; const checkStatus = async () => { if (!sessionId) return; // Should not happen here, but type guard @@ -41,19 +41,26 @@ function App() { if (!response.ok) { // Handle specific errors like 404 (session not found/expired) if (response.status === 404) { - console.warn(`Session ${sessionId} not found or expired during polling.`); + console.warn( + `Session ${sessionId} not found or expired during polling.` + ); setError('Session expired or could not be found.'); setUiState('failed'); // Transition to failed state setResultData({ error: 'Session expired or could not be found.' }); } else { - const errorData = await response.json().catch(() => ({ message: 'Error fetching status' })); - throw new Error(errorData.message || `Server responded with ${response.status}`); + const errorData = await response + .json() + .catch(() => ({ message: 'Error fetching status' })); + throw new Error( + errorData.message || `Server responded with ${response.status}` + ); } if (intervalId) clearInterval(intervalId); // Stop polling on error return; } - const data: { status: UiState, address?: string, error?: string } = await response.json(); + const data: { status: UiState; address?: string; error?: string } = + await response.json(); console.log('Received status data:', data); // If status changed from pending, update UI and stop polling @@ -67,10 +74,12 @@ function App() { if (intervalId) clearInterval(intervalId); } // If status is still 'pending', the interval will continue - } catch (err) { console.error('Error polling status:', err); - const message = err instanceof Error ? err.message : 'An unknown error occurred during status check'; + const message = + err instanceof Error + ? err.message + : 'An unknown error occurred during status check'; setError(`Status polling failed: ${message}`); // Decide if we should stop polling or transition state on generic fetch error // For now, let's stop polling and show error, moving to failed state @@ -104,7 +113,11 @@ function App() { try { const response = await fetch('/api/digiid/start'); if (!response.ok) { - const errorData = await response.json().catch(() => ({ message: 'Failed to start session. Server responded with status: ' + response.status })); + const errorData = await response.json().catch(() => ({ + message: + 'Failed to start session. Server responded with status: ' + + response.status, + })); throw new Error(errorData.message || 'Failed to start session'); } const data = await response.json(); @@ -114,7 +127,8 @@ function App() { setUiState('waiting'); // Move to waiting state } catch (err) { console.error('Error starting Digi-ID session:', err); - const message = err instanceof Error ? err.message : 'An unknown error occurred'; + const message = + err instanceof Error ? err.message : 'An unknown error occurred'; setError(`Failed to initiate Digi-ID: ${message}`); setUiState('initial'); // Stay in initial state on error } finally { @@ -140,9 +154,20 @@ function App() { {/* --- Views based on uiState --- */} {uiState === 'initial' && ( <div className="initial-view"> - <button onClick={handleStart} disabled={isLoading} className="signin-button"> - <img src="/assets/digiid-logo.png" alt="Digi-ID Logo" width="24" height="24" /> - <span>{isLoading ? 'Generating QR...' : 'Sign in with Digi-ID'}</span> + <button + onClick={handleStart} + disabled={isLoading} + className="signin-button" + > + <img + src="/assets/digiid-logo.png" + alt="Digi-ID Logo" + width="24" + height="24" + /> + <span> + {isLoading ? 'Generating QR...' : 'Sign in with Digi-ID'} + </span> </button> </div> )} @@ -152,7 +177,10 @@ function App() { {uiState === 'waiting' && qrCodeDataUrl && ( <div className="waiting-view view-box"> <h2>Scan the QR Code</h2> - <p>Scan the QR code below using your Digi-ID compatible mobile wallet.</p> + <p> + Scan the QR code below using your Digi-ID compatible mobile + wallet. + </p> <img src={qrCodeDataUrl} alt="Digi-ID QR Code" width="250" /> <p>Waiting for authentication...</p> <button onClick={handleReset}>Cancel</button> @@ -161,14 +189,30 @@ function App() { {uiState === 'success' && resultData?.address && ( <div className="success-view view-box"> - <svg xmlns="http://www.w3.org/2000/svg" className="checkmark-icon" viewBox="0 0 52 52"> - <circle className="checkmark__circle" cx="26" cy="26" r="25" fill="none"/> - <path className="checkmark__check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8"/> + <svg + xmlns="http://www.w3.org/2000/svg" + className="checkmark-icon" + viewBox="0 0 52 52" + > + <circle + className="checkmark__circle" + cx="26" + cy="26" + r="25" + fill="none" + /> + <path + className="checkmark__check" + fill="none" + d="M14.1 27.2l7.1 7.2 16.7-16.8" + /> </svg> <h2>Authentication Successful!</h2> <p>Verified Address:</p> <p className="address">{resultData.address}</p> - <p>Address Format: {getDigiByteAddressType(resultData.address)}</p> + <p> + Address Format: {getDigiByteAddressType(resultData.address)} + </p> <button onClick={handleReset}>Start Over</button> </div> )} @@ -177,7 +221,8 @@ function App() { <div className="failed-view view-box"> <h2>Authentication Failed</h2> <p className="error-message"> - Reason: {resultData?.error || error || 'An unknown error occurred.'} + Reason:{' '} + {resultData?.error || error || 'An unknown error occurred.'} </p> <button onClick={handleReset}>Try Again</button> </div> @@ -188,14 +233,26 @@ function App() { {/* --- Description section (Always visible below the view container) --- */} <div className="description"> <p className="code-link"> - This application demonstrates integrating Digi-ID authentication using the <a href="https://github.com/pawelzelawski/digiid-ts" target="_blank" rel="noopener noreferrer">digiid-ts</a> library. + This application demonstrates integrating Digi-ID authentication using + the{' '} + <a + href="https://github.com/pawelzelawski/digiid-ts" + target="_blank" + rel="noopener noreferrer" + > + digiid-ts + </a>{' '} + library. </p> <p> - Upon successful verification, the system can identify the following DigiByte address formats: + Upon successful verification, the system can identify the following + DigiByte address formats: </p> <ul> <li>Legacy Addresses (P2PKH) - starting with 'D'</li> - <li>Pay-to-Script-Hash Addresses (P2SH) - commonly starting with 'S'</li> + <li> + Pay-to-Script-Hash Addresses (P2SH) - commonly starting with 'S' + </li> <li>Segregated Witness (SegWit) Addresses - starting with 'dgb1'</li> </ul> </div> @@ -203,4 +260,4 @@ function App() { ); } -export default App;
\ No newline at end of file +export default App; diff --git a/src/client/main.tsx b/src/client/main.tsx index b895c80..0896e5e 100644 --- a/src/client/main.tsx +++ b/src/client/main.tsx @@ -7,4 +7,4 @@ ReactDOM.createRoot(document.getElementById('root')!).render( <React.StrictMode> <App /> </React.StrictMode> -);
\ No newline at end of file +); diff --git a/src/client/utils.ts b/src/client/utils.ts index 6a95f7b..1677749 100644 --- a/src/client/utils.ts +++ b/src/client/utils.ts @@ -1,6 +1,10 @@ // Utility to determine DigiByte address type based on prefix -export type DigiByteAddressFormat = 'Legacy (P2PKH)' | 'Script (P2SH)' | 'SegWit (Bech32)' | 'Unknown'; +export type DigiByteAddressFormat = + | 'Legacy (P2PKH)' + | 'Script (P2SH)' + | 'SegWit (Bech32)' + | 'Unknown'; /** * Determines the format of a DigiByte address based on its prefix. @@ -11,21 +15,24 @@ export type DigiByteAddressFormat = 'Legacy (P2PKH)' | 'Script (P2SH)' | 'SegWit * @param address The DigiByte address string. * @returns The determined address format. */ -export function getDigiByteAddressType(address: string | undefined | null): DigiByteAddressFormat { +export function getDigiByteAddressType( + address: string | undefined | null +): DigiByteAddressFormat { if (!address) { return 'Unknown'; } if (address.startsWith('dgb1')) { return 'SegWit (Bech32)'; } - if (address.startsWith('S')) { // Common prefix for P2SH on DGB - return 'Script (P2SH)'; + if (address.startsWith('S')) { + // Common prefix for P2SH on DGB + return 'Script (P2SH)'; } if (address.startsWith('D')) { - return 'Legacy (P2PKH)'; + return 'Legacy (P2PKH)'; } // If it doesn't match known prefixes, return Unknown // Could potentially add DigiAsset checks here if they have distinct prefixes return 'Unknown'; -}
\ No newline at end of file +} diff --git a/src/server/main.ts b/src/server/main.ts index 96afb0e..1ab9004 100644 --- a/src/server/main.ts +++ b/src/server/main.ts @@ -28,157 +28,199 @@ console.log(`Attempting to listen on port: ${PORT}`); console.log(`Configured PUBLIC_URL: ${process.env.PUBLIC_URL}`); // Endpoint to initiate the DigiID authentication flow -app.get('/api/digiid/start', (req: Request, res: Response, next: NextFunction) => { - (async () => { - try { - const sessionId = randomBytes(16).toString('hex'); - const nonce = randomBytes(16).toString('hex'); - - const publicUrl = process.env.PUBLIC_URL; - if (!publicUrl) { - console.error('PUBLIC_URL environment variable is not set.'); - return res.status(500).json({ error: 'Server configuration error: PUBLIC_URL is missing.' }); - } - - let callbackUrl: string; +app.get( + '/api/digiid/start', + (req: Request, res: Response, next: NextFunction) => { + (async () => { try { - const baseUrl = new URL(publicUrl); - callbackUrl = new URL('/api/digiid/callback', baseUrl).toString(); + const sessionId = randomBytes(16).toString('hex'); + const nonce = randomBytes(16).toString('hex'); + + const publicUrl = process.env.PUBLIC_URL; + if (!publicUrl) { + console.error('PUBLIC_URL environment variable is not set.'); + return res.status(500).json({ + error: 'Server configuration error: PUBLIC_URL is missing.', + }); + } + + let callbackUrl: string; + try { + const baseUrl = new URL(publicUrl); + callbackUrl = new URL('/api/digiid/callback', baseUrl).toString(); + } catch (error) { + console.error('Invalid PUBLIC_URL format:', publicUrl, error); + return res.status(500).json({ + error: 'Server configuration error: Invalid PUBLIC_URL format.', + }); + } + + const unsecure = callbackUrl.startsWith('http://'); + // Use the actual function from digiid-ts + const digiIdUri = generateDigiIDUri({ callbackUrl, unsecure, nonce }); + console.log( + `Generated DigiID URI: ${digiIdUri} for session ${sessionId}` + ); + + sessionStore.set(sessionId, { nonce, status: 'pending' }); + nonceToSessionMap.set(nonce, sessionId); + console.log(`Stored pending session: ${sessionId}, nonce: ${nonce}`); + + const qrCodeDataUrl = await qrcode.toDataURL(digiIdUri); + res.json({ sessionId, qrCodeDataUrl }); } catch (error) { - console.error('Invalid PUBLIC_URL format:', publicUrl, error); - return res.status(500).json({ error: 'Server configuration error: Invalid PUBLIC_URL format.' }); + console.error('Error in /api/digiid/start:', error); + // Ensure response is sent even on error + if (!res.headersSent) { + // Check if response hasn't already been sent + if (error instanceof Error) { + res + .status(400) + .json({ error: `Failed to generate URI: ${error.message}` }); + } else { + res + .status(500) + .json({ error: 'Internal server error during start' }); + } + } } + })().catch(next); + } +); - const unsecure = callbackUrl.startsWith('http://'); - // Use the actual function from digiid-ts - const digiIdUri = generateDigiIDUri({ callbackUrl, unsecure, nonce }); - console.log(`Generated DigiID URI: ${digiIdUri} for session ${sessionId}`); +// Callback endpoint for the DigiID mobile app +app.post( + '/api/digiid/callback', + (req: Request, res: Response, next: NextFunction) => { + (async () => { + const { address, uri, signature } = req.body; + + // Basic validation of received data + if (!address || !uri || !signature) { + console.warn('Callback missing required fields.', { + address, + uri, + signature, + }); + // Wallet doesn't expect a body on failure, just non-200. Status only for logging/debug. + return res.status(400).send('Missing required callback parameters.'); + } - sessionStore.set(sessionId, { nonce, status: 'pending' }); - nonceToSessionMap.set(nonce, sessionId); - console.log(`Stored pending session: ${sessionId}, nonce: ${nonce}`); + const callbackData = { address, uri, signature }; + console.log('Received callback:', callbackData); - const qrCodeDataUrl = await qrcode.toDataURL(digiIdUri); - res.json({ sessionId, qrCodeDataUrl }); + // --- Nonce Extraction and Session Lookup --- + let receivedNonce: string | null = null; + try { + // DigiID URIs need scheme replaced for standard URL parsing + const parsableUri = uri.replace(/^digiid:/, 'http:'); + const parsedUri = new URL(parsableUri); + receivedNonce = parsedUri.searchParams.get('x'); + } catch (error) { + console.warn('Error parsing received URI:', uri, error); + return res.status(400).send('Invalid URI format.'); + } - } catch (error) { - console.error('Error in /api/digiid/start:', error); - // Ensure response is sent even on error - if (!res.headersSent) { // Check if response hasn't already been sent - if (error instanceof Error) { - res.status(400).json({ error: `Failed to generate URI: ${error.message}` }); - } else { - res.status(500).json({ error: 'Internal server error during start' }); - } + if (!receivedNonce) { + console.warn('Nonce (x parameter) not found in received URI:', uri); + return res.status(400).send('Nonce not found in URI.'); } - } - })().catch(next); -}); -// Callback endpoint for the DigiID mobile app -app.post('/api/digiid/callback', (req: Request, res: Response, next: NextFunction) => { - (async () => { - const { address, uri, signature } = req.body; - - // Basic validation of received data - if (!address || !uri || !signature) { - console.warn('Callback missing required fields.', { address, uri, signature }); - // Wallet doesn't expect a body on failure, just non-200. Status only for logging/debug. - return res.status(400).send('Missing required callback parameters.'); - } - - const callbackData = { address, uri, signature }; - console.log('Received callback:', callbackData); - - // --- Nonce Extraction and Session Lookup --- - let receivedNonce: string | null = null; - try { - // DigiID URIs need scheme replaced for standard URL parsing - const parsableUri = uri.replace(/^digiid:/, 'http:'); - const parsedUri = new URL(parsableUri); - receivedNonce = parsedUri.searchParams.get('x'); - } catch (error) { - console.warn('Error parsing received URI:', uri, error); - return res.status(400).send('Invalid URI format.'); - } - - if (!receivedNonce) { - console.warn('Nonce (x parameter) not found in received URI:', uri); - return res.status(400).send('Nonce not found in URI.'); - } - - const sessionId = nonceToSessionMap.get(receivedNonce); - if (!sessionId) { - console.warn('Session not found for received nonce:', receivedNonce); - // Nonce might be expired or invalid - return res.status(404).send('Session not found or expired for this nonce.'); - } - - // Retrieve the session *before* the try/finally block for verification - const session = sessionStore.get(sessionId); - if (!session) { - console.error(`Critical: Session data missing for ${sessionId} despite nonce match.`); - if (!res.headersSent) res.status(500).send('Internal server error: Session data missing.'); + const sessionId = nonceToSessionMap.get(receivedNonce); + if (!sessionId) { + console.warn('Session not found for received nonce:', receivedNonce); + // Nonce might be expired or invalid + return res + .status(404) + .send('Session not found or expired for this nonce.'); + } + + // Retrieve the session *before* the try/finally block for verification + const session = sessionStore.get(sessionId); + if (!session) { + console.error( + `Critical: Session data missing for ${sessionId} despite nonce match.` + ); + if (!res.headersSent) + res.status(500).send('Internal server error: Session data missing.'); return; // Explicitly return void - } - if (session.status !== 'pending') { + } + if (session.status !== 'pending') { console.warn('Session already processed:', sessionId, session.status); - if (!res.headersSent) res.status(200).send('Session already processed.'); + if (!res.headersSent) + res.status(200).send('Session already processed.'); + return; // Explicitly return void + } + + // --- Verification --- + let expectedCallbackUrl: string; + try { + const publicUrl = process.env.PUBLIC_URL; + if (!publicUrl) + throw new Error( + 'PUBLIC_URL environment variable is not configured on the server.' + ); + expectedCallbackUrl = new URL( + '/api/digiid/callback', + publicUrl + ).toString(); + } catch (error) { + console.error( + 'Server configuration error constructing expected callback URL:', + error + ); + session.status = 'failed'; + session.error = 'Server configuration error preventing verification.'; + sessionStore.set(sessionId, session); + // Respond 200 OK as per protocol + if (!res.headersSent) res.status(200).send(); return; // Explicitly return void - } - - // --- Verification --- - let expectedCallbackUrl: string; - try { - const publicUrl = process.env.PUBLIC_URL; - if (!publicUrl) throw new Error('PUBLIC_URL environment variable is not configured on the server.'); - expectedCallbackUrl = new URL('/api/digiid/callback', publicUrl).toString(); - } catch (error) { - console.error('Server configuration error constructing expected callback URL:', error); - session.status = 'failed'; - session.error = 'Server configuration error preventing verification.'; - sessionStore.set(sessionId, session); - // Respond 200 OK as per protocol - if (!res.headersSent) res.status(200).send(); - return; // Explicitly return void - } - - const verifyOptions = { expectedCallbackUrl, expectedNonce: session.nonce }; - - try { - console.log('Attempting to verify callback with:', { callbackData, verifyOptions }); - await verifyDigiIDCallback(callbackData, verifyOptions); - - // Success case (no throw from verifyDigiIDCallback) - console.log(`Verification successful for session ${sessionId}, address: ${address}`); - session.status = 'success'; - session.address = address; // Store the verified address - session.error = undefined; // Clear any previous error - nonceToSessionMap.delete(session.nonce); // Clean up nonce map only on success - - } catch (error) { - // Failure case (verifyDigiIDCallback threw an error) - console.warn(`Verification failed for session ${sessionId}:`, error); - session.status = 'failed'; - if (error instanceof Error) { - session.error = error.message; - } else { - session.error = 'An unknown verification error occurred.'; } - // Optionally cleanup nonce map on failure too, depending on policy - // nonceToSessionMap.delete(session.nonce); - } finally { - // Update store and respond 200 OK in all cases (after try/catch) - sessionStore.set(sessionId, session); - console.log(`Final session state for ${sessionId}:`, session); - // Ensure response is sent if not already done (e.g. in case of unexpected error before finally) - if (!res.headersSent) { + + const verifyOptions = { + expectedCallbackUrl, + expectedNonce: session.nonce, + }; + + try { + console.log('Attempting to verify callback with:', { + callbackData, + verifyOptions, + }); + await verifyDigiIDCallback(callbackData, verifyOptions); + + // Success case (no throw from verifyDigiIDCallback) + console.log( + `Verification successful for session ${sessionId}, address: ${address}` + ); + session.status = 'success'; + session.address = address; // Store the verified address + session.error = undefined; // Clear any previous error + nonceToSessionMap.delete(session.nonce); // Clean up nonce map only on success + } catch (error) { + // Failure case (verifyDigiIDCallback threw an error) + console.warn(`Verification failed for session ${sessionId}:`, error); + session.status = 'failed'; + if (error instanceof Error) { + session.error = error.message; + } else { + session.error = 'An unknown verification error occurred.'; + } + // Optionally cleanup nonce map on failure too, depending on policy + // nonceToSessionMap.delete(session.nonce); + } finally { + // Update store and respond 200 OK in all cases (after try/catch) + sessionStore.set(sessionId, session); + console.log(`Final session state for ${sessionId}:`, session); + // Ensure response is sent if not already done (e.g. in case of unexpected error before finally) + if (!res.headersSent) { res.status(200).send(); + } + // No explicit return needed here as it's the end of the function } - // No explicit return needed here as it's the end of the function - } - })().catch(next); -}); + })().catch(next); + } +); // Endpoint to check the status of an authentication session app.get('/api/digiid/status/:sessionId', (req: Request, res: Response) => { @@ -200,4 +242,4 @@ app.get('/', (_: Request, res: Response) => { // Start the server app.listen(PORT, () => { console.log(`Server listening on port ${PORT}`); -});
\ No newline at end of file +}); diff --git a/src/server/utils.ts b/src/server/utils.ts index 0b1333f..bb614c0 100644 --- a/src/server/utils.ts +++ b/src/server/utils.ts @@ -1,6 +1,9 @@ // Simple utility to determine DigiByte address type based on prefix -export type DigiByteAddressType = 'DigiByte (DGB)' | 'DigiAsset (DGA)' | 'Unknown'; +export type DigiByteAddressType = + | 'DigiByte (DGB)' + | 'DigiAsset (DGA)' + | 'Unknown'; export function getDigiByteAddressType(address: string): DigiByteAddressType { if (address.startsWith('dgb1')) { @@ -10,11 +13,12 @@ export function getDigiByteAddressType(address: string): DigiByteAddressType { // For now, assume non-DGB is DigiAsset, but this might need refinement // depending on actual DigiAsset address formats. // If the digiid-ts library provides a helper for this, use that instead. - else if (address) { // Basic check to differentiate from empty/null + else if (address) { + // Basic check to differentiate from empty/null // Assuming DigiAssets might start differently or be the fallback // This is a placeholder assumption. // A more robust check based on DigiAsset address specification is needed. return 'DigiAsset (DGA)'; // Placeholder - ADJUST BASED ON ACTUAL DGA PREFIX } return 'Unknown'; -}
\ No newline at end of file +} |
