@@ -4,6 +4,7 @@ import type { AsyncStorage } from "../../utils/storage/AsyncStorage.js";
44import { timeoutPromise } from "../../utils/timeoutPromise.js" ;
55import { isEcosystemWallet } from "../ecosystem/is-ecosystem-wallet.js" ;
66import { ClientScopedStorage } from "../in-app/core/authentication/client-scoped-storage.js" ;
7+ import { linkAccount } from "../in-app/core/authentication/linkAccount.js" ;
78import type {
89 AuthArgsType ,
910 AuthStoredTokenWithCookieReturnType ,
@@ -83,6 +84,24 @@ const _autoConnectCore = async ({
8384
8485 const urlToken = getUrlToken ( ) ;
8586
87+ // Handle linking flow: autoconnect with stored credentials, then link the new profile
88+ if ( urlToken ?. authFlow === "link" && urlToken . authResult ) {
89+ const linkingResult = await handleLinkingFlow ( {
90+ client : props . client ,
91+ connectOverride,
92+ createWalletFn,
93+ manager,
94+ onConnect,
95+ props,
96+ setLastAuthProvider,
97+ storage,
98+ timeout,
99+ urlToken,
100+ wallets,
101+ } ) ;
102+ return linkingResult ;
103+ }
104+
86105 // If an auth cookie is found and this site supports the wallet, we'll set the auth cookie in the client storage
87106 const wallet = wallets . find ( ( w ) => w . id === urlToken ?. walletId ) ;
88107 if ( urlToken ?. authCookie && wallet ) {
@@ -223,6 +242,138 @@ const _autoConnectCore = async ({
223242 return autoConnected ; // useQuery needs a return value
224243} ;
225244
245+ /**
246+ * Handles the linking flow when returning from an OAuth redirect with authFlow=link.
247+ * This autoconnects using stored credentials, then links the new profile from the URL token.
248+ * @internal
249+ */
250+ async function handleLinkingFlow ( params : {
251+ client : ThirdwebClient ;
252+ urlToken : NonNullable < ReturnType < typeof getUrlToken > > ;
253+ wallets : Wallet [ ] ;
254+ storage : AsyncStorage ;
255+ manager : ConnectionManager ;
256+ onConnect ?: ( wallet : Wallet , connectedWallets : Wallet [ ] ) => void ;
257+ timeout : number ;
258+ connectOverride ?: (
259+ walletOrFn : Wallet | ( ( ) => Promise < Wallet > ) ,
260+ ) => Promise < Wallet | null > ;
261+ createWalletFn : ( id : WalletId ) => Wallet ;
262+ setLastAuthProvider ?: (
263+ authProvider : AuthArgsType [ "strategy" ] ,
264+ storage : AsyncStorage ,
265+ ) => Promise < void > ;
266+ props : AutoConnectProps & { wallets : Wallet [ ] } ;
267+ } ) : Promise < boolean > {
268+ const {
269+ client,
270+ connectOverride,
271+ createWalletFn,
272+ manager,
273+ onConnect,
274+ props,
275+ setLastAuthProvider,
276+ storage,
277+ timeout,
278+ urlToken,
279+ wallets,
280+ } = params ;
281+
282+ // Get stored wallet credentials (not from URL)
283+ const [ storedConnectedWalletIds , storedActiveWalletId ] = await Promise . all ( [
284+ getStoredConnectedWalletIds ( storage ) ,
285+ getStoredActiveWalletId ( storage ) ,
286+ ] ) ;
287+ const lastConnectedChain =
288+ ( await getLastConnectedChain ( storage ) ) || props . chain ;
289+
290+ if ( ! storedActiveWalletId || ! storedConnectedWalletIds ) {
291+ console . warn ( "No stored wallet found for linking flow" ) ;
292+ manager . isAutoConnecting . setValue ( false ) ;
293+ return false ;
294+ }
295+
296+ // Update auth provider if provided
297+ if ( urlToken . authProvider ) {
298+ await setLastAuthProvider ?.( urlToken . authProvider , storage ) ;
299+ }
300+
301+ // Find or create the active wallet from stored credentials
302+ const activeWallet =
303+ wallets . find ( ( w ) => w . id === storedActiveWalletId ) ||
304+ createWalletFn ( storedActiveWalletId ) ;
305+
306+ // Autoconnect WITHOUT the URL token (use stored credentials)
307+ manager . activeWalletConnectionStatusStore . setValue ( "connecting" ) ;
308+ try {
309+ await timeoutPromise (
310+ handleWalletConnection ( {
311+ authResult : undefined , // Don't use URL token for connection
312+ client,
313+ lastConnectedChain,
314+ wallet : activeWallet ,
315+ } ) ,
316+ {
317+ message : `AutoConnect timeout: ${ timeout } ms limit exceeded.` ,
318+ ms : timeout ,
319+ } ,
320+ ) ;
321+
322+ await ( connectOverride
323+ ? connectOverride ( activeWallet )
324+ : manager . connect ( activeWallet , {
325+ accountAbstraction : props . accountAbstraction ,
326+ client,
327+ } ) ) ;
328+ } catch ( e ) {
329+ console . warn ( "Failed to auto-connect for linking:" , e ) ;
330+ manager . activeWalletConnectionStatusStore . setValue ( "disconnected" ) ;
331+ manager . isAutoConnecting . setValue ( false ) ;
332+ return false ;
333+ }
334+
335+ // Now link the new profile using URL auth token
336+ const ecosystem = isEcosystemWallet ( activeWallet )
337+ ? {
338+ id : activeWallet . id ,
339+ partnerId : activeWallet . getConfig ( ) ?. partnerId ,
340+ }
341+ : undefined ;
342+
343+ const clientStorage = new ClientScopedStorage ( {
344+ clientId : client . clientId ,
345+ ecosystem,
346+ storage,
347+ } ) ;
348+
349+ try {
350+ await linkAccount ( {
351+ client,
352+ ecosystem,
353+ storage : clientStorage ,
354+ tokenToLink : urlToken . authResult ! . storedToken . cookieString ,
355+ } ) ;
356+ } catch ( e ) {
357+ console . error ( "Failed to link profile after redirect:" , e ) ;
358+ // Continue - user is still connected, just linking failed
359+ }
360+
361+ manager . isAutoConnecting . setValue ( false ) ;
362+
363+ const connectedWallet = manager . activeWalletStore . getValue ( ) ;
364+ const allConnectedWallets = manager . connectedWallets . getValue ( ) ;
365+ if ( connectedWallet ) {
366+ try {
367+ onConnect ?.( connectedWallet , allConnectedWallets ) ;
368+ } catch ( e ) {
369+ console . error ( "Error calling onConnect callback:" , e ) ;
370+ }
371+ return true ;
372+ }
373+
374+ return false ;
375+ }
376+
226377/**
227378 * @internal
228379 */
0 commit comments