@@ -107,12 +107,22 @@ export class ToolHandlers {
107107 throw new Error ( `Authentication failed: ${ result . message } ` ) ;
108108 }
109109 } else {
110- // In non-headless mode, throw an error asking user to authenticate manually
111- const port = this . context . gatewayManager
112- ? this . context . gatewayManager . getCurrentPort ( )
113- : this . context . config . IB_GATEWAY_PORT ;
114- const authUrl = `https://${ this . context . config . IB_GATEWAY_HOST } :${ port } ` ;
115- throw new Error ( `Authentication required. Please use the 'authenticate' tool to complete the authentication process at ${ authUrl } .` ) ;
110+ // In non-headless mode, check if the gateway is already authenticated
111+ // (the user may have completed browser auth via the authenticate tool)
112+ const authStatus = await this . context . ibClient . checkAuthenticationStatus ( ) ;
113+ if ( ! authStatus ) {
114+ const port = this . context . gatewayManager
115+ ? this . context . gatewayManager . getCurrentPort ( )
116+ : this . context . config . IB_GATEWAY_PORT ;
117+ const authUrl = `https://${ this . context . config . IB_GATEWAY_HOST } :${ port } ` ;
118+ throw new Error ( `Authentication required. Please use the 'authenticate' tool to complete the authentication process at ${ authUrl } .` ) ;
119+ }
120+ // Auth status is true, reauthenticate the REST session if needed
121+ try {
122+ await this . context . ibClient . reauthenticate ( ) ;
123+ } catch ( e ) {
124+ Logger . warn ( "[ENSURE-AUTH] Reauthenticate after status check failed, but status is true:" , e ) ;
125+ }
116126 }
117127 }
118128
@@ -145,6 +155,46 @@ export class ToolHandlers {
145155 return `Authentication required. Please use the 'authenticate' tool to complete the authentication process (configured for ${ mode } ) at ${ authUrl } .` ;
146156 }
147157
158+ /**
159+ * After browser opens for OAuth, poll the gateway until authenticated,
160+ * then trigger reauthenticate to establish the REST API session.
161+ * This bridges the gap between browser-based OAuth and the REST API auth state.
162+ */
163+ private startBrowserAuthPolling ( authUrl : string , port : number ) : void {
164+ const maxAttempts = 60 ; // 2 minutes total
165+ const initialDelay = 2000 ; // 2 second initial delay
166+ let attempts = 0 ;
167+
168+ const poll = async ( ) => {
169+ attempts ++ ;
170+ Logger . log ( `[BROWSER-AUTH-POLL] Attempt ${ attempts } /${ maxAttempts } ` ) ;
171+
172+ try {
173+ const isAuth = await this . context . ibClient . checkAuthenticationStatus ( ) ;
174+
175+ if ( isAuth ) {
176+ Logger . log ( "[BROWSER-AUTH-POLL] Authentication detected, reauthenticating REST session" ) ;
177+ // Trigger reauthenticate to establish REST API session
178+ await this . context . ibClient . reauthenticate ( ) ;
179+ Logger . log ( "[BROWSER-AUTH-POLL] Reauthentication successful, REST session established" ) ;
180+ return ; // Success, stop polling
181+ }
182+ } catch ( error ) {
183+ Logger . warn ( "[BROWSER-AUTH-POLL] Poll attempt failed:" , error ) ;
184+ }
185+
186+ if ( attempts < maxAttempts ) {
187+ const delay = Math . min ( initialDelay + ( attempts * 500 ) , 10000 ) ;
188+ setTimeout ( poll , delay ) ;
189+ } else {
190+ Logger . warn ( "[BROWSER-AUTH-POLL] Timed out waiting for browser authentication" ) ;
191+ }
192+ } ;
193+
194+ // Start polling after initial delay
195+ setTimeout ( poll , initialDelay ) ;
196+ }
197+
148198 private formatError ( error : unknown ) : string {
149199 if ( this . isAuthenticationError ( error ) ) {
150200 return this . getAuthenticationErrorMessage ( ) ;
@@ -241,6 +291,11 @@ export class ToolHandlers {
241291 try {
242292 await open ( authUrl ) ;
243293
294+ // Start polling for authentication to complete
295+ // The browser auth creates a server-side session that the REST API can use
296+ // We poll until authenticated, then trigger reauthenticate for the REST session
297+ this . startBrowserAuthPolling ( authUrl , port ) ;
298+
244299 return {
245300 content : [
246301 {
@@ -257,7 +312,8 @@ export class ToolHandlers {
257312 "5. Once authenticated, you can use other trading tools"
258313 ] ,
259314 browserOpened : true ,
260- note : "IB Gateway is running locally - your credentials stay secure on your machine"
315+ polling : true ,
316+ note : "IB Gateway is running locally - your credentials stay secure on your machine. Polling for authentication completion..."
261317 } , null , 2 ) ,
262318 } ,
263319 ] ,
0 commit comments