uttrflow / uttrflow/uttrflow-swift
Cancelling browser sign-in leaves the callback task suspended and its listener unclosed
- Dominant language
- Swift
- Stars
- 4
- Forks
- 17
- Avg merge
- 3h 32m
- Merged PRs (30d)
- 277
Description
## Problem
On main `8d93f5b8574f3c7b8c30086d9c817bf7f5d4827d`, cancelling the browser sign-in task does not end the pending callback wait or run its listener cleanup.
- [SystemLoopbackListener.awaitCallback](https://github.com/uttrflow/uttrflow-swift/blob/8d93f5b8574f3c7b8c30086d9c817bf7f5d4827d/Sources/UttrflowAccount/LoopbackListener%2BSystem.swift#L68-L91) parks a checked continuation without a cancellation handler or an already-cancelled check. Only an incoming callback or explicit `close()` resumes it, despite the method's documented cancellation behavior.
- [HTTPAuthenticationService.awaitBrowser](https://github.com/uttrflow/uttrflow-swift/blob/8d93f5b8574f3c7b8c30086d9c817bf7f5d4827d/Sources/UttrflowAccount/HTTPAuthenticationService.swift#L178-L202) schedules `listener.close()` from a defer. That defer cannot run while the callback wait remains suspended. `completeSignIn` has already removed the attempt from `pending`, so later `abandonPending()` cannot find this listener.
- [OnboardingFlow.abandonSignIn](https://github.com/uttrflow/uttrflow-swift/blob/8d93f5b8574f3c7b8c30086d9c817bf7f5d4827d/Sources/UttrflowUX/OnboardingFlow.swift#L348-L362) cancels and drops the task when continuing locally. The task acquired a strong `self` before awaiting authentication. If the browser never returns, the suspended operation and listener remain alive instead of releasing on abandonment.
## Verification
An isolated Swift test uses the real `SystemLoopbackListener` and its OS-assigned loopback port, in two variants: calling `awaitCallback()` directly and calling `HTTPAuthenticationService.completeSignIn()` with that listener. The transport is a no-network stub and the token store is in memory.
A gate holds the task until after `task.cancel()`; immediately before the tested call, the task records `Task.isCancelled == true`. After a 250 ms observation window, both variants record `finished == false`. Calling `await listener.close()` then releases both tasks, and both completion assertions pass. Both cancellation assertions fail on main. No account or backend is used.
The 250 ms observation is bounded timing evidence; the underlying indefinite wait follows from the continuation having no cancellation path. The current 52 HTTP authentication, device-grant, device-identity, token, timestamp and local-store tests pass. The listener-close test supplies an immediate refusal rather than a pending cancellable callback, so it does not exercise this path.
## Acceptance criteria
- Cancelling a browser sign-in resumes its callback wait with the documented refusal and closes its listener/connections without requiring a browser response.
- Handle cancellation both before continuation registration and while already waiting, with exactly-once resumption when cancellation races a callback or explicit close.
- Test through the real authentication service with a pending listener; prove the task completes and cleanup runs. Include an already-cancelled entry case and cancellation after registration.
- Ensure starting another attempt or continuing locally cannot retain the abandoned callback operation.
This is separate from #413 (the fixed test synchronization issue), #695 (callback validation and endpoint hardening), and #790 (fragmented request framing).
Priority: P2 (resource cleanup and cancellation correctness).
Contributor guide
Research direction
Start with Sources/UttrflowAccount/LoopbackListener+System.swift, Sources/UttrflowAccount/HTTPAuthenticationService.swift, and Sources/UttrflowUX/OnboardingFlow.swift; read awaitCallback, awaitBrowser, completeSignIn, and abandonSignIn, then run the isolated Swift authentication test described in the issue. Done means cancellation before registration and during the wait complete, close the listener and connections, and pass the race and cleanup assertions without a browser response.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- macos, swift
- Domain
- authentication, desktop
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 55/100