The keyring store keeps one D-Bus connection and one Secret Service session for the whole process, and was installed once. The session lives only in the provider's memory, so after the provider restarts every read and write on it fails with NoSession until the process restarts. oo7, the provider on Fedora 45, restarts on failure and can be replaced. A daemon that met this kept every later token refresh only in memory and the kernel keyring, and the next cold boot found the spent refresh token (issue #78, B285). Every keyring call now goes through with_keyring_entry. A platform failure drops the store, unless another thread already replaced it, and the call is made once more on a new connection. A locked keyring, a dismissed prompt and a missing entry come back unchanged. Reproduced against gnome-keyring 51.1 and oo7 0.7.1 restarted with --replace in a throwaway session bus; the ignored test real_secret_service_survives_a_provider_restart passes against both. Refs #78
7.5 KiB
Issue #78: session loss on Fedora 45
Investigation on 2026-11-14 of issue #78,
using its report and comments through gh, pdfs 3.7.0/current source, and the oo7 0.7.0 tag.
What is established
The reporter still saw the problem with oo7 0.7.0 on November 7. The original
SetSecret persistence fix
is present in that release. In an isolated test, three successive binary SetSecret updates
survived reopening the keyring file. Repeating the original persistence explanation is therefore
insufficient.
A different, reproducible failure exists at the boundary between oo7 and pdfs:
- oo7 0.7.0's
Collection::search_inner_itemsreturns an empty list for a locked collection.SearchItemsconsequently returns both the unlocked and locked lists empty even when the matching item is on disk. The isolated test reproduces this with pdfs's exact lookup attributes. This is upstream oo7 #614. dbus-secret-service-keyring-store1.0.1 maps an empty search tokeyring_core::Error::NoEntry; pdfs maps that toError::NotLoggedIn. The client cannot offer an unlock when no locked item is reported.- In pdfs 3.7.0 and the code before this change,
TokenSink::flushtreatedNotLoggedInas a logout, cleared the pending rotation and returned success. It skipped the error path that retains the new tokens in the kernel keyring and retries their persistence. The code was unchanged between v3.7.0 and this investigation. - If a token refresh encounters that reply, the saved blob retains a spent refresh token. The running process can keep working with the new tokens in memory; a restart then exposes the stale saved credentials. A regression test failed before the fix and passes afterward.
The same unsafe assumption also applies to transient empty lookups such as the race reported in
oo7 #652, fixed in 0.7.1. That issue's
replacement race was not independently reproduced here; pdfs normally updates existing items
with SetSecret, so it must not be assumed to encounter the same-item CreateItem race.
Change and limits
TokenSink::flush now propagates a missing-session error instead of discarding the rotation.
The existing retry, kernel-keyring recovery and unsaved-token status paths can then run. Once
the collection is unlocked and readable, the pending rotation is saved. A genuinely deleted
session is never recreated: flushing still requires a readable blob with the matching session
ID and expected refresh token. A new login causes the old rotation to be discarded as before.
This is a confirmed pdfs defect (B284), not a confirmed complete explanation of the reporter's
cold-boot failure. The issue has no oo7 logs or record showing that its collection was locked
when a token rotated. A locked keyring only at startup can hide a login, but does not by itself
explain InvalidRefreshToken. The token-loss path requires a successful refresh while the saved
entry is unreadable.
The fix does not make a locked keyring writable. If it stays unreadable until power-off, tokens retained in process/kernel memory still cannot survive a cold boot. No new disk credential store or credential format is introduced.
Other upstream Fedora-upgrade issues were checked: oo7 #651 creates duplicate placeholder/migrated collections and refuses writes; oo7 #662 can resolve the default alias to a locked collection. Both have fixes in 0.7.1, released November 10, after the reporter's November 7 retest. Neither can be assigned as this report's cause without matching runtime evidence.
A restarted Secret Service
A second path to the same symptom, independent of locking (B285). The keyring store pdfs used
holds one D-Bus connection and one Secret Service session for the whole process, and was
installed once. The session lives only in the provider's memory. When the provider restarts,
the new one answers every GetSecret and SetSecret on the old session with
org.freedesktop.Secret.Error.NoSession, until the pdfs process itself restarts. Reproduced
with both gnome-keyring 51.1 and oo7 0.7.1 using --replace.
gnome-keyring practically never restarts within a desktop session. oo7 can:
oo7-daemon.service has Restart=on-failure, the daemon allows itself to be replaced, and it
exits on a wrong credential password and is D-Bus-activated again. A daemon that met a restarted
provider kept every later token refresh only in memory and the kernel keyring, and a cold boot
then left the spent refresh token in the saved blob.
Now a platform failure drops the store and the call is retried once on a new connection. The
daemon log shows the Secret Service failed this process's connection; reconnecting when that
happens. To check whether a machine meets this, look for oo7 restarts in the same boot:
journalctl --user -b -1 -u oo7-daemon.service | grep -E 'Started|Stopped|Starting oo7|Main process exited'
journalctl --user -b -1 -u proton-drive.service | grep -iE 'keyring|reconnecting|refresh'
Reproduce in a throwaway session bus (never against the desktop's keyring; the runtime directory must be short, as it holds a Unix socket):
T=$(mktemp -d /tmp/ss.XXXX); mkdir -p $T/data $T/run; chmod 700 $T/run
XDG_DATA_HOME=$T/data XDG_RUNTIME_DIR=$T/run PDFS_SECRET_SERVICE_RESTART_TEST=1 \
PDFS_RESTART_PROVIDER='echo -n pw | gnome-keyring-daemon --replace --unlock --components=secrets >/dev/null' \
dbus-run-session -- sh -c 'echo -n pw | gnome-keyring-daemon --unlock --components=secrets >/dev/null; sleep 1;
cargo test -p pdfs-core real_secret_service -- --ignored'
For oo7, start oo7-daemon --login with the password on stdin instead, and restart it with
--login --replace in the background.
Reproduce without a Proton account or desktop keyring
The oo7 fixture uses fake tokens, a temporary disk keyring and a private peer-to-peer D-Bus connection. It asserts the observed 0.7.0 behavior, including the incorrect empty locked lookup; passing means that behavior was reproduced. It does not simulate Proton refresh or a full boot.
From the pdfs repository, choose an unused temporary checkout path:
git clone --depth 1 --branch 0.7.0 https://github.com/linux-credentials/oo7.git /tmp/pdfs-oo7-repro
git -C /tmp/pdfs-oo7-repro apply "$PWD/docs/repro/oo7-0.7.0-issue78.patch"
cargo test --manifest-path /tmp/pdfs-oo7-repro/Cargo.toml \
-p oo7-daemon --lib --features test-util issue78 -- --nocapture
Observed output:
oo7 0.7.0: locked session is on disk but SearchItems returns (0, 0)
oo7 0.7.0: all three binary SetSecret updates survived reopening the disk keyring
test item::tests::issue78_locked_lookup_hides_saved_tokens ... ok
The pdfs side is covered by cargo test -p pdfs-core auth::tests, specifically
a_temporarily_missing_session_does_not_discard_a_rotation and
retrying_a_missing_session_never_recreates_it_or_overwrites_a_new_login.
Remaining live validation: reproduce on a Fedora 45 GNOME session across logout/shutdown, correlating oo7 lock/write errors with pdfs refresh and retry logs, and checking the running daemon's package version rather than just the CLI version. The investigation did not use a real Proton account, alter a desktop keyring, or post to either GitHub issue.