From cd7d4b3c8e4c8c9d1f93bd93375c8b4695a8b11b Mon Sep 17 00:00:00 2001 From: Amin Latifi Date: Tue, 5 Dec 2023 20:00:24 +0330 Subject: [PATCH 1/4] Support solana authentication --- config/local.env | 53 +++ config/test.env | 3 + package-lock.json | 414 +++++++++++++++++- package.json | 4 + .../v1/authenticationController.ts | 89 +++- src/entities/siweNonce.ts | 3 + src/routes/v1/authenticationRouter.test.ts | 96 +++- src/routes/v1/authenticationRouter.ts | 25 +- src/server.ts | 8 +- src/services/authenticationService.ts | 2 +- src/types/requestResponses.ts | 5 + src/utils/errorMessages.ts | 6 + 12 files changed, 661 insertions(+), 47 deletions(-) create mode 100644 config/local.env diff --git a/config/local.env b/config/local.env new file mode 100644 index 0000000..e88843f --- /dev/null +++ b/config/local.env @@ -0,0 +1,53 @@ +PORT=3089 + +TYPEORM_DATABASE_NAME=microservices +TYPEORM_DATABASE_USER=postgres +TYPEORM_DATABASE_PASSWORD=postgres +TYPEORM_DATABASE_HOST=127.0.0.1 +TYPEORM_DATABASE_PORT=5443 + +#JWT_SECRET=fdksjalkfhjio472389roiewfhskjahfjka +#ACCESS_TOKEN_LIFETIME_HOURS=2 +# +#BCRYPT_SALT=$2b$10$MSIRw14eVDzxdztX3AZYlu +PRIVATE_ETHERS_TEST_KEY=8ab0e165c2ea461b01cdd49aec882d179dccdbdb5c85c3f9c94c448aa65c5ace +PUBLIC_ETHERS_TEST_KEY=0x53bFf74b9Af2E3853f758A8D2Bd61CD115d27782 + +PRIVATE_SOLANA_TEST_KEY=4ncZsCpjpVgyp9tRowQNost352WS4EQnuAFvWXHUE3XcasnuyiTwwSrdnixf4Sves8x1zzNhdrHJVYXdBdnfDiKx +PUBLIC_SOLANA_TEST_KEY=ALuY9D3XDhNgJvKQavNcLS6qZ9oGP4mUWRvnerWXxgML +# +#HOSTNAME_WHITELIST=localhost,loca.lt +# +#NONCE_LIFE_MINUTES=5 +# + +REDIS_PORT=6489 + + +HOSTNAME_WHITELIST=next.giveth.io,www.next.giveth.io,staging.giveth.io,www.staging.giveth.io,localhost,giveth.io,vercel.app,www.giveth.io, typescript.giveth.io, www.typescript.giveth.io, loca.lt + +JWT_SECRET=DfiSO79a5GF2Hhw5PE6z4Cq3m8Sj6tzzyFfcCecwJGoeNL7qf3Q7u61E4HL008noEsp4upq1VD5yc2PfUtccn1Gfyh3mgeBIwQAO3RGPizbkqOpa7pckfGnBLgORkdnE +ACCESS_TOKEN_LIFETIME_HOURS=2 + +BCRYPT_SALT=11 + +STAGING_HOST: serve.giveth.io +STAGING_USERNAME: givethadmin +ENVIRONMENT=local + + +# The following are used for the sending logs to elasticsearch +ENABLE_ELASTICSEARCH_LOG=true +ELASTICSEARCH_HOST=https://elastic.giveth.io/ +ELATICSEARCH_AUTH= + +scond=ZzhYs595.t0cQXQi6u5A0962uoknuz88dsRhjl5yo +GITCOIN_COMMUNITY_ID=816 +GITCOIN_API_KEY=EvfMTSF9.W2YKoboS2VQ5l9PjUyyTQi577pNZj6ET + +XDAI_NODE_HTTP_URL=https://rpc.eu-central-2.gateway.fm/v4/gnosis/non-archival/mainnet +BSC_NODE_HTTP_URL= +CELO_NODE_HTTP_URL= +INFURA_ID=fcbaf28d1a284480aa04a4aebbf1132c + +SERVICE_NAME=staging diff --git a/config/test.env b/config/test.env index d216d88..b329e10 100644 --- a/config/test.env +++ b/config/test.env @@ -13,6 +13,9 @@ BCRYPT_SALT=$2b$10$MSIRw14eVDzxdztX3AZYlu PRIVATE_ETHERS_TEST_KEY=8ab0e165c2ea461b01cdd49aec882d179dccdbdb5c85c3f9c94c448aa65c5ace PUBLIC_ETHERS_TEST_KEY=0x53bFf74b9Af2E3853f758A8D2Bd61CD115d27782 +PRIVATE_SOLANA_TEST_KEY=4ncZsCpjpVgyp9tRowQNost352WS4EQnuAFvWXHUE3XcasnuyiTwwSrdnixf4Sves8x1zzNhdrHJVYXdBdnfDiKx +PUBLIC_SOLANA_TEST_KEY=ALuY9D3XDhNgJvKQavNcLS6qZ9oGP4mUWRvnerWXxgML + HOSTNAME_WHITELIST=localhost, loca.lt NONCE_LIFE_MINUTES=5 diff --git a/package-lock.json b/package-lock.json index f0a7696..e8b78e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,13 +14,16 @@ "@ethersproject/testcases": "^5.7.0", "@safe-global/api-kit": "^1.3.1", "@safe-global/protocol-kit": "^1.3.0", + "@solana/web3.js": "^1.87.6", "@types/cors": "^2.8.10", "@types/ioredis": "^4.22.0", "@types/jsonwebtoken": "^8.5.0", + "@web3auth/sign-in-with-solana": "^3.0.0", "adminjs": "^6.8.7", "axios": "^1.3.4", "bcrypt": "5.0.1", "body-parser": "^1.20.0", + "bs58": "^5.0.0", "bunyan": "^1.8.15", "bunyan-elasticsearch": "^1.0.1", "bunyan-rotating-file-stream": "^2.0.1", @@ -39,6 +42,7 @@ "siwe": "^1.1.6", "swagger-ui-express": "^4.3.0", "tsoa": "^5.1.1", + "tweetnacl": "^1.0.3", "typeorm": "0.3.11" }, "devDependencies": { @@ -1802,16 +1806,21 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "node_modules/@babel/runtime": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", - "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.5.tgz", + "integrity": "sha512-NdUTHcPe4C99WxPub+K9l9tK5/lV4UXIoaHSYgzco9BCyjKAAwzdBI+wWtYqHt7LJdbo74ZjRPJgzVweq1sz0w==", "dependencies": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/runtime/node_modules/regenerator-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + }, "node_modules/@babel/template": { "version": "7.20.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", @@ -3415,6 +3424,115 @@ "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", "dev": true }, + "node_modules/@solana/buffer-layout": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz", + "integrity": "sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==", + "dependencies": { + "buffer": "~6.0.3" + }, + "engines": { + "node": ">=5.10" + } + }, + "node_modules/@solana/buffer-layout/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/@solana/web3.js": { + "version": "1.87.6", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.87.6.tgz", + "integrity": "sha512-LkqsEBgTZztFiccZZXnawWa8qNCATEqE97/d0vIwjTclmVlc8pBpD1DmjfVHtZ1HS5fZorFlVhXfpwnCNDZfyg==", + "dependencies": { + "@babel/runtime": "^7.23.2", + "@noble/curves": "^1.2.0", + "@noble/hashes": "^1.3.1", + "@solana/buffer-layout": "^4.0.0", + "agentkeepalive": "^4.3.0", + "bigint-buffer": "^1.1.5", + "bn.js": "^5.2.1", + "borsh": "^0.7.0", + "bs58": "^4.0.1", + "buffer": "6.0.3", + "fast-stable-stringify": "^1.0.0", + "jayson": "^4.1.0", + "node-fetch": "^2.6.12", + "rpc-websockets": "^7.5.1", + "superstruct": "^0.14.2" + } + }, + "node_modules/@solana/web3.js/node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@solana/web3.js/node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@solana/web3.js/node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/@solana/web3.js/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/@spruceid/siwe-parser": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@spruceid/siwe-parser/-/siwe-parser-1.1.3.tgz", @@ -4074,6 +4192,11 @@ "@tiptap/core": "^2.0.0" } }, + "node_modules/@toruslabs/tweetnacl-js": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@toruslabs/tweetnacl-js/-/tweetnacl-js-1.0.4.tgz", + "integrity": "sha512-h8fVemW5pstsKbm/fTx+y61dZkh5Pepy/92lsyKp83KErf96jT+w4LGx4nEgeAVrdYQDTLg2tO7vu/boEb23Iw==" + }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", @@ -4589,6 +4712,14 @@ "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" }, + "node_modules/@types/ws": { + "version": "7.4.7", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", + "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.20.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.20.0.tgz", @@ -4773,6 +4904,20 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@web3auth/sign-in-with-solana": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@web3auth/sign-in-with-solana/-/sign-in-with-solana-3.0.0.tgz", + "integrity": "sha512-F5w0v6nraF2x60DRgUwucN3sJU/R34ZnmyDYe4e6JhnfPrIu5NfPH5+yl69GezTXQcYWsEVBSsVxOO+LdAut1A==", + "dependencies": { + "@toruslabs/tweetnacl-js": "^1.0.4", + "bs58": "^5.0.0", + "valid-url": "^1.0.9" + }, + "engines": { + "node": ">=18.x", + "npm": ">=9.x" + } + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -4904,6 +5049,17 @@ "node": ">= 6.0.0" } }, + "node_modules/agentkeepalive": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", + "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -5279,11 +5435,28 @@ "tweetnacl": "^0.14.3" } }, + "node_modules/bcrypt-pbkdf/node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + }, "node_modules/bech32": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" }, + "node_modules/bigint-buffer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bigint-buffer/-/bigint-buffer-1.1.5.tgz", + "integrity": "sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==", + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.3.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/bignumber.js": { "version": "9.1.2", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", @@ -5301,6 +5474,14 @@ "node": ">=8" } }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -5362,6 +5543,24 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/borsh": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/borsh/-/borsh-0.7.0.tgz", + "integrity": "sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==", + "dependencies": { + "bn.js": "^5.2.0", + "bs58": "^4.0.0", + "text-encoding-utf-8": "^1.0.2" + } + }, + "node_modules/borsh/node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "dependencies": { + "base-x": "^3.0.2" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -5435,13 +5634,18 @@ } }, "node_modules/bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz", + "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==", "dependencies": { - "base-x": "^3.0.2" + "base-x": "^4.0.0" } }, + "node_modules/bs58/node_modules/base-x": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz", + "integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==" + }, "node_modules/bs58check": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", @@ -5452,6 +5656,14 @@ "safe-buffer": "^5.1.2" } }, + "node_modules/bs58check/node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "dependencies": { + "base-x": "^3.0.2" + } + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -6409,6 +6621,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delay": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", + "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -6832,6 +7055,14 @@ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" }, + "node_modules/es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", + "dependencies": { + "es6-promise": "^4.0.3" + } + }, "node_modules/es6-symbol": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", @@ -7601,6 +7832,14 @@ "node >=0.6.0" ] }, + "node_modules/eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==", + "engines": { + "node": "> 0.1.90" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -7651,6 +7890,11 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-stable-stringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz", + "integrity": "sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==" + }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -7672,6 +7916,11 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -8462,6 +8711,14 @@ "node": ">= 6" } }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dependencies": { + "ms": "^2.0.0" + } + }, "node_modules/husky": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", @@ -9045,6 +9302,14 @@ "node": ">=0.10.0" } }, + "node_modules/isomorphic-ws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", + "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", + "peerDependencies": { + "ws": "*" + } + }, "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -9070,6 +9335,41 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/jayson": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jayson/-/jayson-4.1.0.tgz", + "integrity": "sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A==", + "dependencies": { + "@types/connect": "^3.4.33", + "@types/node": "^12.12.54", + "@types/ws": "^7.4.4", + "commander": "^2.20.3", + "delay": "^5.0.0", + "es6-promisify": "^5.0.0", + "eyes": "^0.1.8", + "isomorphic-ws": "^4.0.1", + "json-stringify-safe": "^5.0.1", + "JSONStream": "^1.3.5", + "uuid": "^8.3.2", + "ws": "^7.4.5" + }, + "bin": { + "jayson": "bin/jayson.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jayson/node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" + }, + "node_modules/jayson/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, "node_modules/jest-worker": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", @@ -9204,6 +9504,29 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", @@ -11951,6 +12274,50 @@ "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz", "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==" }, + "node_modules/rpc-websockets": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-7.8.0.tgz", + "integrity": "sha512-AStkq6KDvSAmA4WiwlK1pDvj/33BWmExTATUokC0v+NhWekXSTNzXS5OGXeYwq501/pj6lBZMofg/h4dx4/tCg==", + "dependencies": { + "@babel/runtime": "^7.17.2", + "eventemitter3": "^4.0.7", + "uuid": "^8.3.2", + "ws": "^8.5.0" + }, + "funding": { + "type": "paypal", + "url": "https://paypal.me/kozjak" + }, + "optionalDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + } + }, + "node_modules/rpc-websockets/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "node_modules/rpc-websockets/node_modules/ws": { + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", + "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -12452,6 +12819,11 @@ "node": ">=0.10.0" } }, + "node_modules/sshpk/node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + }, "node_modules/standard-as-callback": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", @@ -12658,6 +13030,11 @@ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" }, + "node_modules/superstruct": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-0.14.2.tgz", + "integrity": "sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ==" + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -12915,6 +13292,11 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, + "node_modules/text-encoding-utf-8": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz", + "integrity": "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==" + }, "node_modules/text-mask-addons": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/text-mask-addons/-/text-mask-addons-3.8.0.tgz", @@ -12953,6 +13335,11 @@ "node": ">=10" } }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + }, "node_modules/timed-out": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", @@ -13293,9 +13680,9 @@ } }, "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" }, "node_modules/type": { "version": "1.2.0", @@ -13881,6 +14268,11 @@ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "devOptional": true }, + "node_modules/valid-url": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz", + "integrity": "sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA==" + }, "node_modules/validator": { "version": "13.9.0", "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz", diff --git a/package.json b/package.json index 9807ab2..9aa144b 100644 --- a/package.json +++ b/package.json @@ -42,13 +42,16 @@ "@ethersproject/testcases": "^5.7.0", "@safe-global/api-kit": "^1.3.1", "@safe-global/protocol-kit": "^1.3.0", + "@solana/web3.js": "^1.87.6", "@types/cors": "^2.8.10", "@types/ioredis": "^4.22.0", "@types/jsonwebtoken": "^8.5.0", + "@web3auth/sign-in-with-solana": "^3.0.0", "adminjs": "^6.8.7", "axios": "^1.3.4", "bcrypt": "5.0.1", "body-parser": "^1.20.0", + "bs58": "^5.0.0", "bunyan": "^1.8.15", "bunyan-elasticsearch": "^1.0.1", "bunyan-rotating-file-stream": "^2.0.1", @@ -67,6 +70,7 @@ "siwe": "^1.1.6", "swagger-ui-express": "^4.3.0", "tsoa": "^5.1.1", + "tweetnacl": "^1.0.3", "typeorm": "0.3.11" }, "devDependencies": { diff --git a/src/controllers/v1/authenticationController.ts b/src/controllers/v1/authenticationController.ts index 2330431..f02e2d0 100644 --- a/src/controllers/v1/authenticationController.ts +++ b/src/controllers/v1/authenticationController.ts @@ -2,51 +2,98 @@ import { SiweMessage } from 'siwe'; import { Route, Tags, Post, Body } from 'tsoa'; import { SiweNonce } from '../../entities/siweNonce'; import { findNonce } from '../../repositories/siweNonceRepository'; -import { generateAccessToken } from '../../services/authenticationService'; +import { + AccessTokenFields, + generateAccessToken, +} from '../../services/authenticationService'; import { AuthenticationRequest, AuthenticationResponse, + solanaAuthenticateRequest, } from '../../types/requestResponses'; import { StandardError } from '../../types/StandardError'; import { errorMessagesEnum } from '../../utils/errorMessages'; import { logger } from '../../utils/logger'; +import { Header, Payload, SIWS } from '@web3auth/sign-in-with-solana'; -@Route('/v1/authentication') @Tags('Authentication') export class AuthenticationController { + @Route('/v1/authentication') @Post() - public async authenticate( + public async ethereumAuthenticate( @Body() body: AuthenticationRequest, ): Promise { try { const message = new SiweMessage(body.message); const fields = await message.validate(body.signature); - const whitelistedNonce = await findNonce(fields.nonce); - if (!whitelistedNonce || fields.nonce !== body.nonce) - throw new StandardError(errorMessagesEnum.NONCE_INVALID); + return await this.issueToken(fields, body.nonce); + } catch (e) { + logger.error('authenticationController error', e); + throw e; + } + } + + @Route('/v1/solanaAuthentication') + @Post() + public async solanaAuthenticate( + @Body() body: solanaAuthenticateRequest, + ): Promise { + try { + const { signature, message, payload } = body; + + const header = new Header(); + header.t = 'sip99'; - if (whitelistedNonce.isExpired()) { - logger.info( - 'POST authenticate, whitelistedNonce is expired', - whitelistedNonce, + const msg = new SIWS(message); + + const result = await msg.verify({ + payload, + signature: { + t: 'sip99', + s: signature, + }, + }); + + if (!result.success) { + logger.error('authenticationController error', result.error); + throw new StandardError( + errorMessagesEnum.INVALID_SOLANA_SIGNATURE_OR_LOGIN_DATA, ); - await SiweNonce.delete({ id: whitelistedNonce.id }); - throw new StandardError(errorMessagesEnum.NONCE_EXPIRED); } - const token = await generateAccessToken(fields); - logger.info(`User with address ${token.publicAddress} logged in`); - // clean up to prevent reuse - await SiweNonce.delete({ id: whitelistedNonce.id }); - return { - jwt: token.jwt, - expiration: token.expirationDate.valueOf(), - publicAddress: token.publicAddress, - }; + return await this.issueToken(payload, payload.nonce); } catch (e) { logger.error('authenticationController error', e); throw e; } } + + async issueToken( + fields: { nonce: string } & AccessTokenFields, + requestNonce: string, + ): Promise { + const whitelistedNonce = await findNonce(fields.nonce); + if (!whitelistedNonce || fields.nonce !== requestNonce) + throw new StandardError(errorMessagesEnum.NONCE_INVALID); + + if (whitelistedNonce.isExpired()) { + logger.info( + 'POST authenticate, whitelistedNonce is expired', + whitelistedNonce, + ); + await SiweNonce.delete({ id: whitelistedNonce.id }); + throw new StandardError(errorMessagesEnum.NONCE_EXPIRED); + } + const token = await generateAccessToken(fields); + logger.info(`User with address ${token.publicAddress} logged in`); + + // clean up to prevent reuse + await SiweNonce.delete({ id: whitelistedNonce.id }); + return { + jwt: token.jwt, + expiration: token.expirationDate.valueOf(), + publicAddress: token.publicAddress, + }; + } } diff --git a/src/entities/siweNonce.ts b/src/entities/siweNonce.ts index a35e3a2..efbf719 100644 --- a/src/entities/siweNonce.ts +++ b/src/entities/siweNonce.ts @@ -12,6 +12,9 @@ import { /** * Sign-In-With-Ethereum Nonce * Check the nonce during verification of Messages + * + * TODO: After integration with Solana, we don't only use SIWE. + * We need to rename this entity to Nonce. */ @Entity() diff --git a/src/routes/v1/authenticationRouter.test.ts b/src/routes/v1/authenticationRouter.test.ts index 2c12351..9241b15 100644 --- a/src/routes/v1/authenticationRouter.test.ts +++ b/src/routes/v1/authenticationRouter.test.ts @@ -2,15 +2,21 @@ import axios from 'axios'; import { serverUrl } from '../../../test/testUtils'; import { assert } from 'chai'; import { SiweNonce } from '../../entities/siweNonce'; -import { generateNonce } from 'siwe'; +import { generateNonce, SiweMessage } from 'siwe'; import moment from 'moment'; import { ethers } from 'ethers'; +import { Keypair } from '@solana/web3.js'; +import nacl from 'tweetnacl'; +import { Header, Payload, SIWS } from '@web3auth/sign-in-with-solana'; +import base58 from 'bs58'; -const siwe = require('siwe'); -const privateKey = process.env.PRIVATE_ETHERS_TEST_KEY as string; -const publicKey = process.env.PUBLIC_ETHERS_TEST_KEY as string; +const ethPrivateKey = process.env.PRIVATE_ETHERS_TEST_KEY as string; +const ethPublicKey = process.env.PUBLIC_ETHERS_TEST_KEY as string; const provider = ethers.getDefaultProvider(); +const solanaPrivateKey = process.env.PRIVATE_SOLANA_TEST_KEY as string; +const solanaPublicKey = process.env.PUBLIC_SOLANA_TEST_KEY as string; + describe('/authentication test cases', authenticationTestCases); function authenticationTestCases() { @@ -23,16 +29,16 @@ function authenticationTestCases() { expirationDate: moment().add(5, 'minutes').toDate(), }).save(); - const wallet = new ethers.Wallet(privateKey, provider); + const wallet = new ethers.Wallet(ethPrivateKey, provider); - const siweMessage = new siwe.SiweMessage({ + const siweMessage = new SiweMessage({ domain, - address: publicKey, + address: ethPublicKey, nonce: nonce.nonce, // verification servers gives statement: 'This is a test statement.', uri: origin, version: '1', - chainId: '1', + chainId: 1, }); const textMessage = siweMessage.prepareMessage(); @@ -46,6 +52,78 @@ function authenticationTestCases() { signature: signature, }); assert.equal(result.status, 200); - assert.equal(result.data.publicAddress, publicKey); + assert.equal(result.data.publicAddress, ethPublicKey); + }); + + it('should authenticate with nonce fetched from the server', async () => { + const nonceResult = await axios.get(`${serverUrl}/v1/nonce`); + const nonce = nonceResult.data.message; + + const wallet = new ethers.Wallet(ethPrivateKey, provider); + + const siweMessage = new SiweMessage({ + domain, + address: ethPublicKey, + nonce: nonce, // verification servers gives + statement: 'This is a test statement.', + uri: origin, + version: '1', + chainId: 1, + }); + + const textMessage = siweMessage.prepareMessage(); + const signature = await wallet.signMessage(textMessage); + + // for future stubbing examples + // sinon.stub(SiweMessage.prototype, 'validate').resolves(siweMessage); + const result = await axios.post(`${serverUrl}/v1/authentication`, { + message: textMessage, + nonce: nonce, + signature: signature, + }); + assert.equal(result.status, 200); + assert.equal(result.data.publicAddress, ethPublicKey); + }); + + it('should authenticate with nonce fetched from the server - solana', async () => { + const nonceResult = await axios.get(`${serverUrl}/v1/nonce`); + const nonce = nonceResult.data.message; + + const keypair = Keypair.fromSecretKey(base58.decode(solanaPrivateKey)); + + const header = new Header(); + header.t = 'sip99'; + + const payload = new Payload(); + + payload.domain = domain; + payload.address = solanaPublicKey; + payload.uri = origin; + payload.statement = 'This is a test statement'; + payload.version = '1'; + payload.chainId = 0; + payload.nonce = nonce; + + const message = new SIWS({ + header, + payload, + }).prepareMessage(); + + const signature = nacl.sign.detached( + Buffer.from(message), + keypair.secretKey, + ); + + const data = { + message, + signature: base58.encode(signature), + payload: payload, + }; + const result = await axios.post( + `${serverUrl}/v1/solanaAuthentication`, + data, + ); + assert.equal(result.status, 200); + assert.equal(result.data.publicAddress, solanaPublicKey); }); } diff --git a/src/routes/v1/authenticationRouter.ts b/src/routes/v1/authenticationRouter.ts index ef4da14..f24eb63 100644 --- a/src/routes/v1/authenticationRouter.ts +++ b/src/routes/v1/authenticationRouter.ts @@ -15,7 +15,7 @@ authenticationRouter.post( } const { message, signature, nonce } = req.body; - const result = await authController.authenticate({ + const result = await authController.ethereumAuthenticate({ message, signature, nonce, @@ -27,3 +27,26 @@ authenticationRouter.post( } }, ); + +authenticationRouter.post( + '/solanaAuthentication', + async (req: Request, res: Response, next) => { + try { + if (!req.body.payload || !req.body.signature || !req.body.message) { + res.status(422).json({ message: errorMessagesEnum.MISSING_LOGIN_DATA }); + return; + } + + const { message, signature, payload } = req.body; + const result = await authController.solanaAuthenticate({ + message, + signature, + payload, + }); + res.send(result); + } catch (e) { + logger.error('authenticationController() error', e); + next(e); + } + }, +); diff --git a/src/server.ts b/src/server.ts index f3f638b..698fa12 100644 --- a/src/server.ts +++ b/src/server.ts @@ -7,10 +7,10 @@ import bodyParser from 'body-parser'; import { errorHandler } from './middlewares/errorHandler'; import { adminJsRootPath, getAdminBroRouter } from './routes/v1/adminbroRouter'; import { logger } from './utils/logger'; -import { getProvider, getSafeTransactionNetworkUrl } from './utils/provider'; -import { EthersAdapter } from '@safe-global/protocol-kit'; -import { ethers } from 'ethers'; -import SafeApiKit from '@safe-global/api-kit'; +// import { getProvider, getSafeTransactionNetworkUrl } from './utils/provider'; +// import { EthersAdapter } from '@safe-global/protocol-kit'; +// import { ethers } from 'ethers'; +// import SafeApiKit from '@safe-global/api-kit'; // tslint:disable:no-var-requires const cors = require('cors'); const whitelistHostnames: string[] = ( diff --git a/src/services/authenticationService.ts b/src/services/authenticationService.ts index 82c9b07..4611f00 100644 --- a/src/services/authenticationService.ts +++ b/src/services/authenticationService.ts @@ -3,7 +3,7 @@ import { AccessToken } from '../entities/accessToken'; import { generateRandomString } from '../utils/utils'; import { generateJwt, JwtPayload } from './jwtService'; -interface AccessTokenFields { +export interface AccessTokenFields { address: string; expirationDate?: Date; } diff --git a/src/types/requestResponses.ts b/src/types/requestResponses.ts index 058e43e..e18cbe0 100644 --- a/src/types/requestResponses.ts +++ b/src/types/requestResponses.ts @@ -1,3 +1,4 @@ +import { Payload } from '@web3auth/sign-in-with-solana'; import { JwtPayload } from '../services/jwtService'; export type AuthenticationRequest = { @@ -6,6 +7,10 @@ export type AuthenticationRequest = { nonce: string; }; +export type solanaAuthenticateRequest = Omit & { + payload: Payload; +}; + export type MultisigAuthenticationRequest = { safeMessageTimestamp?: number; approvalExpirationDays?: number; diff --git a/src/utils/errorMessages.ts b/src/utils/errorMessages.ts index 6015045..5afe29f 100644 --- a/src/utils/errorMessages.ts +++ b/src/utils/errorMessages.ts @@ -34,6 +34,12 @@ export const errorMessagesEnum = { code: 1000, }, + INVALID_SOLANA_SIGNATURE_OR_LOGIN_DATA: { + message: 'Invalid solana signature or login data', + httpStatusCode: 401, + code: 1000, + }, + JWT_EXPIRED: { message: 'Access Token is expired', httpStatusCode: 401, From 0488921fdb2faff4b8261d3eb1aa76651671b8d5 Mon Sep 17 00:00:00 2001 From: Amin Latifi Date: Tue, 5 Dec 2023 20:01:53 +0330 Subject: [PATCH 2/4] Remove commented lines --- src/server.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/server.ts b/src/server.ts index 698fa12..b5c9041 100644 --- a/src/server.ts +++ b/src/server.ts @@ -7,11 +7,6 @@ import bodyParser from 'body-parser'; import { errorHandler } from './middlewares/errorHandler'; import { adminJsRootPath, getAdminBroRouter } from './routes/v1/adminbroRouter'; import { logger } from './utils/logger'; -// import { getProvider, getSafeTransactionNetworkUrl } from './utils/provider'; -// import { EthersAdapter } from '@safe-global/protocol-kit'; -// import { ethers } from 'ethers'; -// import SafeApiKit from '@safe-global/api-kit'; -// tslint:disable:no-var-requires const cors = require('cors'); const whitelistHostnames: string[] = ( process.env.HOSTNAME_WHITELIST as string From fabd80ae6b09a5c168006246db4329d13249c805 Mon Sep 17 00:00:00 2001 From: Amin Latifi Date: Tue, 5 Dec 2023 22:12:59 +0330 Subject: [PATCH 3/4] Removed local.env --- config/local.env | 53 ------------------------------------------------ 1 file changed, 53 deletions(-) delete mode 100644 config/local.env diff --git a/config/local.env b/config/local.env deleted file mode 100644 index e88843f..0000000 --- a/config/local.env +++ /dev/null @@ -1,53 +0,0 @@ -PORT=3089 - -TYPEORM_DATABASE_NAME=microservices -TYPEORM_DATABASE_USER=postgres -TYPEORM_DATABASE_PASSWORD=postgres -TYPEORM_DATABASE_HOST=127.0.0.1 -TYPEORM_DATABASE_PORT=5443 - -#JWT_SECRET=fdksjalkfhjio472389roiewfhskjahfjka -#ACCESS_TOKEN_LIFETIME_HOURS=2 -# -#BCRYPT_SALT=$2b$10$MSIRw14eVDzxdztX3AZYlu -PRIVATE_ETHERS_TEST_KEY=8ab0e165c2ea461b01cdd49aec882d179dccdbdb5c85c3f9c94c448aa65c5ace -PUBLIC_ETHERS_TEST_KEY=0x53bFf74b9Af2E3853f758A8D2Bd61CD115d27782 - -PRIVATE_SOLANA_TEST_KEY=4ncZsCpjpVgyp9tRowQNost352WS4EQnuAFvWXHUE3XcasnuyiTwwSrdnixf4Sves8x1zzNhdrHJVYXdBdnfDiKx -PUBLIC_SOLANA_TEST_KEY=ALuY9D3XDhNgJvKQavNcLS6qZ9oGP4mUWRvnerWXxgML -# -#HOSTNAME_WHITELIST=localhost,loca.lt -# -#NONCE_LIFE_MINUTES=5 -# - -REDIS_PORT=6489 - - -HOSTNAME_WHITELIST=next.giveth.io,www.next.giveth.io,staging.giveth.io,www.staging.giveth.io,localhost,giveth.io,vercel.app,www.giveth.io, typescript.giveth.io, www.typescript.giveth.io, loca.lt - -JWT_SECRET=DfiSO79a5GF2Hhw5PE6z4Cq3m8Sj6tzzyFfcCecwJGoeNL7qf3Q7u61E4HL008noEsp4upq1VD5yc2PfUtccn1Gfyh3mgeBIwQAO3RGPizbkqOpa7pckfGnBLgORkdnE -ACCESS_TOKEN_LIFETIME_HOURS=2 - -BCRYPT_SALT=11 - -STAGING_HOST: serve.giveth.io -STAGING_USERNAME: givethadmin -ENVIRONMENT=local - - -# The following are used for the sending logs to elasticsearch -ENABLE_ELASTICSEARCH_LOG=true -ELASTICSEARCH_HOST=https://elastic.giveth.io/ -ELATICSEARCH_AUTH= - -scond=ZzhYs595.t0cQXQi6u5A0962uoknuz88dsRhjl5yo -GITCOIN_COMMUNITY_ID=816 -GITCOIN_API_KEY=EvfMTSF9.W2YKoboS2VQ5l9PjUyyTQi577pNZj6ET - -XDAI_NODE_HTTP_URL=https://rpc.eu-central-2.gateway.fm/v4/gnosis/non-archival/mainnet -BSC_NODE_HTTP_URL= -CELO_NODE_HTTP_URL= -INFURA_ID=fcbaf28d1a284480aa04a4aebbf1132c - -SERVICE_NAME=staging From 47ed1fff3a8e95e755160fca0ec3d8f9f5a4badd Mon Sep 17 00:00:00 2001 From: Amin Latifi Date: Wed, 6 Dec 2023 12:30:00 +0330 Subject: [PATCH 4/4] Changed solana authentication parameters * It's not needed to pass whole SWIS payload, only address and nonce are sufficient --- src/controllers/v1/authenticationController.ts | 7 +++++-- src/routes/v1/authenticationRouter.test.ts | 4 ++-- src/routes/v1/authenticationRouter.ts | 12 +++++++++--- src/types/requestResponses.ts | 5 ++--- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/controllers/v1/authenticationController.ts b/src/controllers/v1/authenticationController.ts index f02e2d0..245aa0f 100644 --- a/src/controllers/v1/authenticationController.ts +++ b/src/controllers/v1/authenticationController.ts @@ -40,12 +40,15 @@ export class AuthenticationController { @Body() body: solanaAuthenticateRequest, ): Promise { try { - const { signature, message, payload } = body; + const { signature, message, nonce, address } = body; const header = new Header(); header.t = 'sip99'; const msg = new SIWS(message); + const payload = new Payload(); + payload.address = address; + payload.nonce = nonce; const result = await msg.verify({ payload, @@ -62,7 +65,7 @@ export class AuthenticationController { ); } - return await this.issueToken(payload, payload.nonce); + return await this.issueToken(result.data.payload, nonce); } catch (e) { logger.error('authenticationController error', e); throw e; diff --git a/src/routes/v1/authenticationRouter.test.ts b/src/routes/v1/authenticationRouter.test.ts index 9241b15..77bd3cd 100644 --- a/src/routes/v1/authenticationRouter.test.ts +++ b/src/routes/v1/authenticationRouter.test.ts @@ -101,7 +101,6 @@ function authenticationTestCases() { payload.uri = origin; payload.statement = 'This is a test statement'; payload.version = '1'; - payload.chainId = 0; payload.nonce = nonce; const message = new SIWS({ @@ -117,7 +116,8 @@ function authenticationTestCases() { const data = { message, signature: base58.encode(signature), - payload: payload, + address: solanaPublicKey, + nonce, }; const result = await axios.post( `${serverUrl}/v1/solanaAuthentication`, diff --git a/src/routes/v1/authenticationRouter.ts b/src/routes/v1/authenticationRouter.ts index f24eb63..ef618cb 100644 --- a/src/routes/v1/authenticationRouter.ts +++ b/src/routes/v1/authenticationRouter.ts @@ -32,16 +32,22 @@ authenticationRouter.post( '/solanaAuthentication', async (req: Request, res: Response, next) => { try { - if (!req.body.payload || !req.body.signature || !req.body.message) { + if ( + !req.body.message || + !req.body.nonce || + !req.body.signature || + !req.body.address + ) { res.status(422).json({ message: errorMessagesEnum.MISSING_LOGIN_DATA }); return; } - const { message, signature, payload } = req.body; + const { nonce, message, signature, address } = req.body; const result = await authController.solanaAuthenticate({ message, signature, - payload, + address, + nonce, }); res.send(result); } catch (e) { diff --git a/src/types/requestResponses.ts b/src/types/requestResponses.ts index e18cbe0..3b89268 100644 --- a/src/types/requestResponses.ts +++ b/src/types/requestResponses.ts @@ -1,4 +1,3 @@ -import { Payload } from '@web3auth/sign-in-with-solana'; import { JwtPayload } from '../services/jwtService'; export type AuthenticationRequest = { @@ -7,8 +6,8 @@ export type AuthenticationRequest = { nonce: string; }; -export type solanaAuthenticateRequest = Omit & { - payload: Payload; +export type solanaAuthenticateRequest = AuthenticationRequest & { + address: string; }; export type MultisigAuthenticationRequest = {