@@ -114,25 +114,37 @@ func (m *Manager) startDeviceFlowWithElicitation(ctx context.Context, session *m
114114 return fmt .Errorf ("failed to get device authorization: %w" , err )
115115 }
116116
117+ // Create cancellable context for polling
118+ pollCtx , cancelPoll := context .WithCancel (ctx )
119+ defer cancelPoll ()
120+
117121 // Use session elicitation if available to show the user the verification URL and code
118122 if session != nil {
119- elicitID , err := generateRandomToken ()
120- if err != nil {
121- // Log warning but continue - elicitation ID is for tracking only
122- elicitID = "fallback-id"
123- }
124- // Elicitation result is not critical - device flow polls independently
125- _ , _ = session .Elicit (ctx , & mcp.ElicitParams {
126- Mode : "url" ,
127- URL : deviceAuth .VerificationURI ,
128- ElicitationID : elicitID ,
129- Message : fmt .Sprintf ("GitHub OAuth Device Authorization\n \n Your code: %s\n \n Visit the URL and enter this code to authenticate." , deviceAuth .UserCode ),
130- })
123+ // Run elicitation in goroutine - if cancelled, abort the device flow
124+ go func () {
125+ elicitID , err := generateRandomToken ()
126+ if err != nil {
127+ elicitID = "fallback-id"
128+ }
129+ result , err := session .Elicit (ctx , & mcp.ElicitParams {
130+ Mode : "url" ,
131+ URL : deviceAuth .VerificationURI ,
132+ ElicitationID : elicitID ,
133+ Message : fmt .Sprintf ("GitHub OAuth Device Authorization\n \n Your code: %s\n \n Visit the URL and enter this code to authenticate." , deviceAuth .UserCode ),
134+ })
135+ // If elicitation was cancelled or declined, abort the polling
136+ if err != nil || result == nil || result .Action == "cancel" || result .Action == "decline" {
137+ cancelPoll ()
138+ }
139+ }()
131140 }
132141
133- // Poll for the token (blocking)
134- token , err := oauth2Cfg .DeviceAccessToken (ctx , deviceAuth )
142+ // Poll for the token (blocking, but respects context cancellation )
143+ token , err := oauth2Cfg .DeviceAccessToken (pollCtx , deviceAuth )
135144 if err != nil {
145+ if pollCtx .Err () != nil {
146+ return fmt .Errorf ("OAuth authorization was cancelled by user" )
147+ }
136148 return fmt .Errorf ("failed to get device access token: %w" , err )
137149 }
138150
@@ -203,16 +215,29 @@ func (m *Manager) startPKCEFlowWithElicitation(ctx context.Context, session *mcp
203215 // Try to open browser - if it works, no elicitation needed
204216 browserErr := openBrowser (authURL )
205217
218+ // Channel to signal elicitation cancellation
219+ elicitCancelChan := make (chan struct {}, 1 )
220+
206221 // Only elicit if browser failed to open (e.g., headless environment)
207222 // and we need to show the user the URL manually
208223 if browserErr != nil && session != nil {
209- elicitID , _ := generateRandomToken ()
210- _ , _ = session .Elicit (ctx , & mcp.ElicitParams {
211- Mode : "url" ,
212- URL : authURL ,
213- ElicitationID : elicitID ,
214- Message : "GitHub OAuth Authorization\n \n Please visit the URL to authorize access." ,
215- })
224+ // Run elicitation in goroutine so we can monitor callback in parallel
225+ go func () {
226+ elicitID , _ := generateRandomToken ()
227+ result , err := session .Elicit (ctx , & mcp.ElicitParams {
228+ Mode : "url" ,
229+ URL : authURL ,
230+ ElicitationID : elicitID ,
231+ Message : "GitHub OAuth Authorization\n \n Please visit the URL to authorize access." ,
232+ })
233+ // If elicitation was cancelled or declined, signal to abort
234+ if err != nil || result == nil || result .Action == "cancel" || result .Action == "decline" {
235+ select {
236+ case elicitCancelChan <- struct {}{}:
237+ default :
238+ }
239+ }
240+ }()
216241 }
217242
218243 // Wait for callback with timeout
@@ -239,6 +264,10 @@ func (m *Manager) startPKCEFlowWithElicitation(ctx context.Context, session *mcp
239264 cleanup ()
240265 return fmt .Errorf ("OAuth callback error: %w" , err )
241266
267+ case <- elicitCancelChan :
268+ cleanup ()
269+ return fmt .Errorf ("OAuth authorization was cancelled by user" )
270+
242271 case <- ctx .Done ():
243272 cleanup ()
244273 return ctx .Err ()
0 commit comments