@@ -11,6 +11,11 @@ import {
1111 encodeCredential ,
1212} from "@algorandfoundation/liquid-client" ;
1313import { encodeAddress } from "@algorandfoundation/keystore" ;
14+ import type { KeyData , KeyStoreState } from "@algorandfoundation/keystore" ;
15+ import { fetchSecret , getMasterKey , commit } from '@algorandfoundation/react-native-keystore' ;
16+ import { keyStore } from '@/stores/keystore' ;
17+ import { accountsStore } from '@/stores/accounts' ;
18+ import { passkeysStore } from '@/stores/passkeys' ;
1419import { useProvider } from '@/hooks/useProvider' ;
1520import { addMessage } from '@/stores/messages' ;
1621import { sessionsStore , addSession , updateSessionStatus , updateSessionActivity , Session } from '@/stores/sessions' ;
@@ -29,7 +34,7 @@ interface UseConnectionResult {
2934
3035export function useConnection ( origin : string , requestId : string ) : UseConnectionResult {
3136 const router = useRouter ( ) ;
32- const { accounts, keys, key, passkeys , sessions } = useProvider ( ) ;
37+ const { accounts, keys, key, passkey , sessions } = useProvider ( ) ;
3338
3439 const [ isConnected , setIsConnected ] = useState ( false ) ;
3540 const [ address , setAddress ] = useState < string | null > ( null ) ;
@@ -46,18 +51,7 @@ export function useConnection(origin: string, requestId: string): UseConnectionR
4651 const dataChannelRef = useRef < RTCDataChannel | null > ( null ) ;
4752 const clientRef = useRef < SignalClient | null > ( null ) ;
4853 const lastUserActivityRef = useRef < number > ( Date . now ( ) ) ;
49-
50- useEffect ( ( ) => {
51- if ( accounts . length > 0 && keys . length > 0 && ! address ) {
52- let foundKey = keys . find ( ( k ) => k . id === accounts [ 0 ] ?. metadata ?. keyId ) ;
53- if ( ! foundKey && keys . length > 0 ) {
54- foundKey = keys [ 0 ] ;
55- }
56- if ( foundKey ?. publicKey ) {
57- setAddress ( encodeAddress ( foundKey . publicKey ) ) ;
58- }
59- }
60- } , [ accounts , keys , address ] ) ;
54+ const authFlowInProgressRef = useRef < boolean > ( false ) ;
6155
6256 const session = useStore ( sessionsStore , ( state ) =>
6357 state . sessions . find ( s => s . id === requestId && s . origin === origin )
@@ -130,13 +124,19 @@ export function useConnection(origin: string, requestId: string): UseConnectionR
130124 let active = true ;
131125
132126 async function setupConnection ( ) {
127+ const toUrlSafe = ( id : string ) => id . replace ( / \+ / g, "-" ) . replace ( / \/ / g, "_" ) . replace ( / = / g, "" ) ;
133128 if ( ! origin || ! requestId ) {
134129 console . error ( "Missing origin or requestId" ) ;
135130 setIsLoading ( false ) ;
136131 return ;
137132 }
138133
139- if ( accounts . length === 0 || keys . length === 0 ) {
134+ if ( authFlowInProgressRef . current ) {
135+ console . log ( "Auth flow already in progress, skipping duplicate setup" ) ;
136+ return ;
137+ }
138+
139+ if ( accountsStore . state . accounts . length === 0 || keyStore . state . keys . length === 0 ) {
140140 console . log ( "Waiting for accounts and keys to load..." ) ;
141141 // If it's been loading for more than a few seconds, it might really be empty
142142 // but typically it's better to wait for them to be non-empty.
@@ -152,23 +152,27 @@ export function useConnection(origin: string, requestId: string): UseConnectionR
152152 setError ( null ) ;
153153
154154 try {
155- const existingSession = sessions . find ( s => s . id === requestId && s . origin === origin ) ;
155+ const currentSessions = sessionsStore . state . sessions ;
156+ const currentKeys = keyStore . state . keys ;
157+ const currentAccounts = accountsStore . state . accounts ;
158+
159+ const existingSession = currentSessions . find ( s => s . id === requestId && s . origin === origin ) ;
156160 if ( ! existingSession ) {
157161 addSession ( { id : requestId , origin, status : 'active' , ttl : 60000 } ) ;
158162 } else if ( existingSession . status !== 'active' ) {
159163 updateSessionStatus ( requestId , origin , 'active' ) ;
160164 }
161165
162166 // Try to find the key associated with the first account, but fall back to the first available key
163- let foundKey = keys . find ( ( k ) => k . id === accounts [ 0 ] ?. metadata ?. keyId ) ;
164- if ( ! foundKey && keys . length > 0 ) {
165- foundKey = keys [ 0 ] ;
167+ let foundKey = currentKeys . find ( ( k ) => k . id === currentAccounts [ 0 ] ?. metadata ?. keyId ) ;
168+ if ( ! foundKey && currentKeys . length > 0 ) {
169+ foundKey = currentKeys [ 0 ] ;
166170 console . log ( "Falling back to the first available key for attestation" ) ;
167171 }
168172
169173 if ( ! foundKey || ! foundKey . publicKey ) {
170- console . error ( "No key found for attestation. Keys:" , JSON . stringify ( keys . map ( k => ( { id : k . id , type : k . type } ) ) , null , 2 ) ) ;
171- console . error ( "Accounts:" , JSON . stringify ( accounts . map ( a => ( { address : a . address , keyId : a . metadata ?. keyId } ) ) , null , 2 ) ) ;
174+ console . error ( "No key found for attestation. Keys:" , JSON . stringify ( currentKeys . map ( k => ( { id : k . id , type : k . type } ) ) , null , 2 ) ) ;
175+ console . error ( "Accounts:" , JSON . stringify ( currentAccounts . map ( a => ( { address : a . address , keyId : a . metadata ?. keyId } ) ) , null , 2 ) ) ;
172176 throw new Error ( "No key found for attestation" ) ;
173177 }
174178
@@ -177,8 +181,10 @@ export function useConnection(origin: string, requestId: string): UseConnectionR
177181 const sessionCheck = await fetch ( `${ origin } /auth/session` ) ;
178182 if ( ! active ) return ;
179183 console . log ( "Initial session status:" , sessionCheck . ok ) ;
184+ authFlowInProgressRef . current = true ;
180185
181- const relevantPasskeys = passkeys . filter ( p => {
186+ const currentPasskeys = await passkey . store . getPasskeys ( ) ;
187+ const relevantPasskeys = currentPasskeys . filter ( p => {
182188 const storedOrigin = p . metadata ?. origin ;
183189 if ( ! storedOrigin ) return false ;
184190 try {
@@ -256,7 +262,7 @@ export function useConnection(origin: string, requestId: string): UseConnectionR
256262 }
257263
258264 if ( ! selectedAddress ) {
259- const matchedPasskey = relevantPasskeys . find ( p => p . id === credential . id ) || passkeys . find ( p => p . id === credential . id ) ;
265+ const matchedPasskey = relevantPasskeys . find ( p => p . id === credential . id ) || currentPasskeys . find ( p => p . id === credential . id ) ;
260266 const userHandle = matchedPasskey ?. metadata ?. userHandle ;
261267 if ( userHandle ) {
262268 try {
@@ -280,7 +286,7 @@ export function useConnection(origin: string, requestId: string): UseConnectionR
280286
281287 // Re-sign the challenge if the address changed to match the selected passkey
282288 const selectedPublicKey = decodeAddress ( selectedAddress ) ;
283- const selectedKey = keys . find ( k =>
289+ const selectedKey = keyStore . state . keys . find ( k =>
284290 k . publicKey && k . publicKey . length === selectedPublicKey . length &&
285291 k . publicKey . every ( ( v , i ) => v === selectedPublicKey [ i ] )
286292 ) ;
@@ -310,6 +316,24 @@ export function useConnection(origin: string, requestId: string): UseConnectionR
310316 if ( ! submitResponse . ok ) {
311317 throw new Error ( `Failed to submit assertion response: ${ submitResponse . status } ${ submitResponse . statusText } ` ) ;
312318 }
319+
320+ const currentPasskeys = await passkey . store . getPasskeys ( ) ;
321+ const matchedPasskey = currentPasskeys . find ( p => p . id === credential . id ) ;
322+ const matchedKey = keyStore . state . keys . find ( k => k . id === matchedPasskey ?. metadata ?. keyId ) ||
323+ keyStore . state . keys . find ( ( k ) => toUrlSafe ( k . id ) === credential . id ) ;
324+
325+ if ( matchedKey ) {
326+ try {
327+ const masterKey = await getMasterKey ( ) ;
328+ const keyData = await fetchSecret < KeyData > ( { keyId : matchedKey . id , masterKey } ) ;
329+ if ( keyData ) {
330+ keyData . metadata = { ...keyData . metadata , registered : true } ;
331+ await commit ( { store : keyStore as any , keyData } ) ;
332+ }
333+ } catch ( error ) {
334+ console . error ( 'Failed to update key metadata after assertion:' , error ) ;
335+ }
336+ }
313337 } else {
314338 console . log ( "No existing passkey for origin, using attestation" ) ;
315339
@@ -400,6 +424,24 @@ export function useConnection(origin: string, requestId: string): UseConnectionR
400424 if ( ! submitResponse . ok ) {
401425 throw new Error ( `Failed to submit attestation response: ${ submitResponse . status } ${ submitResponse . statusText } ` ) ;
402426 }
427+
428+ const currentPasskeys = await passkey . store . getPasskeys ( ) ;
429+ const matchedPasskey = currentPasskeys . find ( p => p . id === credential . id ) ;
430+ const matchedKey = keyStore . state . keys . find ( k => k . id === matchedPasskey ?. metadata ?. keyId ) ||
431+ keyStore . state . keys . find ( ( k ) => toUrlSafe ( k . id ) === credential . id ) ;
432+
433+ if ( matchedKey ) {
434+ try {
435+ const masterKey = await getMasterKey ( ) ;
436+ const keyData = await fetchSecret < KeyData > ( { keyId : matchedKey . id , masterKey } ) ;
437+ if ( keyData ) {
438+ keyData . metadata = { ...keyData . metadata , registered : true } ;
439+ await commit ( { store : keyStore as any , keyData } ) ;
440+ }
441+ } catch ( error ) {
442+ console . error ( 'Failed to update key metadata after attestation:' , error ) ;
443+ }
444+ }
403445 }
404446
405447 // Final validation of the session before connecting
@@ -517,6 +559,8 @@ export function useConnection(origin: string, requestId: string): UseConnectionR
517559 [ { text : "OK" , onPress : ( ) => router . back ( ) } ]
518560 ) ;
519561 }
562+ } finally {
563+ authFlowInProgressRef . current = false ;
520564 }
521565 }
522566
@@ -533,7 +577,7 @@ export function useConnection(origin: string, requestId: string): UseConnectionR
533577 clientRef . current = null ;
534578 }
535579 } ;
536- } , [ origin , requestId , accounts , keys , key . store , router ] ) ;
580+ } , [ origin , requestId , router , key , passkey , accounts . length > 0 , keys . length > 0 ] ) ;
537581
538582 return {
539583 session,
0 commit comments