5.2.0 stops selecting a token from copying it, and gets the tray menu working outside KDE Plasma (#470, #471, flathub#81).
Clicking a row, or just arrowing past one, used to copy the code and, for HOTP, burn a counter, so scrolling through the list could silently desynchronise an account from its provider. Selection is now inert and each row carries an explicit Copy or Generate button, with Enter and Ctrl+C as the keyboard equivalent. Underneath that, HOTP counters are written to the encrypted database before a code is ever shown, in a single transaction, which also means the GUI and the command line finally agree on what the stored counter means.
The tray menu was separately empty on every desktop except KDE Plasma, and had been since the tray was rewritten: right-clicking the icon opened a blank rectangle on GNOME, Cinnamon, MATE, Xfce and Waybar alike. That is fixed too, and OTPClient can now start minimized to the tray and start at login. A pre-release audit of the whole release then turned up a handful more things that had never worked anywhere: GNOME Shell search results that did nothing when activated, desktop integration that could not be started at all on sessions using dbus-daemon, and exports that reported success after writing nothing.
Selection and HOTP counters
- FIX: merely selecting an HOTP token consumed its counter. Single-click and arrow-key navigation both went through the copy path, so browsing the list advanced counters with nothing to show for it, and enough of that would desynchronise the account from its provider. Selection now does nothing but select. A new Action column carries a button per row, copy for TOTP and generate for HOTP; the same action heads the row's right-click menu, a double-click on the row does the same, and Enter and Ctrl+C activate the selected token while the list has focus
- FIX: an HOTP counter could be shown or copied without ever reaching disk. The GUI generated the code, advanced the counter in memory, and deferred the encrypted re-save by up to five seconds to coalesce bursts of clicks; a crash, a kill, or a power loss inside that window handed out a code the database no longer knew it had issued. Generation and persistence are now one atomic transaction, and the code is not revealed until the write has committed
- FIX: the validity countdown was drawn only for the selected row, a leftover from when selecting a row was what revealed its code. With selection inert the two came apart, and a code could sit visible with no countdown beside it. The countdown now follows the code it counts down, and HOTP rows no longer get one at all, having no rotation period
- FIX: toggling "Hide OTPs by Default" left the already-drawn rows alone until a scroll or a selection change rebound them. The refresh was wired up in the window's
constructed(), which reads the application property before GObject has set it, so the hook was never connected at all
Tray and startup
- FIX: the tray menu was an empty rectangle on every host built on libdbusmenu, which is GNOME with AppIndicator, Cinnamon, MATE, Xfce, Ayatana and Waybar. The tray declared dbusmenu version 3, which makes clients fetch labels with
GetGroupPropertiesand dispatch clicks throughEventGroup, and neither method existed, so both menu entries were silently dropped (#470, flathub#81) - NEW: start minimized to the tray, from Settings under Integration or with
otpclient --start-minimized. It needs minimize-to-tray and a real system tray, and is ignored with the window shown normally when either is missing. The database is deliberately left locked (#471) - NEW: start at login, from Settings under Integration. Native builds write an autostart entry; the Flatpak asks the desktop through the background portal, which a few desktops do not implement, and there the setting is greyed out
- FIX: an autostart entry created by hand, or by GNOME Tweaks' Startup Applications, was deleted on the first launch of this release. An entry already present is now adopted, with the setting turned on to match, instead of being removed
- FIX: with start-minimized on, the first click on the tray icon always asked for the master password, even with the password saved in the keyring. The deferred unlock never consulted the keyring; it does now
- FIX: the start-at-login and background-permission settings recorded what had been asked for rather than what the desktop agreed to, the worst shape of that being a login entry nobody can see still launching the app while the switch reads off. Every answer is now reconciled against what the desktop actually said
- FIX: the autostart entry named the executable without a path and carried no
TryExec, so an install outside the session's PATH wrote an entry that silently never launched - FIX: the background-permission prompt was closed out from under the user after a minute, which turned minimize-to-tray off and blamed the desktop for refusing it. The wait is now five minutes
- FIX: a tray host restarting, a GNOME Shell reload or a
plasmashell --replace, force-raised a window the user had hidden. A short grace period now tells a panel that is coming back from one that is gone - FIX: the tray icon now says so when the database is locked, and "Show OTPClient" raises the window on Wayland instead of being blocked by focus-stealing prevention
Desktop search
- FIX: pressing Enter on a GNOME Shell search result did nothing at all, and never had. A malformed format string meant the handler read none of its arguments, so there was no copy, no notification and no error, only a warning in the journal
- FIX: neither the search provider nor the KRunner plugin could be started at all on sessions that activate services through
dbus-daemonrather than systemd, which covers the Debian and Ubuntu defaults, distributions without systemd, and anything run underdbus-run-session. The service files named the executable without a path, anddbus-daemondoes not search PATH - FIX: with the search provider disabled, the process exited before taking its bus name, so every search produced a spawn error instead of an empty result
- FIX: a locked login keyring froze the search provider inside the first search it was asked for, with every other request queued behind. The keyring lookup and the key derivation now run off the main loop
- FIX: search-provider settings needed a restart, typically a logout, to take effect. Disabling the provider, clearing its keyword, or turning off Secret Service now applies immediately and revokes cached keys and entry lists
- FIX: activating a search result whose password is not in the keyring, or whose database will not decrypt, did nothing whatsoever, not even a log line. The reason is now reported
- FIX: an HOTP search result from a database other than the open one had nowhere to write its counter. Activating one now opens the owning database
- FIX: handing the code to a clipboard tool could hang the search provider indefinitely, which is what an
xclipagainst an unreachable display does. The call is now asynchronous and bounded by a timeout - FIX: the notification carrying the code is now marked transient, so desktops keep it out of their notification history
Import and export
- FIX: an export that could not be written reported success. None of the four exporters closed the file they had opened, and with the atomic-replace mode in use the rename happens inside that close, so a full disk produced "Data successfully exported to" over a truncated file. This is the backup feature, so the silence was the worst part of it
- FIX: a token whose account name contains a percent sign, or whose issuer contains a colon, did not survive a round trip through our own plain-text export. The label was percent-decoded twice, so "alice 100%" was rejected outright on re-import and "Acme:Corp" was split in the wrong place. Files written by older releases still import
- NEW: partial imports now report what was skipped and why, per entry, instead of silently importing fewer tokens than the file contained. The command line keeps a success status when at least one valid entry was imported and prints the warnings to stderr; input with nothing valid in it fails and leaves the database untouched
- FIX: malformed third-party backups could slip past the importers. The Aegis, Authenticator Pro and 2FAS readers now check that the entry list is actually a list, validate and repair each token before accepting it, and no longer leak the fields of an entry they reject
- FIX: a new encrypted migration export could be created with an empty password, producing a file that looked encrypted and was not. Existing empty-password exports still import
- FIX: encrypted Aegis, Authenticator Pro and 2FAS exports were written from a buffer sized with different formatting flags than the dump that filled it, and the size mismatch was checked against a value the function cannot return
- FIX: importing a large file of junk produced one diagnostic line per skipped entry and handed the lot to a single label, which for a big enough file meant hundreds of megabytes of text nobody could read. The list now stops at the first fifty and counts the rest
- FIX: reading an otpauth file trusted the size reported before the read rather than the number of bytes actually read, so a file rewritten shorter in between was read past its end and then zeroed past its end
- FIX: Steam tokens were exported with the issuer named twice, which strict third-party parsers reject
Database and storage
- FIX: a database still in the version 1 or 2 format could crash the app while being upgraded. If the decryption that follows the upgrade failed, the same buffer was released twice. Upgrading is the first thing an older database does on this release, so the window was narrow but badly placed
- FIX: a database whose directory is full, read-only or over quota could not be saved at all. The lock file added in 5.1.x reported "Failed to open database lock" and stopped there, where it was meant to warn once and carry on unlocked
- FIX: the backup taken beside the database was overwritten with a copy of the database that had just been saved, so it held the same generation as the file it was there to protect. It is now taken before the write and left alone afterwards
- FIX: drag-reordering the list when the save failed left what is on screen and what is in the database disagreeing about which row is which. The next Copy or Generate then acted on a different account. The list is now rebuilt from the database whenever a reorder fails to commit
Secrets and locking
- FIX: exiting or locking wiped the clipboard even when the user had since copied something else, destroying unrelated data. OTPClient now clears the clipboard only while it still owns the content it put there
- FIX: locking left sensitive dialogs open with their contents intact. A displayed QR code, a typed secret, an export password, or an unlock prompt survived the lock in memory and on screen. Locking now closes those dialogs, zeroes their entry buffers, drops their QR textures and snapshots, and cancels any operation still in flight
- FIX: secrets were left in ordinary, pageable memory in three places: the key that unwraps an encrypted Aegis backup, the per-token seeds handed back by the Google Authenticator migration decoder, and the URI written by the FreeOTP+ exporter. All three now use the secure pool or are wiped before being released
- FIX: the typed Base32 secret and the export password were left sitting in their entry buffers when the dialog closed along certain paths, instead of being zeroed
Command line
- FIX: the JSON output was not a single document and the CSV had no header row, so neither could be piped into an ordinary parser. Diagnostics also went to stdout, mixed in with the data. JSON is now one document, CSV starts with a header, and diagnostics go to stderr
- FIX: every argument-validation error went to standard output, contrary to what the manual page promises. A mistyped option fed an English sentence to whatever was parsing the JSON. They now go to standard error
- FIX: the master password was never saved to the keyring on first run, so with Secret Service enabled the CLI asked for the password on every single invocation and never explained why. The store was started asynchronously in a process that has no main loop to finish it
- FIX:
otpclient-clifailed with "Application does not handle command line arguments" whenever the GUI was running, because both registered the same application id and the CLI became a remote of the GUI
Flatpak
- FIX: activating a search result copied nothing. The daemon shells out to a clipboard tool and the runtime shipped none. The tools are now bundled, the Wayland session is detected from
WAYLAND_DISPLAYrather than a variable the sandbox may not set, and every available tool is tried instead of committing to one - FIX: databases opened from outside the Flatpak's own data directory were saved without any lock held. The document portal implements POSIX record locks and refuses
flockoutright, which is what the code used - FIX: a database picked from outside the sandbox could stop opening after a reboot, on btrfs and other filesystems whose device numbers are not stable. The dialog now says the sandbox lost access rather than that the file is missing, and offers to re-pick it
- FIX: turning minimize-to-tray off inside the Flatpak left a dead icon in the panel until the app exited, because the sandboxed tray had no bus name it could release
- FIX: every launch logged a warning about not being able to subscribe to suspend events. The system bus is not there to be reached, so the attempt is no longer made. Locking on screen lock is unaffected
Other fixes
- FIX: webcam QR scanning has never worked in the GTK4 rewrite, on any platform. The scanner opened a zbar preview window instead of the camera, then failed on every frame and reported a timeout. It now opens the camera, and no longer needs an X display to do it
- FIX: a QR code pasted from the clipboard with a transparent background never scanned. Pasted images arrive with their colour channels premultiplied by alpha, which the brightness calculation ignored, so a transparent background read as solid black. Images opened from a file were never affected
- FIX: importing or exporting settings from a network or phone mount, sftp, SMB, MTP or a cloud drive, crashed the app. Those locations have no local path, and the failure was reported by reading an error that had never been set
- FIX: searching while the database was locked froze the window for minutes and filled the journal with warnings. The group list is empty while locked, and the loop over it counted down from an empty list rather than skipping it
- FIX: clicking another database in the sidebar while the current one was locked switched to it anyway, out from under the lock
- FIX: "Locate..." on the "Database No Longer Accessible" dialog routed into the plain open picker, which appends a row, so relocating a database left the old entry behind. It now replaces the path on the row that is already there, keeps its name, and moves the saved Secret Service password across
- FIX: a settings backup silently dropped the search-provider keyword and the clipboard-clear timeout, both of which the settings dialog offers, and an imported sidebar-visibility value did not reach a window that was already open
- FIX: several memory leaks in the GUI, and one path where a dialog could be disposed twice
Upgrade notes
- NOTE: the stored HOTP counter now means the next unused code in both interfaces. Older GUI versions stored the last generated one while the CLI stored the next, and there is no way to tell which wrote a given value, so existing counters are left exactly as they are. If an HOTP account rejects its first code after upgrading, generate the next one or resynchronise the counter with the provider. This affects HOTP only, and only accounts previously used from the GUI
- NOTE: the backup reminder now records history and snoozes per database instead of globally, and exports no longer count as backups. The old global value cannot be attributed to a database, so it is not carried over: after upgrading, each database reads "No backup recorded" until you take one, and any active snooze is reset
- NOTE: the search provider's trigger keyword is a query filter, not an authentication mechanism, and is no longer described as one. Desktop search additionally requires Secret Service access, a saved database password, and the provider to be enabled
- NOTE: the search provider is a separate process with its own lock state. A hidden or locked GUI does not stop search from working, and using search does not unlock or reveal the GUI
- NOTE: if you use the system keyring to unlock automatically and turn start-minimized on, OTPClient will sit in the tray over a locked database rather than an open one. That is intentional, and it is the reason the first show asks for the password
Packaging
- No new dependencies. The D-Bus service files for the search provider and the KRunner plugin now carry an absolute
Execpath, so packagers installing to a prefix other than the configured one should confirm the installed path is the one written into those files. otpclient-clino longer shares an application id with the GUI, and the autostart entry written by start-at-login carries the absolute install path and aTryExec.
sha256: 627c632091831be500cdb4bc3dcefceef8082d972e587f8ff72c7c6e3f8eec26