fix(auth): serialise the token refresh across processes
The login cache is shared by every mdrs process, but the refresh was guarded by a lock that only reaches inside one. Concurrent runs each sent the same refresh token, and a provider that rotates them accepts the first and refuses the rest. - hold a lock that spans processes across the whole read-refresh- write, checking cheaply first so ordinary requests never take it - write the cache through a temporary file: opening it for writing truncates it, and a reader landing in that window found it empty and threw the session away - take the lock for every write, not just the refresh, so a login running beside one cannot be silently reverted - wait for a busy lock on Windows rather than giving up after the ten attempts msvcrt allows, and retry the rename it refuses while a reader still holds the file open - bound the refresh request on its own, so a provider that goes quiet cannot hold the lock indefinitely - accept bare hostnames such as localhost, store URLs without the trailing slash, and join download paths through one helper
This commit is contained in:
@@ -2,6 +2,18 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
- Serialised the token refresh across processes. Concurrent `mdrs` invocations shared one refresh token and each sent it, so a provider that rotates refresh tokens accepted the first and refused the rest.
|
||||
- Wrote the login cache through a temporary file so a reader can no longer catch it mid-truncation and discard the session.
|
||||
- Joined the base URL and the API's relative path correctly in `ls --json` output, which produced a doubled separator when the configured URL ended with one.
|
||||
|
||||
### Changed
|
||||
- Accepted bare hostnames such as `localhost` in `config create`/`config update`, and stored the URL without its trailing slash, matching the Rust client so both can share `config.ini`.
|
||||
- Bounded the token refresh request with its own timeout, so a provider that stops answering cannot hold the cross-process lock indefinitely.
|
||||
- **Breaking for embedders:** `CacheInterface` now requires `lock()` and `reload()`. A cache passed to `MdrsClient.from_remote(..., cache=...)` must provide both; `InMemoryCache` implements them as no-ops.
|
||||
|
||||
## [1.3.18] - 2026-07-02
|
||||
|
||||
### Added
|
||||
|
||||
Reference in New Issue
Block a user