mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-03-27 02:52:04 -04:00
Improve iOS quick passkey autofill to work on iOS 18+
This commit is contained in:
@@ -228,52 +228,58 @@ public class CredentialProviderViewController: ASCredentialProviderViewControlle
|
||||
// If we're in quick return mode, now trigger the unlock and complete the request
|
||||
// The loading view is already visible from viewWillAppear
|
||||
if isQuickReturnMode {
|
||||
let vaultStore = VaultStore()
|
||||
// Dispatch async to ensure the view is fully rendered before showing biometric prompt
|
||||
// This prevents a race condition where the first tap doesn't trigger the biometric UI
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
if !sanityChecks(vaultStore: vaultStore) {
|
||||
return
|
||||
}
|
||||
let vaultStore = VaultStore()
|
||||
|
||||
// Check if biometric authentication is available
|
||||
if !vaultStore.isBiometricAuthEnabled() {
|
||||
print("Quick return failed: Biometric auth not enabled")
|
||||
self.extensionContext.cancelRequest(withError: NSError(
|
||||
domain: ASExtensionErrorDomain,
|
||||
code: ASExtensionError.failed.rawValue,
|
||||
userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("biometric_auth_required_message", comment: "Please enable Face ID in the main AliasVault app to use autofill.")]
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
try vaultStore.unlockVault()
|
||||
|
||||
if let passkeyRequest = quickReturnPasskeyRequest {
|
||||
handleQuickReturnPasskeyCredential(vaultStore: vaultStore, request: passkeyRequest)
|
||||
} else if let passwordRequest = quickReturnPasswordRequest {
|
||||
handleQuickReturnPasswordCredential(vaultStore: vaultStore, request: passwordRequest)
|
||||
if !self.sanityChecks(vaultStore: vaultStore) {
|
||||
return
|
||||
}
|
||||
} catch let error as NSError {
|
||||
print("Quick return vault unlock failed: \(error)")
|
||||
|
||||
// Provide specific error message based on error code
|
||||
var errorMessage = error.localizedDescription
|
||||
if error.domain == "VaultStore" {
|
||||
switch error.code {
|
||||
case 3:
|
||||
errorMessage = NSLocalizedString("no_encryption_key_message", comment: "No encryption key found. Please unlock the vault in the main AliasVault app first.")
|
||||
case 9:
|
||||
errorMessage = NSLocalizedString("keychain_error_message", comment: "Failed to retrieve encryption key. This may be due to cancelled biometric authentication.")
|
||||
default:
|
||||
break
|
||||
// Check if biometric authentication is available
|
||||
if !vaultStore.isBiometricAuthEnabled() {
|
||||
print("Quick return failed: Biometric auth not enabled")
|
||||
self.extensionContext.cancelRequest(withError: NSError(
|
||||
domain: ASExtensionErrorDomain,
|
||||
code: ASExtensionError.failed.rawValue,
|
||||
userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("biometric_auth_required_message", comment: "Please enable Face ID in the main AliasVault app to use autofill.")]
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
try vaultStore.unlockVault()
|
||||
|
||||
if let passkeyRequest = self.quickReturnPasskeyRequest {
|
||||
self.handleQuickReturnPasskeyCredential(vaultStore: vaultStore, request: passkeyRequest)
|
||||
} else if let passwordRequest = self.quickReturnPasswordRequest {
|
||||
self.handleQuickReturnPasswordCredential(vaultStore: vaultStore, request: passwordRequest)
|
||||
}
|
||||
}
|
||||
} catch let error as NSError {
|
||||
print("Quick return vault unlock failed: \(error)")
|
||||
|
||||
self.extensionContext.cancelRequest(withError: NSError(
|
||||
domain: ASExtensionErrorDomain,
|
||||
code: ASExtensionError.failed.rawValue,
|
||||
userInfo: [NSLocalizedDescriptionKey: errorMessage]
|
||||
))
|
||||
// Provide specific error message based on error code
|
||||
var errorMessage = error.localizedDescription
|
||||
if error.domain == "VaultStore" {
|
||||
switch error.code {
|
||||
case 3:
|
||||
errorMessage = NSLocalizedString("no_encryption_key_message", comment: "No encryption key found. Please unlock the vault in the main AliasVault app first.")
|
||||
case 9:
|
||||
errorMessage = NSLocalizedString("keychain_error_message", comment: "Failed to retrieve encryption key. This may be due to cancelled biometric authentication.")
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
self.extensionContext.cancelRequest(withError: NSError(
|
||||
domain: ASExtensionErrorDomain,
|
||||
code: ASExtensionError.failed.rawValue,
|
||||
userInfo: [NSLocalizedDescriptionKey: errorMessage]
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,17 +452,8 @@ public class VaultManager: NSObject {
|
||||
// Get all credentials from the vault
|
||||
let credentials = try vaultStore.getAllCredentials()
|
||||
|
||||
if #available(iOS 26.0, *) {
|
||||
// iOS 26+: Register both passwords and passkeys for QuickType and manual selection
|
||||
try await CredentialIdentityStore.shared.saveCredentialIdentities(credentials)
|
||||
} else {
|
||||
// iOS 17 and 18: Only register passkeys (skip passwords for QuickType as biometric unlock is buggy on these versions)
|
||||
let passkeyOnlyCredentials = credentials.filter { credential in
|
||||
guard let passkeys = credential.passkeys else { return false }
|
||||
return !passkeys.isEmpty
|
||||
}
|
||||
try await CredentialIdentityStore.shared.saveCredentialIdentities(passkeyOnlyCredentials)
|
||||
}
|
||||
// Register both passwords and passkeys for QuickType and manual selection
|
||||
try await CredentialIdentityStore.shared.saveCredentialIdentities(credentials)
|
||||
|
||||
await MainActor.run {
|
||||
resolve(nil)
|
||||
|
||||
@@ -222,6 +222,10 @@ extension VaultStore {
|
||||
context.interactionNotAllowed = false
|
||||
context.localizedReason = "Authenticate to unlock your vault"
|
||||
|
||||
// Add a small delay to ensure the context is fully ready
|
||||
// This helps prevent race conditions where the biometric prompt doesn't show on first tap
|
||||
Thread.sleep(forTimeInterval: 0.05)
|
||||
|
||||
let query: [String: Any] = [
|
||||
kSecClass as String: kSecClassGenericPassword,
|
||||
kSecAttrService as String: VaultConstants.keychainService,
|
||||
|
||||
Reference in New Issue
Block a user