From f7d50262949cf4c5cbfa43abd93440b5ed9f937d Mon Sep 17 00:00:00 2001 From: Austin Buckler Date: Tue, 27 Dec 2022 22:49:20 -0700 Subject: [PATCH 01/17] started on siwe prototype-client ui. --- templates/base/api/base.ts.hbs | 3 +++ templates/base/client/.hathora/client.ts.hbs | 5 ++++- templates/base/client/prototype-ui/Login.tsx.hbs | 14 ++++++++++++++ templates/base/client/prototype-ui/SiweLogin.tsx | 9 +++++++++ templates/base/server/.hathora/store.ts.hbs | 2 ++ 5 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 templates/base/client/prototype-ui/SiweLogin.tsx diff --git a/templates/base/api/base.ts.hbs b/templates/base/api/base.ts.hbs index bcde1878..69ec507f 100644 --- a/templates/base/api/base.ts.hbs +++ b/templates/base/api/base.ts.hbs @@ -55,6 +55,7 @@ export interface {{capitalize @key}}UserData { email: string; locale: string; picture: string; +{{else if (eq @key "siwe")}} {{/if}} } {{/each}} @@ -74,6 +75,8 @@ export function getUserDisplayName(user: UserData) { return user.name; {{else if (eq @key "google")}} return user.name; + {{else if (eq @key "siwe")}} + return user.publicAddress; {{else if (eq @key "email")}} return user.email; {{/if}} diff --git a/templates/base/client/.hathora/client.ts.hbs b/templates/base/client/.hathora/client.ts.hbs index 07043729..61e31bb9 100644 --- a/templates/base/client/.hathora/client.ts.hbs +++ b/templates/base/client/.hathora/client.ts.hbs @@ -48,7 +48,10 @@ export class HathoraClient { public async loginGoogle(idToken: string): Promise { return this._client.loginGoogle(idToken); } - +{{else if (eq @key "siwe")}} + public async loginSiwe(mesage: string, signature: string): Promise { + return this._client.loginSiwe(message, signature); + } {{/if}} {{/each}} public async create(token: string, request: IInitializeRequest): Promise { diff --git a/templates/base/client/prototype-ui/Login.tsx.hbs b/templates/base/client/prototype-ui/Login.tsx.hbs index bf97e7f8..7c9618cb 100644 --- a/templates/base/client/prototype-ui/Login.tsx.hbs +++ b/templates/base/client/prototype-ui/Login.tsx.hbs @@ -72,6 +72,20 @@ export function Login({ client, setToken }: { client: HathoraClient; setToken: ( onFailure={(error) => toast.error("Authentication error: " + error.details)} /> +{{else if (eq @key "siwe")}} +
+ + client.loginSiwe(message, signature) + .then(token => { + sessionStorage.setItem(client.appId, token) + setToken(token) + }) + .catch(e => toast.error("Authentication error: " + e.reason)) + } + onFailure={(error) => toast.error("Authentication error: " + error.details)} + /> +
{{/if}} {{/each}} diff --git a/templates/base/client/prototype-ui/SiweLogin.tsx b/templates/base/client/prototype-ui/SiweLogin.tsx new file mode 100644 index 00000000..21b0e517 --- /dev/null +++ b/templates/base/client/prototype-ui/SiweLogin.tsx @@ -0,0 +1,9 @@ +type SiweLoginProps = { + statement?: string; + onSuccess: (payload: { message: string; signature: string; }) => Promise | void; + onFailure: (error: unknown) => Promise | void; +} + +export const SiweLogin = ({ statement, onFailure, onSuccess }: SiweLoginProps) => { + return null; +} \ No newline at end of file diff --git a/templates/base/server/.hathora/store.ts.hbs b/templates/base/server/.hathora/store.ts.hbs index a265d3b6..ca70cb2b 100644 --- a/templates/base/server/.hathora/store.ts.hbs +++ b/templates/base/server/.hathora/store.ts.hbs @@ -160,6 +160,8 @@ const coordinator = await register({ {{else if (eq @key "google")}} google: { clientId: "{{clientId}}" }, {{/if}} +{{else if (eq @key "siwe")}} + publicAddress: {}, {{/each}} }, store: new Store(), From 1c3a0e28e292370350ec16ac3c767e9cf959d701 Mon Sep 17 00:00:00 2001 From: Austin Buckler Date: Tue, 27 Dec 2022 23:13:57 -0700 Subject: [PATCH 02/17] fix: incorrect ethers version for siwe. feat: added chat-siwe example for sign-in with ethereum. --- examples/chat-siwe/.gitignore | 9 ++++ examples/chat-siwe/Dockerfile | 15 ++++++ examples/chat-siwe/README.md | 11 +++++ examples/chat-siwe/hathora.yml | 25 ++++++++++ examples/chat-siwe/server/impl.ts | 48 +++++++++++++++++++ examples/chat-siwe/server/package-lock.json | 36 ++++++++++++++ examples/chat-siwe/server/package.json | 8 ++++ examples/chat-siwe/server/tsconfig.json | 10 ++++ src/generate.ts | 1 + .../base/client/prototype-ui/SiweLogin.tsx | 15 ++++++ .../base/client/prototype-ui/package.json.hbs | 4 ++ 11 files changed, 182 insertions(+) create mode 100644 examples/chat-siwe/.gitignore create mode 100644 examples/chat-siwe/Dockerfile create mode 100644 examples/chat-siwe/README.md create mode 100644 examples/chat-siwe/hathora.yml create mode 100644 examples/chat-siwe/server/impl.ts create mode 100644 examples/chat-siwe/server/package-lock.json create mode 100644 examples/chat-siwe/server/package.json create mode 100644 examples/chat-siwe/server/tsconfig.json diff --git a/examples/chat-siwe/.gitignore b/examples/chat-siwe/.gitignore new file mode 100644 index 00000000..40b33d31 --- /dev/null +++ b/examples/chat-siwe/.gitignore @@ -0,0 +1,9 @@ +.hathora +node_modules +dist +.env +/api +/data/* +!/data/saves +/client/prototype-ui/* +!/client/prototype-ui/plugins diff --git a/examples/chat-siwe/Dockerfile b/examples/chat-siwe/Dockerfile new file mode 100644 index 00000000..dc005ee3 --- /dev/null +++ b/examples/chat-siwe/Dockerfile @@ -0,0 +1,15 @@ +FROM node:16 + +WORKDIR /app + +RUN npm i -g hathora@0.10.2 + +ENV NODE_ENV=production + +ARG APP_SECRET +ENV APP_SECRET=${APP_SECRET} + +COPY . . +RUN hathora build --only server + +CMD ["node", "server/dist/index.mjs"] diff --git a/examples/chat-siwe/README.md b/examples/chat-siwe/README.md new file mode 100644 index 00000000..8d326ffe --- /dev/null +++ b/examples/chat-siwe/README.md @@ -0,0 +1,11 @@ +Try it at: https://hathora-chat.surge.sh/ + +![image](https://user-images.githubusercontent.com/5400947/149680221-98474638-e88c-47db-a3bd-8bca56a611aa.png) + +To run locally: + +- install hathora (`npm install -g hathora`) +- clone or download this repo +- cd into this directory +- run `hathora dev` +- visit http://localhost:3000 in your browser diff --git a/examples/chat-siwe/hathora.yml b/examples/chat-siwe/hathora.yml new file mode 100644 index 00000000..69f5ef03 --- /dev/null +++ b/examples/chat-siwe/hathora.yml @@ -0,0 +1,25 @@ +types: + Message: + text: string + sentAt: int + sentBy: UserId + sentTo: UserId? + RoomState: + users: UserId[] + messages: Message[] + +methods: + joinRoom: + leaveRoom: + sendPublicMessage: + text: string + sendPrivateMessage: + text: string + to: UserId + +auth: + siwe: + statement: "Sign in to Hathora" + +userState: RoomState +error: string diff --git a/examples/chat-siwe/server/impl.ts b/examples/chat-siwe/server/impl.ts new file mode 100644 index 00000000..687cfa78 --- /dev/null +++ b/examples/chat-siwe/server/impl.ts @@ -0,0 +1,48 @@ +import { Methods, Context } from "./.hathora/methods"; +import { Response } from "../api/base"; +import { UserId, RoomState, ISendPublicMessageRequest, ISendPrivateMessageRequest } from "../api/types"; + +export class Impl implements Methods { + initialize(): RoomState { + return { users: [], messages: [] }; + } + joinRoom(state: RoomState, userId: string): Response { + if (state.users.includes(userId)) { + return Response.error("Already joined!"); + } + state.users.push(userId); + return Response.ok(); + } + leaveRoom(state: RoomState, userId: string): Response { + if (!state.users.includes(userId)) { + return Response.error("Not joined"); + } + state.users.splice(state.users.indexOf(userId), 1); + return Response.ok(); + } + sendPublicMessage(state: RoomState, userId: UserId, ctx: Context, request: ISendPublicMessageRequest): Response { + if (!state.users.includes(userId)) { + return Response.error("Not joined"); + } + state.messages.push({ text: request.text, sentAt: ctx.time, sentBy: userId }); + return Response.ok(); + } + sendPrivateMessage(state: RoomState, userId: UserId, ctx: Context, request: ISendPrivateMessageRequest): Response { + if (!state.users.includes(userId)) { + return Response.error("Not joined"); + } + if (!state.users.includes(request.to)) { + return Response.error("Recpient not joined"); + } + state.messages.push({ text: request.text, sentAt: ctx.time, sentBy: userId, sentTo: request.to }); + return Response.ok(); + } + getUserState(state: RoomState, userId: UserId): RoomState { + return { + users: state.users, + messages: state.messages.filter( + (msg) => msg.sentBy === userId || msg.sentTo === userId || msg.sentTo === undefined + ), + }; + } +} diff --git a/examples/chat-siwe/server/package-lock.json b/examples/chat-siwe/server/package-lock.json new file mode 100644 index 00000000..d07a71b6 --- /dev/null +++ b/examples/chat-siwe/server/package-lock.json @@ -0,0 +1,36 @@ +{ + "name": "chat-server", + "version": "0.0.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "chat-server", + "version": "0.0.1", + "devDependencies": { + "typescript": "^4.5.2" + } + }, + "node_modules/typescript": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", + "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + } + }, + "dependencies": { + "typescript": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", + "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", + "dev": true + } + } +} diff --git a/examples/chat-siwe/server/package.json b/examples/chat-siwe/server/package.json new file mode 100644 index 00000000..cf8c69aa --- /dev/null +++ b/examples/chat-siwe/server/package.json @@ -0,0 +1,8 @@ +{ + "name": "chat-server", + "version": "0.0.1", + "type": "module", + "devDependencies": { + "typescript": "^4.5.2" + } +} diff --git a/examples/chat-siwe/server/tsconfig.json b/examples/chat-siwe/server/tsconfig.json new file mode 100644 index 00000000..d0fc07e7 --- /dev/null +++ b/examples/chat-siwe/server/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "esModuleInterop": true, + "module": "esnext", + "strict": true, + "target": "esnext", + "moduleResolution": "node", + "isolatedModules": true + } +} diff --git a/src/generate.ts b/src/generate.ts index e2f649a4..ee4f0461 100644 --- a/src/generate.ts +++ b/src/generate.ts @@ -18,6 +18,7 @@ const HathoraConfig = z anonymous: z.optional(z.object({ separator: z.optional(z.string()).default("-") }).strict()), nickname: z.optional(z.object({}).strict()), google: z.optional(z.object({ clientId: z.string() }).strict()), + siwe: z.optional(z.object({ statement: z.optional(z.string()).default("Sign in with Ethereum") }).strict()), }) .strict(), userState: z.string(), diff --git a/templates/base/client/prototype-ui/SiweLogin.tsx b/templates/base/client/prototype-ui/SiweLogin.tsx index 21b0e517..3a889908 100644 --- a/templates/base/client/prototype-ui/SiweLogin.tsx +++ b/templates/base/client/prototype-ui/SiweLogin.tsx @@ -1,9 +1,24 @@ +import { ethers } from 'ethers'; +import { SiweMessage } from 'siwe'; +import { useCallback, useMemo } from 'react' + type SiweLoginProps = { statement?: string; onSuccess: (payload: { message: string; signature: string; }) => Promise | void; onFailure: (error: unknown) => Promise | void; } +const useConnectWallet = () => { + const provider = useMemo(() => new ethers.providers.Web3Provider((window as any).ethereum), []) + const signer = useMemo(() => provider.getSigner(), []) + const connectWallet = useCallback(async () => provider.send('eth_requestAccounts', []), [provider]) + return useMemo(() => ({ connectWallet, signer, provider }), [connectWallet, signer, provider]) +} + +const useSiweLogin = () => { + return null +} + export const SiweLogin = ({ statement, onFailure, onSuccess }: SiweLoginProps) => { return null; } \ No newline at end of file diff --git a/templates/base/client/prototype-ui/package.json.hbs b/templates/base/client/prototype-ui/package.json.hbs index cfa50a60..890f245b 100644 --- a/templates/base/client/prototype-ui/package.json.hbs +++ b/templates/base/client/prototype-ui/package.json.hbs @@ -8,6 +8,10 @@ {{#if auth.google}} "react-google-login": "5.2.2", {{/if}} + {{#if auth.siwe}} + "ethers": "5.5.1", + "siwe": "1.1.6", + {{/if}} "react-router-dom": "6.2.1", "react-toastify": "8.1.0" }, From edab85dd52981aa3de7cea0bbee5cb0cff81bf11 Mon Sep 17 00:00:00 2001 From: cooperate Date: Wed, 28 Dec 2022 09:28:10 -0800 Subject: [PATCH 03/17] Component and type for UserData. --- templates/base/api/base.ts.hbs | 1 + .../base/client/prototype-ui/SiweLogin.tsx | 30 +++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/templates/base/api/base.ts.hbs b/templates/base/api/base.ts.hbs index 69ec507f..417ecf60 100644 --- a/templates/base/api/base.ts.hbs +++ b/templates/base/api/base.ts.hbs @@ -56,6 +56,7 @@ export interface {{capitalize @key}}UserData { locale: string; picture: string; {{else if (eq @key "siwe")}} + publicAddress: string; {{/if}} } {{/each}} diff --git a/templates/base/client/prototype-ui/SiweLogin.tsx b/templates/base/client/prototype-ui/SiweLogin.tsx index 3a889908..26aa6743 100644 --- a/templates/base/client/prototype-ui/SiweLogin.tsx +++ b/templates/base/client/prototype-ui/SiweLogin.tsx @@ -1,5 +1,6 @@ import { ethers } from 'ethers'; import { SiweMessage } from 'siwe'; +import { JsonRpcSigner } from '@ethersproject/providers'; import { useCallback, useMemo } from 'react' type SiweLoginProps = { @@ -15,10 +16,33 @@ const useConnectWallet = () => { return useMemo(() => ({ connectWallet, signer, provider }), [connectWallet, signer, provider]) } -const useSiweLogin = () => { - return null +const useSiweLogin = (signer: JsonRpcSigner) => { + //not entirely sure where we are pulling most of these values from yet + const siweMessage: SiweMessage = { + domain: '', + address: '', + statement: '', + uri: '', + version: '1', + chainId: 1 + } + return useMemo(() => ({ siweMessage}), [siweMessage]) } export const SiweLogin = ({ statement, onFailure, onSuccess }: SiweLoginProps) => { - return null; + const { connectWallet, signer } = useConnectWallet() + const { siweMessage } = useSiweLogin(signer) + const handleLogin = useCallback(async () => { + try { + await connectWallet() + const message = new SiweMessage(siweMessage) + const signature = await signer.signMessage(message.toString()) + await onSuccess({ message: siweMessage, signature }) + } catch (error) { + onFailure(error) + } + }, [connectWallet, onFailure, onSuccess, siweMessage, signer]) + return ( + + ) } \ No newline at end of file From c16832adbecd3759a68ccb4472dcc69fdbbf0a30 Mon Sep 17 00:00:00 2001 From: cooperate Date: Wed, 28 Dec 2022 09:32:17 -0800 Subject: [PATCH 04/17] Login import conditional for siwe --- templates/base/client/prototype-ui/Login.tsx.hbs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/templates/base/client/prototype-ui/Login.tsx.hbs b/templates/base/client/prototype-ui/Login.tsx.hbs index 7c9618cb..09634bd6 100644 --- a/templates/base/client/prototype-ui/Login.tsx.hbs +++ b/templates/base/client/prototype-ui/Login.tsx.hbs @@ -3,6 +3,9 @@ import { toast } from "react-toastify"; {{#if auth.google}} import { GoogleLogin } from "react-google-login"; {{/if}} +{{#if auth.siwe}} +import { SiweLogin } from "./SiweLogin"; +{{/if}} import { HathoraClient } from "../.hathora/client"; export function Login({ client, setToken }: { client: HathoraClient; setToken: (token: string) => void }) { From 132772c2bc843514a84ec02c1c32030a48cb02da Mon Sep 17 00:00:00 2001 From: cooperate Date: Wed, 28 Dec 2022 10:26:25 -0800 Subject: [PATCH 05/17] More dataset changes. --- .../base/client/prototype-ui/Login.tsx.hbs | 4 +- .../client/prototype-ui/SiweLogin.tsx.hbs | 49 +++++++++++++++++++ templates/base/server/.hathora/store.ts.hbs | 7 ++- 3 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 templates/base/client/prototype-ui/SiweLogin.tsx.hbs diff --git a/templates/base/client/prototype-ui/Login.tsx.hbs b/templates/base/client/prototype-ui/Login.tsx.hbs index 09634bd6..ffbb5d26 100644 --- a/templates/base/client/prototype-ui/Login.tsx.hbs +++ b/templates/base/client/prototype-ui/Login.tsx.hbs @@ -78,8 +78,10 @@ export function Login({ client, setToken }: { client: HathoraClient; setToken: ( {{else if (eq @key "siwe")}}
- client.loginSiwe(message, signature) + client + .loginSiwe(message, signature) .then(token => { sessionStorage.setItem(client.appId, token) setToken(token) diff --git a/templates/base/client/prototype-ui/SiweLogin.tsx.hbs b/templates/base/client/prototype-ui/SiweLogin.tsx.hbs new file mode 100644 index 00000000..d441a555 --- /dev/null +++ b/templates/base/client/prototype-ui/SiweLogin.tsx.hbs @@ -0,0 +1,49 @@ +import React, { useCallback, useMemo } from "react"; +import ReactDOM from "react-dom"; +import { ethers } from 'ethers'; +import { SiweMessage } from 'siwe'; +import { JsonRpcSigner } from '@ethersproject/providers'; + +type SiweLoginProps = { + statement?: string; + onSuccess: (payload: { message: string; signature: string; }) => Promise | void; + onFailure: (error: unknown) => Promise | void; +} + +const useConnectWallet = () => { + const provider = useMemo(() => new ethers.providers.Web3Provider((window as any).ethereum), []) + const signer = useMemo(() => provider.getSigner(), []) + const connectWallet = useCallback(async () => provider.send('eth_requestAccounts', []), [provider]) + return useMemo(() => ({ connectWallet, signer, provider }), [connectWallet, signer, provider]) +} + +const useSiweLogin = (signer: JsonRpcSigner) => { + //not entirely sure where we are pulling most of these values from yet + const siweMessage: SiweMessage = { + domain: '', + address: '', + statement: 'Sign in to Hathora', + uri: '', + version: '1', + chainId: 11155111 + } + return useMemo(() => ({ siweMessage}), [siweMessage]) +} + +export const SiweLogin = ({ statement, onFailure, onSuccess }: SiweLoginProps) => { + const { connectWallet, signer } = useConnectWallet() + const { siweMessage } = useSiweLogin(signer) + const handleLogin = useCallback(async () => { + try { + await connectWallet() + const message = new SiweMessage(siweMessage) + const signature = await signer.signMessage(message.toString()) + await onSuccess({ message: siweMessage, signature }) + } catch (error) { + onFailure(error) + } + }, [connectWallet, onFailure, onSuccess, siweMessage, signer]) + return ( + + ) +} \ No newline at end of file diff --git a/templates/base/server/.hathora/store.ts.hbs b/templates/base/server/.hathora/store.ts.hbs index ca70cb2b..4077078a 100644 --- a/templates/base/server/.hathora/store.ts.hbs +++ b/templates/base/server/.hathora/store.ts.hbs @@ -159,9 +159,12 @@ const coordinator = await register({ nickname: {}, {{else if (eq @key "google")}} google: { clientId: "{{clientId}}" }, -{{/if}} {{else if (eq @key "siwe")}} - publicAddress: {}, + siwe: { publicAddress: "{{publicAddress}}", + chainId: "{{chainId}}", + statement: "{{statement}}", + }, +{{/if}} {{/each}} }, store: new Store(), From 33a4d3d66e6e5a9a56fb1868713fa555a6726cf4 Mon Sep 17 00:00:00 2001 From: cooperate Date: Wed, 28 Dec 2022 10:28:50 -0800 Subject: [PATCH 06/17] Remove non hbs component file. --- .../base/client/prototype-ui/SiweLogin.tsx | 48 ------------------- 1 file changed, 48 deletions(-) delete mode 100644 templates/base/client/prototype-ui/SiweLogin.tsx diff --git a/templates/base/client/prototype-ui/SiweLogin.tsx b/templates/base/client/prototype-ui/SiweLogin.tsx deleted file mode 100644 index 26aa6743..00000000 --- a/templates/base/client/prototype-ui/SiweLogin.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { ethers } from 'ethers'; -import { SiweMessage } from 'siwe'; -import { JsonRpcSigner } from '@ethersproject/providers'; -import { useCallback, useMemo } from 'react' - -type SiweLoginProps = { - statement?: string; - onSuccess: (payload: { message: string; signature: string; }) => Promise | void; - onFailure: (error: unknown) => Promise | void; -} - -const useConnectWallet = () => { - const provider = useMemo(() => new ethers.providers.Web3Provider((window as any).ethereum), []) - const signer = useMemo(() => provider.getSigner(), []) - const connectWallet = useCallback(async () => provider.send('eth_requestAccounts', []), [provider]) - return useMemo(() => ({ connectWallet, signer, provider }), [connectWallet, signer, provider]) -} - -const useSiweLogin = (signer: JsonRpcSigner) => { - //not entirely sure where we are pulling most of these values from yet - const siweMessage: SiweMessage = { - domain: '', - address: '', - statement: '', - uri: '', - version: '1', - chainId: 1 - } - return useMemo(() => ({ siweMessage}), [siweMessage]) -} - -export const SiweLogin = ({ statement, onFailure, onSuccess }: SiweLoginProps) => { - const { connectWallet, signer } = useConnectWallet() - const { siweMessage } = useSiweLogin(signer) - const handleLogin = useCallback(async () => { - try { - await connectWallet() - const message = new SiweMessage(siweMessage) - const signature = await signer.signMessage(message.toString()) - await onSuccess({ message: siweMessage, signature }) - } catch (error) { - onFailure(error) - } - }, [connectWallet, onFailure, onSuccess, siweMessage, signer]) - return ( - - ) -} \ No newline at end of file From a0272a40ad9ed22fcd2f4286d00ad9fdd97ccadd Mon Sep 17 00:00:00 2001 From: cooperate Date: Wed, 28 Dec 2022 11:00:16 -0800 Subject: [PATCH 07/17] generate properties for siwe. --- src/generate.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/generate.ts b/src/generate.ts index ee4f0461..e10d3339 100644 --- a/src/generate.ts +++ b/src/generate.ts @@ -18,7 +18,10 @@ const HathoraConfig = z anonymous: z.optional(z.object({ separator: z.optional(z.string()).default("-") }).strict()), nickname: z.optional(z.object({}).strict()), google: z.optional(z.object({ clientId: z.string() }).strict()), - siwe: z.optional(z.object({ statement: z.optional(z.string()).default("Sign in with Ethereum") }).strict()), + siwe: z.optional(z.object({ statement: z.optional(z.string()).default("Sign in with Ethereum"), + chainId: z.optional(z.number().int()).default(11155111), + publicAddress: z.optional(z.string()), + }).strict()), }) .strict(), userState: z.string(), From ae5ad27f7ed138f07572c044c6b620a4d3288ec5 Mon Sep 17 00:00:00 2001 From: cooperate Date: Wed, 28 Dec 2022 11:28:07 -0800 Subject: [PATCH 08/17] Syntax error. --- templates/base/client/.hathora/client.ts.hbs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/templates/base/client/.hathora/client.ts.hbs b/templates/base/client/.hathora/client.ts.hbs index 61e31bb9..ebea0511 100644 --- a/templates/base/client/.hathora/client.ts.hbs +++ b/templates/base/client/.hathora/client.ts.hbs @@ -48,8 +48,9 @@ export class HathoraClient { public async loginGoogle(idToken: string): Promise { return this._client.loginGoogle(idToken); } + {{else if (eq @key "siwe")}} - public async loginSiwe(mesage: string, signature: string): Promise { + public async loginSiwe(message: string, signature: string): Promise { return this._client.loginSiwe(message, signature); } {{/if}} From 030cf47c94aa23977401030c5f58816681e5fbc4 Mon Sep 17 00:00:00 2001 From: cooperate Date: Wed, 28 Dec 2022 13:45:47 -0800 Subject: [PATCH 09/17] More templates updates. --- .../base/client/prototype-ui/Login.tsx.hbs | 4 +- .../client/prototype-ui/SiweLogin.tsx.hbs | 40 ++++++++++++------- templates/base/server/.hathora/store.ts.hbs | 5 +-- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/templates/base/client/prototype-ui/Login.tsx.hbs b/templates/base/client/prototype-ui/Login.tsx.hbs index ffbb5d26..234c8878 100644 --- a/templates/base/client/prototype-ui/Login.tsx.hbs +++ b/templates/base/client/prototype-ui/Login.tsx.hbs @@ -86,9 +86,9 @@ export function Login({ client, setToken }: { client: HathoraClient; setToken: ( sessionStorage.setItem(client.appId, token) setToken(token) }) - .catch(e => toast.error("Authentication error: " + e.reason)) + .catch(e => toast.error(`Authentication error: ${JSON.stringify(e)}`)) } - onFailure={(error) => toast.error("Authentication error: " + error.details)} + onFailure={(e) => toast.error(`Authentication error: ${JSON.stringify(e)}`)} />
{{/if}} diff --git a/templates/base/client/prototype-ui/SiweLogin.tsx.hbs b/templates/base/client/prototype-ui/SiweLogin.tsx.hbs index d441a555..a5c2d3dc 100644 --- a/templates/base/client/prototype-ui/SiweLogin.tsx.hbs +++ b/templates/base/client/prototype-ui/SiweLogin.tsx.hbs @@ -3,6 +3,7 @@ import ReactDOM from "react-dom"; import { ethers } from 'ethers'; import { SiweMessage } from 'siwe'; import { JsonRpcSigner } from '@ethersproject/providers'; +import axios from "axios"; type SiweLoginProps = { statement?: string; @@ -17,32 +18,43 @@ const useConnectWallet = () => { return useMemo(() => ({ connectWallet, signer, provider }), [connectWallet, signer, provider]) } -const useSiweLogin = (signer: JsonRpcSigner) => { - //not entirely sure where we are pulling most of these values from yet - const siweMessage: SiweMessage = { - domain: '', - address: '', - statement: 'Sign in to Hathora', - uri: '', +const useAuthServer = () => { + const getNonce = useCallback(async () => { + //make request to auth server for nonce using axios + return axios.get('http://localhost:3001/nonce') + }, []); + return useMemo(() => ({ getNonce }), [getNonce]) +} + +const getSiweMessage = (address: string, nonce: string, statement?: string): Partial => ( + { + domain: window.location.host, + address: address, + statement: statement || 'Sign in to Hathora', + uri: window.location.origin, version: '1', - chainId: 11155111 + chainId: 1, + nonce } - return useMemo(() => ({ siweMessage}), [siweMessage]) -} +); export const SiweLogin = ({ statement, onFailure, onSuccess }: SiweLoginProps) => { const { connectWallet, signer } = useConnectWallet() - const { siweMessage } = useSiweLogin(signer) + const { getNonce } = useAuthServer(); const handleLogin = useCallback(async () => { try { await connectWallet() - const message = new SiweMessage(siweMessage) + const address = await signer.getAddress(); + const nonce = (await getNonce())?.data; + const siweMessagePartial = getSiweMessage(address, nonce, statement) + const message = new SiweMessage(siweMessagePartial) const signature = await signer.signMessage(message.toString()) - await onSuccess({ message: siweMessage, signature }) + const messagePrepared = message.prepareMessage(); + await onSuccess({ message: messagePrepared, signature }) } catch (error) { onFailure(error) } - }, [connectWallet, onFailure, onSuccess, siweMessage, signer]) + }, [connectWallet, onFailure, onSuccess, signer]) return ( ) diff --git a/templates/base/server/.hathora/store.ts.hbs b/templates/base/server/.hathora/store.ts.hbs index 4077078a..e0405b48 100644 --- a/templates/base/server/.hathora/store.ts.hbs +++ b/templates/base/server/.hathora/store.ts.hbs @@ -160,10 +160,7 @@ const coordinator = await register({ {{else if (eq @key "google")}} google: { clientId: "{{clientId}}" }, {{else if (eq @key "siwe")}} - siwe: { publicAddress: "{{publicAddress}}", - chainId: "{{chainId}}", - statement: "{{statement}}", - }, + {{/if}} {{/each}} }, From ad763d906acaaf2328343aa96de2b94e0f98309c Mon Sep 17 00:00:00 2001 From: cooperate Date: Wed, 28 Dec 2022 14:07:34 -0800 Subject: [PATCH 10/17] Updated methods for nonceTokens. --- templates/base/client/prototype-ui/Login.tsx.hbs | 4 ++-- templates/base/client/prototype-ui/SiweLogin.tsx.hbs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/base/client/prototype-ui/Login.tsx.hbs b/templates/base/client/prototype-ui/Login.tsx.hbs index 234c8878..8397276e 100644 --- a/templates/base/client/prototype-ui/Login.tsx.hbs +++ b/templates/base/client/prototype-ui/Login.tsx.hbs @@ -79,9 +79,9 @@ export function Login({ client, setToken }: { client: HathoraClient; setToken: (
+ onSuccess={({ message, signature, nonce }: any) => client - .loginSiwe(message, signature) + .loginSiwe(message, signature, nonceToken) .then(token => { sessionStorage.setItem(client.appId, token) setToken(token) diff --git a/templates/base/client/prototype-ui/SiweLogin.tsx.hbs b/templates/base/client/prototype-ui/SiweLogin.tsx.hbs index a5c2d3dc..4bef79e6 100644 --- a/templates/base/client/prototype-ui/SiweLogin.tsx.hbs +++ b/templates/base/client/prototype-ui/SiweLogin.tsx.hbs @@ -45,12 +45,12 @@ export const SiweLogin = ({ statement, onFailure, onSuccess }: SiweLoginProps) = try { await connectWallet() const address = await signer.getAddress(); - const nonce = (await getNonce())?.data; + const { nonce, nonceToken } = (await getNonce())?.data const siweMessagePartial = getSiweMessage(address, nonce, statement) const message = new SiweMessage(siweMessagePartial) const signature = await signer.signMessage(message.toString()) const messagePrepared = message.prepareMessage(); - await onSuccess({ message: messagePrepared, signature }) + await onSuccess({ message: messagePrepared, signature, nonceToken }) } catch (error) { onFailure(error) } From 2764cb91da3bb572d153c6a611323c5a3d102ad8 Mon Sep 17 00:00:00 2001 From: cooperate Date: Wed, 28 Dec 2022 14:18:08 -0800 Subject: [PATCH 11/17] nonceToken added. --- templates/base/client/.hathora/client.ts.hbs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/base/client/.hathora/client.ts.hbs b/templates/base/client/.hathora/client.ts.hbs index ebea0511..864c09e4 100644 --- a/templates/base/client/.hathora/client.ts.hbs +++ b/templates/base/client/.hathora/client.ts.hbs @@ -50,8 +50,8 @@ export class HathoraClient { } {{else if (eq @key "siwe")}} - public async loginSiwe(message: string, signature: string): Promise { - return this._client.loginSiwe(message, signature); + public async loginSiwe(message: string, signature: string, nonceToken: string): Promise { + return this._client.loginSiwe(message, signature, nonceToken); } {{/if}} {{/each}} From bbe9e38cfc13acb99f37481fb056e3340a1e5f6c Mon Sep 17 00:00:00 2001 From: cooperate Date: Wed, 28 Dec 2022 14:37:12 -0800 Subject: [PATCH 12/17] nonceToken property. --- templates/base/client/prototype-ui/Login.tsx.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/base/client/prototype-ui/Login.tsx.hbs b/templates/base/client/prototype-ui/Login.tsx.hbs index 8397276e..3ddee38d 100644 --- a/templates/base/client/prototype-ui/Login.tsx.hbs +++ b/templates/base/client/prototype-ui/Login.tsx.hbs @@ -79,7 +79,7 @@ export function Login({ client, setToken }: { client: HathoraClient; setToken: (
+ onSuccess={({ message, signature, nonceToken }: any) => client .loginSiwe(message, signature, nonceToken) .then(token => { From e374c9d5ba18c320c0872885ad7520667c8b5e4f Mon Sep 17 00:00:00 2001 From: cooperate Date: Wed, 28 Dec 2022 14:38:37 -0800 Subject: [PATCH 13/17] Types. --- templates/base/client/prototype-ui/SiweLogin.tsx.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/base/client/prototype-ui/SiweLogin.tsx.hbs b/templates/base/client/prototype-ui/SiweLogin.tsx.hbs index 4bef79e6..fa8d0664 100644 --- a/templates/base/client/prototype-ui/SiweLogin.tsx.hbs +++ b/templates/base/client/prototype-ui/SiweLogin.tsx.hbs @@ -7,7 +7,7 @@ import axios from "axios"; type SiweLoginProps = { statement?: string; - onSuccess: (payload: { message: string; signature: string; }) => Promise | void; + onSuccess: (payload: { message: string; signature: string; nonceToken: string; }) => Promise | void; onFailure: (error: unknown) => Promise | void; } From 9420858b1310c23e67bd9d730eb4aa92a452fa15 Mon Sep 17 00:00:00 2001 From: cooperate Date: Wed, 28 Dec 2022 14:41:07 -0800 Subject: [PATCH 14/17] Argh. --- templates/base/client/prototype-ui/Login.tsx.hbs | 4 ++-- templates/base/client/prototype-ui/SiweLogin.tsx.hbs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/base/client/prototype-ui/Login.tsx.hbs b/templates/base/client/prototype-ui/Login.tsx.hbs index 3ddee38d..69aaf2c6 100644 --- a/templates/base/client/prototype-ui/Login.tsx.hbs +++ b/templates/base/client/prototype-ui/Login.tsx.hbs @@ -4,7 +4,7 @@ import { toast } from "react-toastify"; import { GoogleLogin } from "react-google-login"; {{/if}} {{#if auth.siwe}} -import { SiweLogin } from "./SiweLogin"; +import { SiweLogin, SiweLoginProps } from "./SiweLogin"; {{/if}} import { HathoraClient } from "../.hathora/client"; @@ -79,7 +79,7 @@ export function Login({ client, setToken }: { client: HathoraClient; setToken: (
+ onSuccess={({ message, signature, nonceToken }: SiweLoginProps) => client .loginSiwe(message, signature, nonceToken) .then(token => { diff --git a/templates/base/client/prototype-ui/SiweLogin.tsx.hbs b/templates/base/client/prototype-ui/SiweLogin.tsx.hbs index fa8d0664..17c92259 100644 --- a/templates/base/client/prototype-ui/SiweLogin.tsx.hbs +++ b/templates/base/client/prototype-ui/SiweLogin.tsx.hbs @@ -5,7 +5,7 @@ import { SiweMessage } from 'siwe'; import { JsonRpcSigner } from '@ethersproject/providers'; import axios from "axios"; -type SiweLoginProps = { +export type SiweLoginProps = { statement?: string; onSuccess: (payload: { message: string; signature: string; nonceToken: string; }) => Promise | void; onFailure: (error: unknown) => Promise | void; From 4453fa8f53c5815d83c74846c83e0024c802ef7f Mon Sep 17 00:00:00 2001 From: cooperate Date: Wed, 28 Dec 2022 14:42:09 -0800 Subject: [PATCH 15/17] Argh. --- templates/base/client/prototype-ui/Login.tsx.hbs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/base/client/prototype-ui/Login.tsx.hbs b/templates/base/client/prototype-ui/Login.tsx.hbs index 69aaf2c6..4f93df25 100644 --- a/templates/base/client/prototype-ui/Login.tsx.hbs +++ b/templates/base/client/prototype-ui/Login.tsx.hbs @@ -4,7 +4,7 @@ import { toast } from "react-toastify"; import { GoogleLogin } from "react-google-login"; {{/if}} {{#if auth.siwe}} -import { SiweLogin, SiweLoginProps } from "./SiweLogin"; +import { SiweLogin } from "./SiweLogin"; {{/if}} import { HathoraClient } from "../.hathora/client"; @@ -79,7 +79,7 @@ export function Login({ client, setToken }: { client: HathoraClient; setToken: (
+ onSuccess={({ message, signature, nonceToken }: { message: string; signature: string; nonceToken: string; }) => client .loginSiwe(message, signature, nonceToken) .then(token => { From 0f1cacd4ce5f81702475c6afe8a807e0834638d3 Mon Sep 17 00:00:00 2001 From: cooperate Date: Wed, 28 Dec 2022 14:46:36 -0800 Subject: [PATCH 16/17] Argh. --- templates/base/client/prototype-ui/Login.tsx.hbs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/templates/base/client/prototype-ui/Login.tsx.hbs b/templates/base/client/prototype-ui/Login.tsx.hbs index 4f93df25..3ca8d14f 100644 --- a/templates/base/client/prototype-ui/Login.tsx.hbs +++ b/templates/base/client/prototype-ui/Login.tsx.hbs @@ -78,8 +78,8 @@ export function Login({ client, setToken }: { client: HathoraClient; setToken: ( {{else if (eq @key "siwe")}}
+ { client .loginSiwe(message, signature, nonceToken) .then(token => { @@ -87,8 +87,9 @@ export function Login({ client, setToken }: { client: HathoraClient; setToken: ( setToken(token) }) .catch(e => toast.error(`Authentication error: ${JSON.stringify(e)}`)) + } } - onFailure={(e) => toast.error(`Authentication error: ${JSON.stringify(e)}`)} + onFailure={(e) => {toast.error(`Authentication error: ${JSON.stringify(e)}`)}} />
{{/if}} From 207625eb6079572e53c243c59f0fc5ae417018be Mon Sep 17 00:00:00 2001 From: cooperate Date: Wed, 28 Dec 2022 15:15:56 -0800 Subject: [PATCH 17/17] Rearrange. --- templates/base/client/prototype-ui/SiweLogin.tsx.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/base/client/prototype-ui/SiweLogin.tsx.hbs b/templates/base/client/prototype-ui/SiweLogin.tsx.hbs index 17c92259..f0d4be8f 100644 --- a/templates/base/client/prototype-ui/SiweLogin.tsx.hbs +++ b/templates/base/client/prototype-ui/SiweLogin.tsx.hbs @@ -48,8 +48,8 @@ export const SiweLogin = ({ statement, onFailure, onSuccess }: SiweLoginProps) = const { nonce, nonceToken } = (await getNonce())?.data const siweMessagePartial = getSiweMessage(address, nonce, statement) const message = new SiweMessage(siweMessagePartial) - const signature = await signer.signMessage(message.toString()) const messagePrepared = message.prepareMessage(); + const signature = await signer.signMessage(messagePrepared) await onSuccess({ message: messagePrepared, signature, nonceToken }) } catch (error) { onFailure(error)