Fix WINBIO_E_INVALID_TICKET (0x80098044) on face unlock: enforce foreground on the Hello prompt#159
Open
MrSco wants to merge 1 commit into
Open
Conversation
…NVALID_TICKET The long-standing 0x80098044 failure on face unlock (sirAndros#113, sirAndros#118, sirAndros#120, sirAndros#128, sirAndros#130, sirAndros#132, sirAndros#137-sirAndros#139) turns out to be a foreground race on the first Hello prompt of a fresh KeePass launch: the credential dialog ("Windows Security", hosted in another process) can lose the foreground while KeePass is still starting up, and a biometric gesture that completes against a non-foreground dialog yields a rejected ticket. PIN was never affected only because typing it takes long enough for the window state to settle - which also explains why every unlock after the first one works with face. The repo already had the cure (WinHelloProviderForegroundDecorator) but only applied it when KeePass ran elevated, and only once per unlock, so plain retries kept failing. Now every decrypt prompt runs under a CredentialDialogForegroundEnforcer that grants foreground rights and keeps pushing the credential dialog to the foreground while it settles; on WINBIO_E_INVALID_TICKET the parent window is brought to the front and the prompt is retried immediately under the same enforcement. If all retries fail, a presentable AuthProviderInvalidTicketException suggests the PIN fallback instead of surfacing a raw error code. Also repairs a truncated KeyManager.cs, keeps net4.0 compatibility (Interlocked.CompareExchange instead of Volatile.Read), and adds tools/HelloTicketProbe, a standalone console harness that replays the plugin's exact NGC/NCrypt calls for diagnosing Hello ticket issues (note: a console app is always foreground, so it cannot reproduce this particular race). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
MrSco
marked this pull request as ready for review
July 11, 2026 20:42
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



The bug
Face unlock fails with
NCryptDecrypt→0x80098044(WINBIO_E_INVALID_TICKET) on the first prompt of a fresh KeePass launch, while PIN always works — reported in #113, #118, #120, #128, #130, #132, #137, #138, #139.Root cause
It's a foreground race, not a stale ticket, key corruption, or Windows-session state. The Windows Hello credential dialog ("Windows Security", class
Credential Dialog Xaml Host) is hosted in another process. During KeePass startup the dialog can lose the foreground to KeePass's own appearing windows, and Windows rejects a biometric gesture ticket that completes against a non-foreground dialog. This is the same failure mode Bitwarden confirmed in their desktop app ("Windows Hello fails if the prompt is not in the foreground").The evidence, gathered on a machine reproducing this 100% of the time (Windows 11 Pro 25H2, no Enhanced Sign-in Security):
tools/HelloTicketProbe, included in this PR) replaying the plugin's exact NGC/NCrypt call sequence against the same persistent key never fails — a console app the user just interacted with is always foreground.This also explains why plain retry loops (the approach in #158) don't fix it: each retried prompt hits the same unsettled foreground state. The missing ingredient is foreground enforcement, which this repo already had —
WinHelloProviderForegroundDecorator— but it only activates when KeePass runs elevated, and only once per unlock rather than per prompt attempt.The fix
CredentialDialogForegroundEnforcer(inWinHelloProvider): around everyNCryptDecryptprompt, grant foreground rights (AllowSetForegroundWindow(ASFW_ANY)) and keep pushing the credential dialog to the foreground (~2 s, 100 ms cadence, stops as soon as the dialog is foreground) — the same technique the elevated-only decorator used, now applied to every prompt regardless of elevation.WINBIO_E_INVALID_TICKET: bring the parent window to the front and immediately re-prompt (no artificial delay — the prompt is user-paced), with the retry also running under the enforcer.AuthProviderInvalidTicketExceptionsuggesting the PIN option instead of surfacing a raw0x80098044.KeyManager.cs(net4.0-compatibleInterlocked.CompareExchangeinstead ofVolatile.Read) and adds theHelloTicketProbediagnostic harness used during the investigation.Testing
On hardware reproducing the bug on every fresh launch: with this change, face unlock succeeds on the first prompt of a fresh KeePass start. Subsequent unlocks unaffected. PIN path unaffected.
Closes #113, closes #118, closes #120, closes #128, closes #130, closes #132, closes #137, closes #138, closes #139.
🤖 Generated with Claude Code