@@ -4,6 +4,13 @@ import * as automerge from '@automerge/automerge';
44import { uuid } from '@automerge/automerge' ;
55import { RepoContext } from '@automerge/automerge-repo-react-hooks' ;
66import { Identity , Key , Messages , SpaceEvents , type SpaceStorageEntry , Utils , store } from '@graphprotocol/hypergraph' ;
7+ import {
8+ getSessionNonce ,
9+ identityExists ,
10+ prepareSiweMessage ,
11+ restoreKeys ,
12+ signup ,
13+ } from '@graphprotocol/hypergraph/identity/login' ;
714import { useSelector as useSelectorStore } from '@xstate/store/react' ;
815import { Effect , Exit } from 'effect' ;
916import * as Schema from 'effect/Schema' ;
@@ -17,7 +24,6 @@ import {
1724 useRef ,
1825 useState ,
1926} from 'react' ;
20- import { SiweMessage } from 'siwe' ;
2127import type { Hex } from 'viem' ;
2228import { type Address , getAddress } from 'viem' ;
2329import { privateKeyToAccount } from 'viem/accounts' ;
@@ -127,45 +133,17 @@ export function HypergraphAppProvider({
127133 const sessionToken = useSelectorStore ( store , ( state ) => state . context . sessionToken ) ;
128134 const keys = useSelectorStore ( store , ( state ) => state . context . keys ) ;
129135
130- function prepareSiweMessage ( address : Address , nonce : string ) {
131- return new SiweMessage ( {
132- domain : window . location . host ,
133- address,
134- statement : 'Sign in to Hypergraph' ,
135- uri : window . location . origin ,
136- version : '1' ,
137- chainId,
138- nonce,
139- expirationTime : new Date ( Date . now ( ) + 1000 * 60 * 60 * 24 * 30 ) . toISOString ( ) ,
140- } ) . prepareMessage ( ) ;
141- }
142-
143- async function getSessionNonce ( accountId : string ) {
144- const nonceReq = { accountId } as const satisfies Messages . RequestLoginNonce ;
145- const res = await fetch ( new URL ( '/login/nonce' , syncServerUri ) , {
146- method : 'POST' ,
147- headers : {
148- 'Content-Type' : 'application/json' ,
149- } ,
150- body : JSON . stringify ( nonceReq ) ,
151- } ) ;
152- const decoded = Schema . decodeUnknownSync ( Messages . ResponseLoginNonce ) ( await res . json ( ) ) ;
153- return decoded . sessionNonce ;
154- }
155-
156- async function identityExists ( accountId : string ) {
157- const res = await fetch ( new URL ( `/identity?accountId=${ accountId } ` , syncServerUri ) , {
158- method : 'GET' ,
159- } ) ;
160- return res . status === 200 ;
161- }
162-
163136 async function loginWithWallet ( signer : Identity . Signer , accountId : Address , retryCount = 0 ) {
164137 const sessionToken = Identity . loadSyncServerSessionToken ( storage , accountId ) ;
165138 if ( ! sessionToken ) {
166- const sessionNonce = await getSessionNonce ( accountId ) ;
139+ const sessionNonce = await getSessionNonce ( accountId , syncServerUri ) ;
167140 // Use SIWE to login with the server and get a token
168- const message = prepareSiweMessage ( accountId , sessionNonce ) ;
141+ const message = prepareSiweMessage (
142+ accountId ,
143+ sessionNonce ,
144+ { host : window . location . host , origin : window . location . origin } ,
145+ chainId ,
146+ ) ;
169147 const signature = await signer . signMessage ( message ) ;
170148 const loginReq = { accountId, message, signature } as const satisfies Messages . RequestLogin ;
171149 const res = await fetch ( new URL ( '/login' , syncServerUri ) , {
@@ -178,7 +156,7 @@ export function HypergraphAppProvider({
178156 const decoded = Schema . decodeUnknownSync ( Messages . ResponseLogin ) ( await res . json ( ) ) ;
179157 Identity . storeAccountId ( storage , accountId ) ;
180158 Identity . storeSyncServerSessionToken ( storage , accountId , decoded . sessionToken ) ;
181- const keys = await restoreKeys ( signer , accountId , decoded . sessionToken ) ;
159+ const keys = await restoreKeys ( signer , accountId , decoded . sessionToken , syncServerUri , storage ) ;
182160 return {
183161 accountId,
184162 sessionToken : decoded . sessionToken ,
@@ -199,7 +177,7 @@ export function HypergraphAppProvider({
199177 }
200178 return await loginWithWallet ( signer , accountId , retryCount + 1 ) ;
201179 }
202- const keys = await restoreKeys ( signer , accountId , sessionToken ) ;
180+ const keys = await restoreKeys ( signer , accountId , sessionToken , syncServerUri , storage ) ;
203181 return {
204182 accountId,
205183 sessionToken,
@@ -228,8 +206,13 @@ export function HypergraphAppProvider({
228206 }
229207
230208 const account = privateKeyToAccount ( keys . signaturePrivateKey as Hex ) ;
231- const sessionNonce = await getSessionNonce ( account . address ) ;
232- const message = prepareSiweMessage ( account . address , sessionNonce ) ;
209+ const sessionNonce = await getSessionNonce ( account . address , syncServerUri ) ;
210+ const message = prepareSiweMessage (
211+ account . address ,
212+ sessionNonce ,
213+ { host : window . location . host , origin : window . location . origin } ,
214+ chainId ,
215+ ) ;
233216 const signature = await account . signMessage ( { message } ) ;
234217 const req = {
235218 accountId,
@@ -257,70 +240,6 @@ export function HypergraphAppProvider({
257240 } ;
258241 }
259242
260- async function restoreKeys ( signer : Identity . Signer , accountId : Address , sessionToken : string ) {
261- const keys = Identity . loadKeys ( storage , accountId ) ;
262- if ( keys ) {
263- return keys ;
264- }
265- // Try to get the users identity from the sync server
266- const res = await fetch ( new URL ( '/identity/encrypted' , syncServerUri ) , {
267- headers : {
268- Authorization : `Bearer ${ sessionToken } ` ,
269- } ,
270- } ) ;
271- if ( res . status === 200 ) {
272- console . log ( 'Identity found' ) ;
273- const decoded = Schema . decodeUnknownSync ( Messages . ResponseIdentityEncrypted ) ( await res . json ( ) ) ;
274- const { keyBox } = decoded ;
275- const { ciphertext, nonce } = keyBox ;
276- const keys = await Identity . decryptIdentity ( signer , accountId , ciphertext , nonce ) ;
277- Identity . storeKeys ( storage , accountId , keys ) ;
278- return keys ;
279- }
280- throw new Error ( `Error fetching identity ${ res . status } ` ) ;
281- }
282-
283- async function signup ( signer : Identity . Signer , accountId : Address ) {
284- const keys = Identity . createIdentityKeys ( ) ;
285- const { ciphertext, nonce } = await Identity . encryptIdentity ( signer , accountId , keys ) ;
286- const { accountProof, keyProof } = await Identity . proveIdentityOwnership ( signer , accountId , keys ) ;
287-
288- const account = privateKeyToAccount ( keys . signaturePrivateKey as Hex ) ;
289- const sessionNonce = await getSessionNonce ( accountId ) ;
290- const message = prepareSiweMessage ( account . address , sessionNonce ) ;
291- const signature = await account . signMessage ( { message } ) ;
292- const req = {
293- keyBox : { accountId, ciphertext, nonce } ,
294- accountProof,
295- keyProof,
296- message,
297- signaturePublicKey : keys . signaturePublicKey ,
298- encryptionPublicKey : keys . encryptionPublicKey ,
299- signature,
300- } as const satisfies Messages . RequestCreateIdentity ;
301- const res = await fetch ( new URL ( '/identity' , syncServerUri ) , {
302- method : 'POST' ,
303- headers : {
304- 'Content-Type' : 'application/json' ,
305- } ,
306- body : JSON . stringify ( req ) ,
307- } ) ;
308- if ( res . status !== 200 ) {
309- // TODO: handle this better?
310- throw new Error ( `Error creating identity: ${ res . status } ` ) ;
311- }
312- const decoded = Schema . decodeUnknownSync ( Messages . ResponseCreateIdentity ) ( await res . json ( ) ) ;
313- Identity . storeAccountId ( storage , accountId ) ;
314- Identity . storeSyncServerSessionToken ( storage , accountId , decoded . sessionToken ) ;
315- Identity . storeKeys ( storage , accountId , keys ) ;
316-
317- return {
318- accountId,
319- sessionToken : decoded . sessionToken ,
320- keys,
321- } ;
322- }
323-
324243 async function login ( signer : Identity . Signer ) {
325244 if ( ! signer ) {
326245 return ;
@@ -336,8 +255,11 @@ export function HypergraphAppProvider({
336255 sessionToken : string ;
337256 keys : Identity . IdentityKeys ;
338257 } ;
339- if ( ! keys && ! ( await identityExists ( accountId ) ) ) {
340- authData = await signup ( signer , accountId ) ;
258+ if ( ! keys && ! ( await identityExists ( accountId , syncServerUri ) ) ) {
259+ authData = await signup ( signer , accountId , syncServerUri , chainId , storage , {
260+ host : window . location . host ,
261+ origin : window . location . origin ,
262+ } ) ;
341263 } else if ( keys ) {
342264 authData = await loginWithKeys ( keys , accountId ) ;
343265 } else {
0 commit comments