@@ -64,13 +64,7 @@ export class OAuthSessionManager implements vscode.Disposable {
6464 private refreshInProgress = false ;
6565 private lastRefreshAttempt = 0 ;
6666
67- // Pending authorization flow state
68- private pendingAuthResolve :
69- | ( ( value : { code : string ; verifier : string } ) => void )
70- | undefined ;
7167 private pendingAuthReject : ( ( reason : Error ) => void ) | undefined ;
72- private expectedState : string | undefined ;
73- private pendingVerifier : string | undefined ;
7468
7569 /**
7670 * Create and initialize a new OAuth session manager.
@@ -370,106 +364,80 @@ export class OAuthSessionManager implements vscode.Disposable {
370364 challenge ,
371365 ) ;
372366
373- return new Promise < { code : string ; verifier : string } > (
367+ const callbackPromise = new Promise < { code : string ; verifier : string } > (
374368 ( resolve , reject ) => {
375369 const timeoutMins = 5 ;
376- const timeout = setTimeout (
370+ const timeoutHandle = setTimeout (
377371 ( ) => {
378- this . clearPendingAuth ( ) ;
372+ cleanup ( ) ;
379373 reject (
380374 new Error ( `OAuth flow timed out after ${ timeoutMins } minutes` ) ,
381375 ) ;
382376 } ,
383377 timeoutMins * 60 * 1000 ,
384378 ) ;
385379
386- const clearPromise = ( ) => {
387- clearTimeout ( timeout ) ;
388- this . clearPendingAuth ( ) ;
389- } ;
390-
391- this . pendingAuthResolve = ( result ) => {
392- clearPromise ( ) ;
393- resolve ( result ) ;
394- } ;
395-
396- this . pendingAuthReject = ( error ) => {
397- clearPromise ( ) ;
398- reject ( error ) ;
399- } ;
380+ const listener = this . secretsManager . onDidChangeOAuthCallback (
381+ ( { state : callbackState , code, error } ) => {
382+ if ( callbackState !== state ) {
383+ return ;
384+ }
400385
401- this . expectedState = state ;
402- this . pendingVerifier = verifier ;
386+ cleanup ( ) ;
403387
404- vscode . env . openExternal ( vscode . Uri . parse ( authUrl ) ) . then (
405- ( ) => { } ,
406- ( error ) => {
407- if ( error instanceof Error ) {
408- this . pendingAuthReject ?.( error ) ;
388+ if ( error ) {
389+ reject ( new Error ( `OAuth error: ${ error } ` ) ) ;
390+ } else if ( code ) {
391+ resolve ( { code, verifier } ) ;
409392 } else {
410- this . pendingAuthReject ?. ( new Error ( "Failed to open browser " ) ) ;
393+ reject ( new Error ( "No authorization code received " ) ) ;
411394 }
412395 } ,
413396 ) ;
397+
398+ const cleanup = ( ) => {
399+ clearTimeout ( timeoutHandle ) ;
400+ listener . dispose ( ) ;
401+ } ;
402+
403+ this . pendingAuthReject = ( error ) => {
404+ cleanup ( ) ;
405+ reject ( error ) ;
406+ } ;
414407 } ,
415408 ) ;
416- }
417409
418- /**
419- * Clear pending authorization flow state.
420- */
421- private clearPendingAuth ( ) : void {
422- this . pendingAuthResolve = undefined ;
423- this . pendingAuthReject = undefined ;
424- this . expectedState = undefined ;
425- this . pendingVerifier = undefined ;
410+ try {
411+ await vscode . env . openExternal ( vscode . Uri . parse ( authUrl ) ) ;
412+ } catch ( error ) {
413+ throw error instanceof Error
414+ ? error
415+ : new Error ( "Failed to open browser" ) ;
416+ }
417+
418+ return callbackPromise ;
426419 }
427420
428421 /**
429422 * Handle OAuth callback from browser redirect.
430- * Validates state and resolves pending authorization promise.
431- *
432- * // TODO this has to work across windows!
423+ * Writes the callback result to secrets storage, triggering the waiting window to proceed.
433424 */
434- handleCallback (
425+ async handleCallback (
435426 code : string | null ,
436427 state : string | null ,
437428 error : string | null ,
438- ) : void {
439- if ( ! this . pendingAuthResolve || ! this . pendingAuthReject ) {
440- this . logger . warn ( "Received OAuth callback but no pending auth flow" ) ;
441- return ;
442- }
443-
444- if ( error ) {
445- this . pendingAuthReject ( new Error ( `OAuth error: ${ error } ` ) ) ;
446- return ;
447- }
448-
449- if ( ! code ) {
450- this . pendingAuthReject ( new Error ( "No authorization code received" ) ) ;
451- return ;
452- }
453-
429+ ) : Promise < void > {
454430 if ( ! state ) {
455- this . pendingAuthReject ( new Error ( "No state received" ) ) ;
456- return ;
457- }
458-
459- if ( state !== this . expectedState ) {
460- this . pendingAuthReject (
461- new Error ( "State mismatch - possible CSRF attack" ) ,
462- ) ;
431+ this . logger . warn ( "Received OAuth callback with no state parameter" ) ;
463432 return ;
464433 }
465434
466- const verifier = this . pendingVerifier ;
467- if ( ! verifier ) {
468- this . pendingAuthReject ( new Error ( "No PKCE verifier found" ) ) ;
469- return ;
435+ try {
436+ await this . secretsManager . setOAuthCallback ( { state, code, error } ) ;
437+ this . logger . debug ( "OAuth callback processed successfully" ) ;
438+ } catch ( err ) {
439+ this . logger . error ( "Failed to process OAuth callback:" , err ) ;
470440 }
471-
472- this . pendingAuthResolve ( { code, verifier } ) ;
473441 }
474442
475443 /**
@@ -712,13 +680,13 @@ export class OAuthSessionManager implements vscode.Disposable {
712680 }
713681
714682 /**
715- * Clears all in-memory state and rejects any pending operations .
683+ * Clears all in-memory state.
716684 */
717685 dispose ( ) : void {
718686 if ( this . pendingAuthReject ) {
719687 this . pendingAuthReject ( new Error ( "OAuth session manager disposed" ) ) ;
720688 }
721- this . clearPendingAuth ( ) ;
689+ this . pendingAuthReject = undefined ;
722690 this . storedTokens = undefined ;
723691 this . refreshInProgress = false ;
724692 this . lastRefreshAttempt = 0 ;
0 commit comments