From 0d004b7c9ff74fc436a6a2aff7014814f7940802 Mon Sep 17 00:00:00 2001 From: Harman-singh-waraich Date: Mon, 15 Jul 2024 15:27:00 +0530 Subject: [PATCH 01/28] chore(web): initial-wrapper-update --- web/package.json | 2 +- web/src/components/EnsureAuth.tsx | 20 +++--- web/src/utils/authoriseUser.ts | 30 ++++++--- yarn.lock | 101 +++++++++++++++++++++++++++--- 4 files changed, 126 insertions(+), 27 deletions(-) diff --git a/web/package.json b/web/package.json index 359d65c0f..8a54409a0 100644 --- a/web/package.json +++ b/web/package.json @@ -118,7 +118,7 @@ "siwe": "^2.3.1", "styled-components": "^5.3.11", "vanilla-jsoneditor": "^0.21.4", - "viem": "^2.1.0", + "viem": "^2.17.3", "vite": "^5.2.10", "wagmi": "^2.11.3" } diff --git a/web/src/components/EnsureAuth.tsx b/web/src/components/EnsureAuth.tsx index 5da207605..0b500b271 100644 --- a/web/src/components/EnsureAuth.tsx +++ b/web/src/components/EnsureAuth.tsx @@ -1,7 +1,7 @@ import React, { useMemo, useState } from "react"; import * as jwt from "jose"; -import { SiweMessage } from "siwe"; +import { createSiweMessage } from "viem/siwe"; import { useAccount, useChainId, useSignMessage } from "wagmi"; import { Button } from "@kleros/ui-components-library"; @@ -41,7 +41,7 @@ export const EnsureAuth: React.FC = ({ children, className }) => { setIsLoading(true); if (!address) return; - const message = await createSiweMessage(address, "Sign In to Kleros with Ethereum.", chainId); + const message = await createMessage(address, "Sign In to Kleros with Ethereum.", chainId); const signature = await signMessageAsync({ message }); @@ -52,9 +52,8 @@ export const EnsureAuth: React.FC = ({ children, className }) => { signature, message, }) - .then(async (res) => { - const response = await res.json(); - setAuthToken(response["token"]); + .then(async (token) => { + setAuthToken(token); }) .catch((err) => console.log({ err })) .finally(() => setIsLoading(false)); @@ -77,16 +76,15 @@ export const EnsureAuth: React.FC = ({ children, className }) => { ); }; -async function createSiweMessage(address: `0x${string}`, statement: string, chainId: number = DEFAULT_CHAIN) { +async function createMessage(address: `0x${string}`, statement: string, chainId: number = DEFAULT_CHAIN) { const domain = window.location.host; const origin = window.location.origin; - const response = await getNonce(address); - const nonce = (await response.json()).nonce; + const nonce = await getNonce(address); // signature is valid only for 10 mins - const expirationTime = new Date(Date.now() + 10 * 60 * 1000).toISOString(); + const expirationTime = new Date(Date.now() + 10 * 60 * 1000); - const message = new SiweMessage({ + const message = createSiweMessage({ domain, address, statement, @@ -96,5 +94,5 @@ async function createSiweMessage(address: `0x${string}`, statement: string, chai nonce, expirationTime, }); - return message.prepareMessage(); + return message; } diff --git a/web/src/utils/authoriseUser.ts b/web/src/utils/authoriseUser.ts index 8b29a11d6..9472b6d83 100644 --- a/web/src/utils/authoriseUser.ts +++ b/web/src/utils/authoriseUser.ts @@ -1,4 +1,5 @@ import { toast } from "react-toastify"; + import { OPTIONS } from "utils/wrapWithToast"; type AuthoriseUserData = { @@ -7,20 +8,25 @@ type AuthoriseUserData = { message: string; }; -export function authoriseUser(authData: AuthoriseUserData): Promise { - return toast.promise( +export function authoriseUser(authData: AuthoriseUserData): Promise { + const query = `mutation Login { + login(message: "${authData.message}", signature: "${authData.signature}") + } + `; + + return toast.promise( fetch(`/.netlify/functions/authUser`, { method: "POST", headers: { "Content-Type": "application/json", }, - body: JSON.stringify(authData), + body: JSON.stringify({ query }), }).then(async (response) => { if (response.status !== 200) { const error = await response.json().catch(() => ({ message: "Error signing in" })); throw new Error(error.message); } - return response; + return (await response.json()).data.login.access_token; }), { pending: `Signing in User...`, @@ -35,16 +41,24 @@ export function authoriseUser(authData: AuthoriseUserData): Promise { ); } -export function getNonce(address: string): Promise { - return toast.promise( +export function getNonce(address: string): Promise { + const query = `mutation GetNonce { + nonce(address: "${address}") +}`; + + return toast.promise( fetch(`/.netlify/functions/getNonce?address=${address}`, { - method: "GET", + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ query }), }).then(async (response) => { if (response.status !== 200) { const error = await response.json().catch(() => ({ message: "Error getting nonce" })); throw new Error(error.message); } - return response; + return (await response.json()).data.nonce; }), { error: { diff --git a/yarn.lock b/yarn.lock index 7b790ff62..869a54de2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6625,7 +6625,7 @@ __metadata: supabase: "npm:^1.133.3" typescript: "npm:^5.3.3" vanilla-jsoneditor: "npm:^0.21.4" - viem: "npm:^2.1.0" + viem: "npm:^2.17.3" vite: "npm:^5.2.10" vite-plugin-node-polyfills: "npm:^0.21.0" vite-plugin-svgr: "npm:^4.2.0" @@ -7773,7 +7773,7 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:^1.1.0, @noble/curves@npm:^1.4.2, @noble/curves@npm:~1.4.0": +"@noble/curves@npm:~1.4.0": version: 1.4.2 resolution: "@noble/curves@npm:1.4.2" dependencies: @@ -7817,7 +7817,7 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.4.0, @noble/hashes@npm:^1.1.2, @noble/hashes@npm:^1.4.0, @noble/hashes@npm:~1.4.0": +"@noble/hashes@npm:1.4.0, @noble/hashes@npm:^1.1.2, @noble/hashes@npm:~1.4.0": version: 1.4.0 resolution: "@noble/hashes@npm:1.4.0" checksum: e156e65794c473794c52fa9d06baf1eb20903d0d96719530f523cc4450f6c721a957c544796e6efd0197b2296e7cd70efeb312f861465e17940a3e3c7e0febc6 @@ -9192,6 +9192,13 @@ __metadata: languageName: node linkType: hard +"@scure/base@npm:~1.1.6": + version: 1.1.7 + resolution: "@scure/base@npm:1.1.7" + checksum: fc50ffaab36cb46ff9fa4dc5052a06089ab6a6707f63d596bb34aaaec76173c9a564ac312a0b981b5e7a5349d60097b8878673c75d6cbfc4da7012b63a82099b + languageName: node + linkType: hard + "@scure/bip32@npm:1.1.5": version: 1.1.5 resolution: "@scure/bip32@npm:1.1.5" @@ -9236,6 +9243,17 @@ __metadata: languageName: node linkType: hard +"@scure/bip32@npm:1.4.0": + version: 1.4.0 + resolution: "@scure/bip32@npm:1.4.0" + dependencies: + "@noble/curves": "npm:~1.4.0" + "@noble/hashes": "npm:~1.4.0" + "@scure/base": "npm:~1.1.6" + checksum: 6cd5062d902564d9e970597ec8b1adacb415b2eadfbb95aee1a1a0480a52eb0de4d294d3753aa8b48548064c9795ed108d348a31a8ce3fc88785377bb12c63b9 + languageName: node + linkType: hard + "@scure/bip39@npm:1.1.1": version: 1.1.1 resolution: "@scure/bip39@npm:1.1.1" @@ -9266,6 +9284,16 @@ __metadata: languageName: node linkType: hard +"@scure/bip39@npm:1.3.0": + version: 1.3.0 + resolution: "@scure/bip39@npm:1.3.0" + dependencies: + "@noble/hashes": "npm:~1.4.0" + "@scure/base": "npm:~1.1.6" + checksum: 7d71fd58153de22fe8cd65b525f6958a80487bc9d0fbc32c71c328aeafe41fa259f989d2f1e0fa4fdfeaf83b8fcf9310d52ed9862987e46c2f2bfb9dd8cf9fc1 + languageName: node + linkType: hard + "@selderee/plugin-htmlparser2@npm:^0.6.0": version: 0.6.0 resolution: "@selderee/plugin-htmlparser2@npm:0.6.0" @@ -12679,6 +12707,34 @@ __metadata: languageName: node linkType: hard +"abitype@npm:1.0.5": + version: 1.0.5 + resolution: "abitype@npm:1.0.5" + peerDependencies: + typescript: ">=5.0.4" + zod: ^3 >=3.19.1 + peerDependenciesMeta: + zod: + optional: true + checksum: 2ea02b160753f3a35ab407bc2abcd356982a13128a5a2d5de206d18ab88d2d69116b58428b0369c5d22338fec059d452a1900d95dc98d19b0b532308c80c4b4d + languageName: node + linkType: hard + +"abitype@npm:0.9.8": + version: 0.9.8 + resolution: "abitype@npm:0.9.8" + peerDependencies: + typescript: ">=5.0.4" + zod: ^3 >=3.19.1 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + checksum: 1acd0d9687945dd78442b71bd84ff3b9dceae27d15f0d8b14b16554a0c8c9518eeb971ff8e94d507f4d9f05a8a8b91eb8fafd735eaecebac37d5c5a4aac06d8e + languageName: node + linkType: hard + "abitype@npm:1.0.5": version: 1.0.5 resolution: "abitype@npm:1.0.5" @@ -23743,6 +23799,15 @@ __metadata: languageName: node linkType: hard +"isows@npm:1.0.4": + version: 1.0.4 + resolution: "isows@npm:1.0.4" + peerDependencies: + ws: "*" + checksum: a3ee62e3d6216abb3adeeb2a551fe2e7835eac87b05a6ecc3e7739259bf5f8e83290501f49e26137390c8093f207fc3378d4a7653aab76ad7bbab4b2dba9c5b9 + languageName: node + linkType: hard + "isstream@npm:~0.1.2": version: 0.1.2 resolution: "isstream@npm:0.1.2" @@ -36442,9 +36507,9 @@ __metadata: languageName: node linkType: hard -"viem@npm:^2.1.1, viem@npm:^2.17.5": - version: 2.17.8 - resolution: "viem@npm:2.17.8" +"viem@npm:^2.17.3": + version: 2.17.4 + resolution: "viem@npm:2.17.4" dependencies: "@adraffy/ens-normalize": "npm:1.10.0" "@noble/curves": "npm:1.4.0" @@ -36454,12 +36519,19 @@ __metadata: abitype: "npm:1.0.5" isows: "npm:1.0.4" ws: "npm:8.17.1" + "@noble/curves": "npm:1.4.0" + "@noble/hashes": "npm:1.4.0" + "@scure/bip32": "npm:1.4.0" + "@scure/bip39": "npm:1.3.0" + abitype: "npm:1.0.5" + isows: "npm:1.0.4" + ws: "npm:8.17.1" peerDependencies: typescript: ">=5.0.4" peerDependenciesMeta: typescript: optional: true - checksum: ae009818ac2c8a7a612e1a90edf2a171d99616de837f502d8ca6287cd7c5ef8f33b4eb21135e30b554d379d7c109a3d2b8ecfce458ec848dea32878088101f6f + checksum: 29e2b7d9034ea0520c47ef25cd8048078f8a0bacd24e1c468ee2ec5c51f780429e1bbb914611b54b8ee19f7eb251e171af67db7f27b97209c16cf8e917b405bf languageName: node linkType: hard @@ -37747,6 +37819,21 @@ __metadata: languageName: node linkType: hard +"ws@npm:8.17.1": + version: 8.17.1 + resolution: "ws@npm:8.17.1" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 1769532b6fdab9ff659f0b17810e7501831d34ecca23fd179ee64091dd93a51f42c59f6c7bb4c7a384b6c229aca8076fb312aa35626257c18081511ef62a161d + languageName: node + linkType: hard + "ws@npm:8.17.1": version: 8.17.1 resolution: "ws@npm:8.17.1" From 7172065728a146212bc6b5451ab4a191389033a1 Mon Sep 17 00:00:00 2001 From: Harman-singh-waraich Date: Fri, 26 Jul 2024 15:44:54 +0530 Subject: [PATCH 02/28] chore(web): update-queries --- web/.env.devnet-neo.public | 1 + web/.env.devnet-university.public | 1 + web/.env.devnet.public | 1 + web/.env.local.public | 1 + web/.env.mainnet-neo.public | 1 + web/.env.testnet.public | 1 + web/src/components/EnsureAuth.tsx | 2 +- web/src/utils/authoriseUser.ts | 42 ++++++++++++++++++++++++------- 8 files changed, 40 insertions(+), 10 deletions(-) diff --git a/web/.env.devnet-neo.public b/web/.env.devnet-neo.public index 3c58752cd..ccf47427b 100644 --- a/web/.env.devnet-neo.public +++ b/web/.env.devnet-neo.public @@ -5,6 +5,7 @@ export REACT_APP_DRT_ARBSEPOLIA_SUBGRAPH=https://api.studio.thegraph.com/query/6 export REACT_APP_STATUS_URL=https://kleros-v2-devnet.betteruptime.com/badge export REACT_APP_GENESIS_BLOCK_ARBSEPOLIA=3084598 export REACT_APP_ARBITRATOR_TYPE=neo +export REACT_APP_ATLAS_URI=http://localhost:3000/graphql export WALLETCONNECT_PROJECT_ID= export ALCHEMY_API_KEY= export NODE_OPTIONS='--max-old-space-size=7680' diff --git a/web/.env.devnet-university.public b/web/.env.devnet-university.public index fb26be0b6..2ae062b0a 100644 --- a/web/.env.devnet-university.public +++ b/web/.env.devnet-university.public @@ -5,6 +5,7 @@ export REACT_APP_DRT_ARBSEPOLIA_SUBGRAPH=https://api.studio.thegraph.com/query/6 export REACT_APP_STATUS_URL=https://kleros-v2-devnet.betteruptime.com/badge export REACT_APP_GENESIS_BLOCK_ARBSEPOLIA=3084598 export REACT_APP_ARBITRATOR_TYPE=university +export REACT_APP_ATLAS_URI=http://localhost:3000/graphql export WALLETCONNECT_PROJECT_ID= export ALCHEMY_API_KEY= export NODE_OPTIONS='--max-old-space-size=7680' diff --git a/web/.env.devnet.public b/web/.env.devnet.public index 0fca24c7b..943c2148d 100644 --- a/web/.env.devnet.public +++ b/web/.env.devnet.public @@ -4,6 +4,7 @@ export REACT_APP_CORE_SUBGRAPH=https://api.studio.thegraph.com/query/61738/klero export REACT_APP_DRT_ARBSEPOLIA_SUBGRAPH=https://api.studio.thegraph.com/query/61738/kleros-v2-drt-arbisep-devnet/version/latest export REACT_APP_STATUS_URL=https://kleros-v2-devnet.betteruptime.com/badge export REACT_APP_GENESIS_BLOCK_ARBSEPOLIA=3084598 +export REACT_APP_ATLAS_URI=http://localhost:3000/graphql export WALLETCONNECT_PROJECT_ID= export ALCHEMY_API_KEY= export NODE_OPTIONS='--max-old-space-size=7680' diff --git a/web/.env.local.public b/web/.env.local.public index 3128b605e..b71f31e93 100644 --- a/web/.env.local.public +++ b/web/.env.local.public @@ -2,6 +2,7 @@ export REACT_APP_DEPLOYMENT=devnet export REACT_APP_CORE_SUBGRAPH=http://localhost:8000/subgraphs/name/kleros/kleros-v2-core-local export REACT_APP_DRT_ARBSEPOLIA_SUBGRAPH=https://api.thegraph.com/subgraphs/name/alcercu/templateregistrydevnet +export REACT_APP_ATLAS_URI=http://localhost:3000/graphql export WALLETCONNECT_PROJECT_ID= export ALCHEMY_API_KEY= export NODE_OPTIONS='--max-old-space-size=7680' diff --git a/web/.env.mainnet-neo.public b/web/.env.mainnet-neo.public index 269f04862..a0eb52416 100644 --- a/web/.env.mainnet-neo.public +++ b/web/.env.mainnet-neo.public @@ -5,6 +5,7 @@ export REACT_APP_DRT_ARBMAINNET_SUBGRAPH=https://api.studio.thegraph.com/query/6 export REACT_APP_STATUS_URL=https://kleros-v2-devnet.betteruptime.com/badge export REACT_APP_GENESIS_BLOCK_ARBMAINNET=190274403 export REACT_APP_ARBITRATOR_TYPE=neo +export REACT_APP_ATLAS_URI=http://localhost:3000/graphql export WALLETCONNECT_PROJECT_ID= export ALCHEMY_API_KEY= export NODE_OPTIONS='--max-old-space-size=7680' diff --git a/web/.env.testnet.public b/web/.env.testnet.public index da92c1742..ab5c07779 100644 --- a/web/.env.testnet.public +++ b/web/.env.testnet.public @@ -3,6 +3,7 @@ export REACT_APP_DEPLOYMENT=testnet export REACT_APP_CORE_SUBGRAPH=https://api.studio.thegraph.com/query/61738/kleros-v2-core-testnet/version/latest export REACT_APP_DRT_ARBSEPOLIA_SUBGRAPH=https://api.studio.thegraph.com/query/61738/kleros-v2-drt-arbisep-devnet/version/latest export REACT_APP_STATUS_URL=https://kleros-v2.betteruptime.com/badge +export REACT_APP_ATLAS_URI=http://localhost:3000/graphql export REACT_APP_GENESIS_BLOCK_ARBSEPOLIA=3842783 export WALLETCONNECT_PROJECT_ID= export ALCHEMY_API_KEY= diff --git a/web/src/components/EnsureAuth.tsx b/web/src/components/EnsureAuth.tsx index 0b500b271..63524ad00 100644 --- a/web/src/components/EnsureAuth.tsx +++ b/web/src/components/EnsureAuth.tsx @@ -30,7 +30,7 @@ export const EnsureAuth: React.FC = ({ children, className }) => { const payload = jwt.decodeJwt(authToken); - if ((payload?.id as string).toLowerCase() !== address.toLowerCase()) return false; + if ((payload?.sub as string)?.toLowerCase() !== address.toLowerCase()) return false; if (payload.exp && payload.exp < Date.now() / 1000) return false; return true; diff --git a/web/src/utils/authoriseUser.ts b/web/src/utils/authoriseUser.ts index 9472b6d83..56eb36ca9 100644 --- a/web/src/utils/authoriseUser.ts +++ b/web/src/utils/authoriseUser.ts @@ -7,26 +7,41 @@ type AuthoriseUserData = { signature: `0x${string}`; message: string; }; +const atlasUri = import.meta.env.REACT_APP_ATLAS_URI ?? ""; export function authoriseUser(authData: AuthoriseUserData): Promise { - const query = `mutation Login { - login(message: "${authData.message}", signature: "${authData.signature}") + const query = `mutation Login($message: String!, $signature: String!) { + login(message: $message, signature: $signature) } `; + const variables = { + message: authData.message, + signature: authData.signature, + }; return toast.promise( - fetch(`/.netlify/functions/authUser`, { + fetch(atlasUri, { method: "POST", headers: { "Content-Type": "application/json", }, - body: JSON.stringify({ query }), + body: JSON.stringify({ query, variables }), }).then(async (response) => { - if (response.status !== 200) { + if (!response.ok) { const error = await response.json().catch(() => ({ message: "Error signing in" })); throw new Error(error.message); } - return (await response.json()).data.login.access_token; + + const result = await response.json(); + + const token = result.data.login.accessToken; + + // TODO ugly fix until a more standard return type in decided upon in atlas + if (token) { + return token; + } else { + throw new Error(result.data); + } }), { pending: `Signing in User...`, @@ -47,18 +62,27 @@ export function getNonce(address: string): Promise { }`; return toast.promise( - fetch(`/.netlify/functions/getNonce?address=${address}`, { + fetch(atlasUri, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ query }), }).then(async (response) => { - if (response.status !== 200) { + if (!response.ok) { const error = await response.json().catch(() => ({ message: "Error getting nonce" })); throw new Error(error.message); } - return (await response.json()).data.nonce; + const result = await response.json(); + + const nonce = result.data.nonce; + + // TODO ugly fix until a more standard return type in decided upon in atlas + if (nonce) { + return nonce; + } else { + throw new Error(result.data); + } }), { error: { From f4543726efb0e6ea73d116c026f2aa12102d51fc Mon Sep 17 00:00:00 2001 From: Harman-singh-waraich Date: Fri, 26 Jul 2024 15:55:01 +0530 Subject: [PATCH 03/28] chore: update-netlify-functions-to-test-notifications --- web/netlify/functions/authUser.ts | 117 ----------------------- web/netlify/functions/getNonce.ts | 50 ---------- web/netlify/middleware/authMiddleware.ts | 8 +- 3 files changed, 4 insertions(+), 171 deletions(-) delete mode 100644 web/netlify/functions/authUser.ts delete mode 100644 web/netlify/functions/getNonce.ts diff --git a/web/netlify/functions/authUser.ts b/web/netlify/functions/authUser.ts deleted file mode 100644 index 8c4b18cd3..000000000 --- a/web/netlify/functions/authUser.ts +++ /dev/null @@ -1,117 +0,0 @@ -import middy from "@middy/core"; -import jsonBodyParser from "@middy/http-json-body-parser"; -import { createClient } from "@supabase/supabase-js"; -import { ethers } from "ethers"; -import * as jwt from "jose"; -import { SiweMessage } from "siwe"; - -import { ETH_SIGNATURE_REGEX, DEFAULT_CHAIN, isProductionDeployment } from "consts/processEnvConsts"; - -import { netlifyUri, netlifyDeployUri, netlifyDeployPrimeUri } from "src/generatedNetlifyInfo.json"; -import { Database } from "src/types/supabase-notification"; - -const authUser = async (event) => { - try { - if (!event.body) { - throw new Error("No body provided"); - } - - const signature = event?.body?.signature; - if (!signature) { - throw new Error("Missing key : signature"); - } - - if (!ETH_SIGNATURE_REGEX.test(signature)) { - throw new Error("Invalid signature"); - } - - const message = event?.body?.message; - if (!message) { - throw new Error("Missing key : message"); - } - - const address = event?.body?.address; - if (!address) { - throw new Error("Missing key : address"); - } - - const siweMessage = new SiweMessage(message); - - if ( - !( - (netlifyUri && netlifyUri === siweMessage.uri) || - (netlifyDeployUri && netlifyDeployUri === siweMessage.uri) || - (netlifyDeployPrimeUri && netlifyDeployPrimeUri === siweMessage.uri) - ) - ) { - console.debug( - `Invalid URI: expected one of [${netlifyUri} ${netlifyDeployUri} ${netlifyDeployPrimeUri}] but got ${siweMessage.uri}` - ); - throw new Error(`Invalid URI`); - } - - if (siweMessage.chainId !== DEFAULT_CHAIN) { - console.debug(`Invalid chain ID: expected ${DEFAULT_CHAIN} but got ${siweMessage.chainId}`); - throw new Error(`Invalid chain ID`); - } - - const lowerCaseAddress = siweMessage.address.toLowerCase(); - if (lowerCaseAddress !== address.toLowerCase()) { - throw new Error("Address mismatch in provided address and message"); - } - - const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_CLIENT_API_KEY!); - - // get nonce from db, if its null that means it was already used - const { error: nonceError, data: nonceData } = await supabase - .from("user-nonce") - .select("nonce") - .eq("address", lowerCaseAddress) - .single(); - - if (nonceError || !nonceData?.nonce) { - throw new Error("Unable to fetch nonce from DB"); - } - - try { - // If the main Alchemy API key is permissioned, it won't work in a Netlify Function so we use a dedicated API key - const alchemyApiKey = process.env.ALCHEMY_FUNCTIONS_API_KEY ?? process.env.ALCHEMY_API_KEY; - const alchemyChain = isProductionDeployment() ? "arb-mainnet" : "arb-sepolia"; - const alchemyRpcURL = `https://${alchemyChain}.g.alchemy.com/v2/${alchemyApiKey}`; - const provider = new ethers.providers.JsonRpcProvider(alchemyRpcURL); - await siweMessage.verify({ signature, nonce: nonceData.nonce, time: new Date().toISOString() }, { provider }); - } catch (err) { - throw new Error("Invalid signer: " + JSON.stringify(err)); - } - - const { error } = await supabase.from("user-nonce").delete().match({ address: lowerCaseAddress }); - - if (error) { - throw new Error("Error updating nonce in DB"); - } - - const issuer = process.env.JWT_ISSUER ?? "Kleros"; // ex :- Kleros - const audience = process.env.JWT_AUDIENCE ?? "Court"; // ex :- Court, Curate, Escrow - const authExp = process.env.JWT_EXP_TIME ?? "2h"; - const secret = process.env.JWT_SECRET; - - if (!secret) { - throw new Error("Secret not set in environment"); - } - // user verified, generate auth token - const encodedSecret = new TextEncoder().encode(secret); - - const token = await new jwt.SignJWT({ id: address.toLowerCase() }) - .setProtectedHeader({ alg: "HS256" }) - .setIssuer(issuer) - .setAudience(audience) - .setExpirationTime(authExp) - .sign(encodedSecret); - - return { statusCode: 200, body: JSON.stringify({ message: "User authorised", token }) }; - } catch (err) { - return { statusCode: 500, body: JSON.stringify({ message: `${err}` }) }; - } -}; - -export const handler = middy(authUser).use(jsonBodyParser()); diff --git a/web/netlify/functions/getNonce.ts b/web/netlify/functions/getNonce.ts deleted file mode 100644 index 9219c5eab..000000000 --- a/web/netlify/functions/getNonce.ts +++ /dev/null @@ -1,50 +0,0 @@ -import middy from "@middy/core"; -import { createClient } from "@supabase/supabase-js"; -import { generateNonce } from "siwe"; - -import { ETH_ADDRESS_REGEX } from "consts/processEnvConsts"; - -import { Database } from "src/types/supabase-notification"; - -const getNonce = async (event) => { - try { - const { queryStringParameters } = event; - - if (!queryStringParameters?.address) { - return { - statusCode: 400, - body: JSON.stringify({ message: "Invalid query parameters" }), - }; - } - - const { address } = queryStringParameters; - - if (!ETH_ADDRESS_REGEX.test(address)) { - throw new Error("Invalid Ethereum address format"); - } - - const lowerCaseAddress = address.toLowerCase() as `0x${string}`; - - const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_CLIENT_API_KEY!); - - // generate nonce and save in db - const nonce = generateNonce(); - - const { error } = await supabase - .from("user-nonce") - .upsert({ address: lowerCaseAddress, nonce: nonce }) - .eq("address", lowerCaseAddress); - - if (error) { - throw error; - } - - return { statusCode: 200, body: JSON.stringify({ nonce }) }; - } catch (err) { - console.log(err); - - return { statusCode: 500, message: `Error ${err?.message ?? err}` }; - } -}; - -export const handler = middy(getNonce); diff --git a/web/netlify/middleware/authMiddleware.ts b/web/netlify/middleware/authMiddleware.ts index 8835e5f01..f199a3467 100644 --- a/web/netlify/middleware/authMiddleware.ts +++ b/web/netlify/middleware/authMiddleware.ts @@ -14,9 +14,9 @@ export const authMiddleware = () => { } try { - const issuer = process.env.JWT_ISSUER ?? "Kleros"; // ex :- Kleros - const audience = process.env.JWT_AUDIENCE ?? "Court"; // ex :- Court, Curate, Escrow - const secret = process.env.JWT_SECRET; + // const secret = process.env.JWT_SECRET; + // TODO testing purpose + const secret = "u03tzA7Un9w+fetret343t6U2YaOlINle1E4avjc="; if (!secret) { throw new Error("Secret not set in environment"); @@ -24,7 +24,7 @@ export const authMiddleware = () => { const encodedSecret = new TextEncoder().encode(secret); - const { payload } = await jwt.jwtVerify(authToken, encodedSecret, { issuer, audience }); + const { payload } = await jwt.jwtVerify(authToken, encodedSecret); // add auth details to event request.event.auth = payload; From e9854552bf2bf19662fcc209b5950f14de438e49 Mon Sep 17 00:00:00 2001 From: Harman-singh-waraich Date: Fri, 26 Jul 2024 15:59:53 +0530 Subject: [PATCH 04/28] chore: update-lockfile --- yarn.lock | 124 +++++++++++------------------------------------------- 1 file changed, 24 insertions(+), 100 deletions(-) diff --git a/yarn.lock b/yarn.lock index 869a54de2..9518b2f9d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7773,7 +7773,7 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:~1.4.0": +"@noble/curves@npm:^1.1.0, @noble/curves@npm:^1.4.0, @noble/curves@npm:^1.4.2, @noble/curves@npm:~1.4.0": version: 1.4.2 resolution: "@noble/curves@npm:1.4.2" dependencies: @@ -7817,7 +7817,7 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.4.0, @noble/hashes@npm:^1.1.2, @noble/hashes@npm:~1.4.0": +"@noble/hashes@npm:1.4.0, @noble/hashes@npm:^1.1.2, @noble/hashes@npm:^1.4.0, @noble/hashes@npm:~1.4.0": version: 1.4.0 resolution: "@noble/hashes@npm:1.4.0" checksum: e156e65794c473794c52fa9d06baf1eb20903d0d96719530f523cc4450f6c721a957c544796e6efd0197b2296e7cd70efeb312f861465e17940a3e3c7e0febc6 @@ -9192,13 +9192,6 @@ __metadata: languageName: node linkType: hard -"@scure/base@npm:~1.1.6": - version: 1.1.7 - resolution: "@scure/base@npm:1.1.7" - checksum: fc50ffaab36cb46ff9fa4dc5052a06089ab6a6707f63d596bb34aaaec76173c9a564ac312a0b981b5e7a5349d60097b8878673c75d6cbfc4da7012b63a82099b - languageName: node - linkType: hard - "@scure/bip32@npm:1.1.5": version: 1.1.5 resolution: "@scure/bip32@npm:1.1.5" @@ -9243,17 +9236,6 @@ __metadata: languageName: node linkType: hard -"@scure/bip32@npm:1.4.0": - version: 1.4.0 - resolution: "@scure/bip32@npm:1.4.0" - dependencies: - "@noble/curves": "npm:~1.4.0" - "@noble/hashes": "npm:~1.4.0" - "@scure/base": "npm:~1.1.6" - checksum: 6cd5062d902564d9e970597ec8b1adacb415b2eadfbb95aee1a1a0480a52eb0de4d294d3753aa8b48548064c9795ed108d348a31a8ce3fc88785377bb12c63b9 - languageName: node - linkType: hard - "@scure/bip39@npm:1.1.1": version: 1.1.1 resolution: "@scure/bip39@npm:1.1.1" @@ -9284,16 +9266,6 @@ __metadata: languageName: node linkType: hard -"@scure/bip39@npm:1.3.0": - version: 1.3.0 - resolution: "@scure/bip39@npm:1.3.0" - dependencies: - "@noble/hashes": "npm:~1.4.0" - "@scure/base": "npm:~1.1.6" - checksum: 7d71fd58153de22fe8cd65b525f6958a80487bc9d0fbc32c71c328aeafe41fa259f989d2f1e0fa4fdfeaf83b8fcf9310d52ed9862987e46c2f2bfb9dd8cf9fc1 - languageName: node - linkType: hard - "@selderee/plugin-htmlparser2@npm:^0.6.0": version: 0.6.0 resolution: "@selderee/plugin-htmlparser2@npm:0.6.0" @@ -12707,34 +12679,6 @@ __metadata: languageName: node linkType: hard -"abitype@npm:1.0.5": - version: 1.0.5 - resolution: "abitype@npm:1.0.5" - peerDependencies: - typescript: ">=5.0.4" - zod: ^3 >=3.19.1 - peerDependenciesMeta: - zod: - optional: true - checksum: 2ea02b160753f3a35ab407bc2abcd356982a13128a5a2d5de206d18ab88d2d69116b58428b0369c5d22338fec059d452a1900d95dc98d19b0b532308c80c4b4d - languageName: node - linkType: hard - -"abitype@npm:0.9.8": - version: 0.9.8 - resolution: "abitype@npm:0.9.8" - peerDependencies: - typescript: ">=5.0.4" - zod: ^3 >=3.19.1 - peerDependenciesMeta: - typescript: - optional: true - zod: - optional: true - checksum: 1acd0d9687945dd78442b71bd84ff3b9dceae27d15f0d8b14b16554a0c8c9518eeb971ff8e94d507f4d9f05a8a8b91eb8fafd735eaecebac37d5c5a4aac06d8e - languageName: node - linkType: hard - "abitype@npm:1.0.5": version: 1.0.5 resolution: "abitype@npm:1.0.5" @@ -23799,15 +23743,6 @@ __metadata: languageName: node linkType: hard -"isows@npm:1.0.4": - version: 1.0.4 - resolution: "isows@npm:1.0.4" - peerDependencies: - ws: "*" - checksum: a3ee62e3d6216abb3adeeb2a551fe2e7835eac87b05a6ecc3e7739259bf5f8e83290501f49e26137390c8093f207fc3378d4a7653aab76ad7bbab4b2dba9c5b9 - languageName: node - linkType: hard - "isstream@npm:~0.1.2": version: 0.1.2 resolution: "isstream@npm:0.1.2" @@ -36486,24 +36421,25 @@ __metadata: languageName: node linkType: hard -"viem@npm:^2.1.0": - version: 2.1.0 - resolution: "viem@npm:2.1.0" +"viem@npm:^2.1.1, viem@npm:^2.17.5": + version: 2.18.2 + resolution: "viem@npm:2.18.2" dependencies: "@adraffy/ens-normalize": "npm:1.10.0" - "@noble/curves": "npm:1.2.0" - "@noble/hashes": "npm:1.3.2" - "@scure/bip32": "npm:1.3.2" - "@scure/bip39": "npm:1.2.1" - abitype: "npm:0.10.0" - isows: "npm:1.0.3" - ws: "npm:8.13.0" + "@noble/curves": "npm:1.4.0" + "@noble/hashes": "npm:1.4.0" + "@scure/bip32": "npm:1.4.0" + "@scure/bip39": "npm:1.3.0" + abitype: "npm:1.0.5" + isows: "npm:1.0.4" + webauthn-p256: "npm:0.0.5" + ws: "npm:8.17.1" peerDependencies: typescript: ">=5.0.4" peerDependenciesMeta: typescript: optional: true - checksum: 594406567b7666eaffe60b4ae5a7d1b98953d12e1b455c07594ae38e8f41bf910dfa2ddab69911fa9fd092b82e5a347f64df2919e5424b3d13b2e6c5ad2f7f92 + checksum: 34dc1a8039f984d84002be03dd35dd258837c2a393304a18fd84b3de5645510cc906e0552af33d476fbb6a6f94143b35e623c39f194d5a4b7d467f937bf6c174 languageName: node linkType: hard @@ -36519,13 +36455,6 @@ __metadata: abitype: "npm:1.0.5" isows: "npm:1.0.4" ws: "npm:8.17.1" - "@noble/curves": "npm:1.4.0" - "@noble/hashes": "npm:1.4.0" - "@scure/bip32": "npm:1.4.0" - "@scure/bip39": "npm:1.3.0" - abitype: "npm:1.0.5" - isows: "npm:1.0.4" - ws: "npm:8.17.1" peerDependencies: typescript: ">=5.0.4" peerDependenciesMeta: @@ -37038,6 +36967,16 @@ __metadata: languageName: node linkType: hard +"webauthn-p256@npm:0.0.5": + version: 0.0.5 + resolution: "webauthn-p256@npm:0.0.5" + dependencies: + "@noble/curves": "npm:^1.4.0" + "@noble/hashes": "npm:^1.4.0" + checksum: 6bf5d1857dfb99ecb3b318af06eddea874c10135e6ebb9f046270f5cbb162933bc6caf77aedb033e14c09971dda544a5fb367ac545e4ec8001b309ba517555cf + languageName: node + linkType: hard + "webcrypto-core@npm:^1.7.7": version: 1.7.7 resolution: "webcrypto-core@npm:1.7.7" @@ -37819,21 +37758,6 @@ __metadata: languageName: node linkType: hard -"ws@npm:8.17.1": - version: 8.17.1 - resolution: "ws@npm:8.17.1" - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ">=5.0.2" - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - checksum: 1769532b6fdab9ff659f0b17810e7501831d34ecca23fd179ee64091dd93a51f42c59f6c7bb4c7a384b6c229aca8076fb312aa35626257c18081511ef62a161d - languageName: node - linkType: hard - "ws@npm:8.17.1": version: 8.17.1 resolution: "ws@npm:8.17.1" From 53b7b2ac02b2406dcde6ccd8baabf1beadff7fc6 Mon Sep 17 00:00:00 2001 From: Harman-singh-waraich Date: Fri, 26 Jul 2024 16:44:25 +0530 Subject: [PATCH 05/28] fix(functions): fix-address-fetching-from-jwt-payload --- web/netlify/functions/fetch-settings.ts | 2 +- web/netlify/functions/update-settings.ts | 2 +- web/netlify/middleware/authMiddleware.ts | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/web/netlify/functions/fetch-settings.ts b/web/netlify/functions/fetch-settings.ts index 73a4d04ad..393d8d1f4 100644 --- a/web/netlify/functions/fetch-settings.ts +++ b/web/netlify/functions/fetch-settings.ts @@ -6,7 +6,7 @@ import { authMiddleware } from "../middleware/authMiddleware"; const fetchSettings = async (event) => { try { - const address = event.auth.id; + const address = event.auth.sub; const lowerCaseAddress = address.toLowerCase() as `0x${string}`; const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_CLIENT_API_KEY!); diff --git a/web/netlify/functions/update-settings.ts b/web/netlify/functions/update-settings.ts index 4a30dddd3..a572f4de7 100644 --- a/web/netlify/functions/update-settings.ts +++ b/web/netlify/functions/update-settings.ts @@ -63,7 +63,7 @@ const updateSettings = async (event) => { const lowerCaseAddress = address.toLowerCase() as `0x${string}`; // Prevent using someone else's token - if (event?.auth?.id.toLowerCase() !== lowerCaseAddress) { + if (event?.auth?.sub.toLowerCase() !== lowerCaseAddress) { throw new Error("Unauthorised user"); } const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_CLIENT_API_KEY!); diff --git a/web/netlify/middleware/authMiddleware.ts b/web/netlify/middleware/authMiddleware.ts index f199a3467..d54d7f9bd 100644 --- a/web/netlify/middleware/authMiddleware.ts +++ b/web/netlify/middleware/authMiddleware.ts @@ -14,9 +14,7 @@ export const authMiddleware = () => { } try { - // const secret = process.env.JWT_SECRET; - // TODO testing purpose - const secret = "u03tzA7Un9w+fetret343t6U2YaOlINle1E4avjc="; + const secret = process.env.JWT_SECRET; if (!secret) { throw new Error("Secret not set in environment"); From 33b818e662df22d045f4bcb6deea0409d439863e Mon Sep 17 00:00:00 2001 From: Harman-singh-waraich Date: Fri, 26 Jul 2024 17:58:30 +0530 Subject: [PATCH 06/28] refactor(web): export-ensure-auth-as-default --- web/src/components/EnsureAuth.tsx | 4 +++- .../Header/navbar/Menu/Settings/Notifications/index.tsx | 2 +- .../pages/Cases/CaseDetails/Evidence/SubmitEvidenceModal.tsx | 2 +- web/src/pages/Resolver/index.tsx | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/web/src/components/EnsureAuth.tsx b/web/src/components/EnsureAuth.tsx index 63524ad00..f9099d32d 100644 --- a/web/src/components/EnsureAuth.tsx +++ b/web/src/components/EnsureAuth.tsx @@ -15,7 +15,7 @@ interface IEnsureAuth { className?: string; } -export const EnsureAuth: React.FC = ({ children, className }) => { +const EnsureAuth: React.FC = ({ children, className }) => { const localToken = window.sessionStorage.getItem("auth-token"); const [isLoading, setIsLoading] = useState(false); @@ -96,3 +96,5 @@ async function createMessage(address: `0x${string}`, statement: string, chainId: }); return message; } + +export default EnsureAuth; diff --git a/web/src/layout/Header/navbar/Menu/Settings/Notifications/index.tsx b/web/src/layout/Header/navbar/Menu/Settings/Notifications/index.tsx index eccd220fd..9cafa3954 100644 --- a/web/src/layout/Header/navbar/Menu/Settings/Notifications/index.tsx +++ b/web/src/layout/Header/navbar/Menu/Settings/Notifications/index.tsx @@ -4,8 +4,8 @@ import styled from "styled-components"; import TelegramLogo from "svgs/socialmedia/telegram.svg"; import { HERMES_TELEGRAM_BOT_URL } from "consts/index"; -import { EnsureAuth } from "components/EnsureAuth"; +import EnsureAuth from "components/EnsureAuth"; import { EnsureChain } from "components/EnsureChain"; import { ISettings } from "layout/Header/navbar/index"; diff --git a/web/src/pages/Cases/CaseDetails/Evidence/SubmitEvidenceModal.tsx b/web/src/pages/Cases/CaseDetails/Evidence/SubmitEvidenceModal.tsx index 63984acd0..24e74f74d 100644 --- a/web/src/pages/Cases/CaseDetails/Evidence/SubmitEvidenceModal.tsx +++ b/web/src/pages/Cases/CaseDetails/Evidence/SubmitEvidenceModal.tsx @@ -11,7 +11,7 @@ import { simulateEvidenceModuleSubmitEvidence } from "hooks/contracts/generated" import { uploadFormDataToIPFS } from "utils/uploadFormDataToIPFS"; import { wrapWithToast, OPTIONS as toastOptions } from "utils/wrapWithToast"; -import { EnsureAuth } from "components/EnsureAuth"; +import EnsureAuth from "components/EnsureAuth"; import { EnsureChain } from "components/EnsureChain"; const StyledModal = styled(Modal)` diff --git a/web/src/pages/Resolver/index.tsx b/web/src/pages/Resolver/index.tsx index 06a71c3dd..d370896ac 100644 --- a/web/src/pages/Resolver/index.tsx +++ b/web/src/pages/Resolver/index.tsx @@ -9,7 +9,7 @@ import { landscapeStyle } from "styles/landscapeStyle"; import { responsiveSize } from "styles/responsiveSize"; import ConnectWallet from "components/ConnectWallet"; -import { EnsureAuth } from "components/EnsureAuth"; +import EnsureAuth from "components/EnsureAuth"; import HeroImage from "components/HeroImage"; import HowItWorks from "components/HowItWorks"; import Resolver from "components/Popup/MiniGuides/DisputeResolver"; From ba2442ade131fd6c752b4f3a35efe967510a938d Mon Sep 17 00:00:00 2001 From: Harman-singh-waraich Date: Thu, 8 Aug 2024 16:35:34 +0530 Subject: [PATCH 07/28] refactor(web): structured-atlas-interaction --- web/src/app.tsx | 133 ++++++++++++++------------- web/src/components/EnsureAuth.tsx | 84 ++--------------- web/src/context/AtlasProvider.tsx | 118 ++++++++++++++++++++++++ web/src/utils/atlas/createMessage.ts | 23 +++++ web/src/utils/atlas/getNonce.ts | 39 ++++++++ web/src/utils/atlas/index.ts | 3 + web/src/utils/atlas/loginUser.ts | 49 ++++++++++ web/src/utils/authoriseUser.ts | 96 ------------------- 8 files changed, 307 insertions(+), 238 deletions(-) create mode 100644 web/src/context/AtlasProvider.tsx create mode 100644 web/src/utils/atlas/createMessage.ts create mode 100644 web/src/utils/atlas/getNonce.ts create mode 100644 web/src/utils/atlas/index.ts create mode 100644 web/src/utils/atlas/loginUser.ts delete mode 100644 web/src/utils/authoriseUser.ts diff --git a/web/src/app.tsx b/web/src/app.tsx index 650e2823d..078fa282a 100644 --- a/web/src/app.tsx +++ b/web/src/app.tsx @@ -4,6 +4,7 @@ import { Route } from "react-router-dom"; import "react-loading-skeleton/dist/skeleton.css"; import "react-toastify/dist/ReactToastify.css"; +import AtlasProvider from "context/AtlasProvider"; import GraphqlBatcherProvider from "context/GraphqlBatcher"; import IsListProvider from "context/IsListProvider"; import { NewDisputeProvider } from "context/NewDisputeContext"; @@ -29,71 +30,73 @@ const App: React.FC = () => { - - - - }> - }> - - - } - /> - }> - - - } - /> - }> - - - } - /> - }> - - - } - /> - }> - - - } - /> - }> - - - } - /> - }> - - - } - /> - Justice not found here ¯\_( ͡° ͜ʖ ͡°)_/¯} /> - - - - + + + + + }> + }> + + + } + /> + }> + + + } + /> + }> + + + } + /> + }> + + + } + /> + }> + + + } + /> + }> + + + } + /> + }> + + + } + /> + Justice not found here ¯\_( ͡° ͜ʖ ͡°)_/¯} /> + + + + + diff --git a/web/src/components/EnsureAuth.tsx b/web/src/components/EnsureAuth.tsx index f9099d32d..3414bdf72 100644 --- a/web/src/components/EnsureAuth.tsx +++ b/web/src/components/EnsureAuth.tsx @@ -1,14 +1,10 @@ -import React, { useMemo, useState } from "react"; +import React from "react"; -import * as jwt from "jose"; -import { createSiweMessage } from "viem/siwe"; -import { useAccount, useChainId, useSignMessage } from "wagmi"; +import { useAccount } from "wagmi"; import { Button } from "@kleros/ui-components-library"; -import { DEFAULT_CHAIN } from "consts/chains"; -import { useSessionStorage } from "hooks/useSessionStorage"; -import { authoriseUser, getNonce } from "utils/authoriseUser"; +import { useAtlasProvider } from "context/AtlasProvider"; interface IEnsureAuth { children: React.ReactElement; @@ -16,85 +12,19 @@ interface IEnsureAuth { } const EnsureAuth: React.FC = ({ children, className }) => { - const localToken = window.sessionStorage.getItem("auth-token"); - const [isLoading, setIsLoading] = useState(false); - - const [authToken, setAuthToken] = useSessionStorage("auth-token", localToken); const { address } = useAccount(); - const chainId = useChainId(); - - const { signMessageAsync } = useSignMessage(); - - const isVerified = useMemo(() => { - if (!authToken || !address) return false; - - const payload = jwt.decodeJwt(authToken); - - if ((payload?.sub as string)?.toLowerCase() !== address.toLowerCase()) return false; - if (payload.exp && payload.exp < Date.now() / 1000) return false; - - return true; - }, [authToken, address]); - - const handleSignIn = async () => { - try { - setIsLoading(true); - if (!address) return; - - const message = await createMessage(address, "Sign In to Kleros with Ethereum.", chainId); - - const signature = await signMessageAsync({ message }); - - if (!signature) return; - - authoriseUser({ - address, - signature, - message, - }) - .then(async (token) => { - setAuthToken(token); - }) - .catch((err) => console.log({ err })) - .finally(() => setIsLoading(false)); - } catch (err) { - setIsLoading(false); - console.log({ err }); - } - }; - + const { isVerified, isSigningIn, authoriseUser } = useAtlasProvider(); return isVerified ? ( children ) : (