Skip to content

[PM-41332] [BWA-260] fix: Recover from missing biometric key on Authenticator unlock - #2926

Open
stefan-jevtic wants to merge 1 commit into
bitwarden:mainfrom
stefan-jevtic:fix/bwa-260-authenticator-biometric-lockout
Open

[PM-41332] [BWA-260] fix: Recover from missing biometric key on Authenticator unlock#2926
stefan-jevtic wants to merge 1 commit into
bitwarden:mainfrom
stefan-jevtic:fix/bwa-260-authenticator-biometric-lockout

Conversation

@stefan-jevtic

Copy link
Copy Markdown

🎟️ Tracking

Fixes #2732 (BWA-260)

📔 Objective

The Authenticator app can end up permanently locked behind a "Use Face ID to Unlock" button that does nothing when tapped.

Cause

getBiometricUnlockStatus() reports .available(_, enabled: true) from two independent sources: the device's biometry status, and the user's stored preference. Those can disagree. The stored preference survives an app reinstall and an iCloud restore; the biometric key in the keychain does not.

When the preference says enabled but the key is gone, AppCoordinator routes to the unlock screen, and VaultUnlockProcessor.unlockWithBiometrics() calls getUserAuthKey(), which throws BiometricsServiceError.getAuthKeyFailed. That error was funnelled into loadData(), which re-reads the same two unchanged sources and produces the same enabled status. Nothing in the state changes and no error surfaces.

The keychain lookup fails on a missing item, so iOS never presents a biometric prompt — which is why the button reads as completely unresponsive rather than as a failed scan. The key cannot be recovered, so every subsequent tap and every relaunch repeats the same dead end.

The reported workaround — revoking the app's biometrics permission in the Settings app — works because it flips the device half of the status to unavailable, so AppCoordinator skips the unlock screen entirely. It depends on iOS showing a Face ID toggle for the app, and iOS only shows that toggle once the app has evaluated a biometric policy at least once. A user who reinstalled before ever granting the permission has no toggle and no way back in (#2732 comment).

Fix

Handle BiometricsServiceError.getAuthKeyFailed explicitly: log it, clear the stale preference via setBiometricUnlockKey(authKey: nil), and complete auth.

This mirrors the recovery BitwardenShared's VaultUnlockProcessor already performs for the same error — the Authenticator's copy of that processor was simply missing the case. The end state is identical to what the Settings-app workaround produces today, so it grants no access the user cannot already obtain, and it reaches the users who currently have no workaround at all. Re-enabling biometric unlock from the app's own settings then goes through evaluateBiometricPolicy(), which requests the permission and writes a fresh key, so the toggle recovers properly instead of resurrecting the broken state.

Other error cases are unchanged: cancellation is still a no-op, and recoverable failures still fall through to loadData() so a retry stays possible.

Tests

Adds VaultUnlockProcessorTests, which did not previously exist for the Authenticator. It covers the lockout regression, the successful-unlock path, cancellation, a recoverable error, and the case where clearing the preference itself fails.

📸 Screenshots

Not applicable — no UI changes. The existing unlock screen is unchanged; it is now reached only when biometric unlock can actually succeed.

…lock

When the Authenticator's biometric unlock preference is enabled but the
biometric key is missing from the keychain, the unlock screen renders a
"Use Face ID to Unlock" button that can never succeed. Tapping it reads
the absent keychain item, which fails without presenting a biometric
prompt, so the button appears completely unresponsive and the user is
locked out of the app.

The key is lost whenever the app is reinstalled or the device is restored
from a backup, neither of which clears the stored preference. Users whose
device never registered the app as a biometrics client have no Face ID
toggle in the Settings app, so they cannot apply the known workaround of
revoking that permission and are locked out permanently.

Handle BiometricsServiceError.getAuthKeyFailed by clearing the stale
preference and completing auth, mirroring how the Password Manager's
VaultUnlockProcessor already recovers from this error. This leaves the app
in the same state the Settings-app workaround produces, and lets the user
re-enable biometric unlock from the app's own settings, which writes a
fresh key.

Adds VaultUnlockProcessorTests, which did not previously exist for the
Authenticator.

Fixes bitwarden#2732

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@CLAassistant

CLAassistant commented Aug 1, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@bitwarden-bot

Copy link
Copy Markdown
Collaborator

Thank you for your contribution! We've added this to our internal Community PR board for review.
ID: PM-41332
Link: https://bitwarden.atlassian.net/browse/PM-41332

Details on our contribution process can be found here: https://contributing.bitwarden.com/contributing/pull-requests/community-pr-process.

@bitwarden-bot bitwarden-bot changed the title [BWA-260] fix: Recover from missing biometric key on Authenticator unlock [PM-41332] [BWA-260] fix: Recover from missing biometric key on Authenticator unlock Aug 1, 2026
@stefan-jevtic
stefan-jevtic marked this pull request as ready for review August 1, 2026 19:03
@stefan-jevtic
stefan-jevtic requested review from a team and matt-livefront as code owners August 1, 2026 19:03
@KatherineInCode KatherineInCode self-assigned this Aug 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BWA-260] Face ID Authentication Bug – Unresponsive Unlock Button

4 participants