From cccb77b552af59be55df2475115ac4ea2e6a963b Mon Sep 17 00:00:00 2001 From: hkfires <10558748+hkfires@users.noreply.github.com> Date: Fri, 20 Mar 2026 11:48:30 +0800 Subject: [PATCH] fix(auth): avoid blocking oauth callback wait on prompt --- internal/auth/gemini/gemini_auth.go | 31 ++++++++++++++++++++++------- sdk/auth/antigravity.go | 25 +++++++++++++++++++---- sdk/auth/claude.go | 25 +++++++++++++++++++---- sdk/auth/codex.go | 25 +++++++++++++++++++---- sdk/auth/iflow.go | 25 +++++++++++++++++++---- 5 files changed, 108 insertions(+), 23 deletions(-) diff --git a/internal/auth/gemini/gemini_auth.go b/internal/auth/gemini/gemini_auth.go index c459c5ca..0830e0a2 100644 --- a/internal/auth/gemini/gemini_auth.go +++ b/internal/auth/gemini/gemini_auth.go @@ -305,6 +305,9 @@ func (g *GeminiAuth) getTokenFromWeb(ctx context.Context, config *oauth2.Config, defer manualPromptTimer.Stop() } + var manualInputCh <-chan string + var manualInputErrCh <-chan error + waitForCallback: for { select { @@ -326,13 +329,25 @@ waitForCallback: return nil, err default: } - input, err := opts.Prompt("Paste the Gemini callback URL (or press Enter to keep waiting): ") - if err != nil { - return nil, err - } - parsed, err := misc.ParseOAuthCallback(input) - if err != nil { - return nil, err + inputCh := make(chan string, 1) + inputErrCh := make(chan error, 1) + go func() { + input, err := opts.Prompt("Paste the Gemini callback URL (or press Enter to keep waiting): ") + if err != nil { + inputErrCh <- err + return + } + inputCh <- input + }() + manualInputCh = inputCh + manualInputErrCh = inputErrCh + continue + case input := <-manualInputCh: + manualInputCh = nil + manualInputErrCh = nil + parsed, errParse := misc.ParseOAuthCallback(input) + if errParse != nil { + return nil, errParse } if parsed == nil { continue @@ -345,6 +360,8 @@ waitForCallback: } authCode = parsed.Code break waitForCallback + case errManual := <-manualInputErrCh: + return nil, errManual case <-timeoutTimer.C: return nil, fmt.Errorf("oauth flow timed out") } diff --git a/sdk/auth/antigravity.go b/sdk/auth/antigravity.go index 6ed31d6d..38617868 100644 --- a/sdk/auth/antigravity.go +++ b/sdk/auth/antigravity.go @@ -98,6 +98,9 @@ func (AntigravityAuthenticator) Login(ctx context.Context, cfg *config.Config, o defer manualPromptTimer.Stop() } + var manualInputCh <-chan string + var manualInputErrCh <-chan error + waitForCallback: for { select { @@ -115,10 +118,22 @@ waitForCallback: break waitForCallback default: } - input, errPrompt := opts.Prompt("Paste the antigravity callback URL (or press Enter to keep waiting): ") - if errPrompt != nil { - return nil, errPrompt - } + inputCh := make(chan string, 1) + inputErrCh := make(chan error, 1) + go func() { + input, errPrompt := opts.Prompt("Paste the antigravity callback URL (or press Enter to keep waiting): ") + if errPrompt != nil { + inputErrCh <- errPrompt + return + } + inputCh <- input + }() + manualInputCh = inputCh + manualInputErrCh = inputErrCh + continue + case input := <-manualInputCh: + manualInputCh = nil + manualInputErrCh = nil parsed, errParse := misc.ParseOAuthCallback(input) if errParse != nil { return nil, errParse @@ -132,6 +147,8 @@ waitForCallback: Error: parsed.Error, } break waitForCallback + case errManual := <-manualInputErrCh: + return nil, errManual case <-timeoutTimer.C: return nil, fmt.Errorf("antigravity: authentication timed out") } diff --git a/sdk/auth/claude.go b/sdk/auth/claude.go index 706763b3..4e54a99f 100644 --- a/sdk/auth/claude.go +++ b/sdk/auth/claude.go @@ -124,6 +124,9 @@ func (a *ClaudeAuthenticator) Login(ctx context.Context, cfg *config.Config, opt defer manualPromptTimer.Stop() } + var manualInputCh <-chan string + var manualInputErrCh <-chan error + waitForCallback: for { select { @@ -149,10 +152,22 @@ waitForCallback: return nil, err default: } - input, errPrompt := opts.Prompt("Paste the Claude callback URL (or press Enter to keep waiting): ") - if errPrompt != nil { - return nil, errPrompt - } + inputCh := make(chan string, 1) + inputErrCh := make(chan error, 1) + go func() { + input, errPrompt := opts.Prompt("Paste the Claude callback URL (or press Enter to keep waiting): ") + if errPrompt != nil { + inputErrCh <- errPrompt + return + } + inputCh <- input + }() + manualInputCh = inputCh + manualInputErrCh = inputErrCh + continue + case input := <-manualInputCh: + manualInputCh = nil + manualInputErrCh = nil parsed, errParse := misc.ParseOAuthCallback(input) if errParse != nil { return nil, errParse @@ -167,6 +182,8 @@ waitForCallback: Error: parsed.Error, } break waitForCallback + case errManual := <-manualInputErrCh: + return nil, errManual } } diff --git a/sdk/auth/codex.go b/sdk/auth/codex.go index 1af36936..7a6e0d38 100644 --- a/sdk/auth/codex.go +++ b/sdk/auth/codex.go @@ -127,6 +127,9 @@ func (a *CodexAuthenticator) Login(ctx context.Context, cfg *config.Config, opts defer manualPromptTimer.Stop() } + var manualInputCh <-chan string + var manualInputErrCh <-chan error + waitForCallback: for { select { @@ -152,10 +155,22 @@ waitForCallback: return nil, err default: } - input, errPrompt := opts.Prompt("Paste the Codex callback URL (or press Enter to keep waiting): ") - if errPrompt != nil { - return nil, errPrompt - } + inputCh := make(chan string, 1) + inputErrCh := make(chan error, 1) + go func() { + input, errPrompt := opts.Prompt("Paste the Codex callback URL (or press Enter to keep waiting): ") + if errPrompt != nil { + inputErrCh <- errPrompt + return + } + inputCh <- input + }() + manualInputCh = inputCh + manualInputErrCh = inputErrCh + continue + case input := <-manualInputCh: + manualInputCh = nil + manualInputErrCh = nil parsed, errParse := misc.ParseOAuthCallback(input) if errParse != nil { return nil, errParse @@ -170,6 +185,8 @@ waitForCallback: Error: parsed.Error, } break waitForCallback + case errManual := <-manualInputErrCh: + return nil, errManual } } diff --git a/sdk/auth/iflow.go b/sdk/auth/iflow.go index a695311d..0e7b5ce8 100644 --- a/sdk/auth/iflow.go +++ b/sdk/auth/iflow.go @@ -109,6 +109,9 @@ func (a *IFlowAuthenticator) Login(ctx context.Context, cfg *config.Config, opts defer manualPromptTimer.Stop() } + var manualInputCh <-chan string + var manualInputErrCh <-chan error + waitForCallback: for { select { @@ -128,10 +131,22 @@ waitForCallback: return nil, fmt.Errorf("iflow auth: callback wait failed: %w", err) default: } - input, errPrompt := opts.Prompt("Paste the iFlow callback URL (or press Enter to keep waiting): ") - if errPrompt != nil { - return nil, errPrompt - } + inputCh := make(chan string, 1) + inputErrCh := make(chan error, 1) + go func() { + input, errPrompt := opts.Prompt("Paste the iFlow callback URL (or press Enter to keep waiting): ") + if errPrompt != nil { + inputErrCh <- errPrompt + return + } + inputCh <- input + }() + manualInputCh = inputCh + manualInputErrCh = inputErrCh + continue + case input := <-manualInputCh: + manualInputCh = nil + manualInputErrCh = nil parsed, errParse := misc.ParseOAuthCallback(input) if errParse != nil { return nil, errParse @@ -145,6 +160,8 @@ waitForCallback: Error: parsed.Error, } break waitForCallback + case errManual := <-manualInputErrCh: + return nil, errManual } } if result.Error != "" {