Fix keychain accessibility issue (#771)

This commit is contained in:
Leendert de Borst
2025-04-23 14:15:46 +02:00
parent b19ee32b28
commit 2a808fb137
2 changed files with 46 additions and 9 deletions

View File

@@ -4,10 +4,11 @@ import { ThemedSafeAreaView } from '@/components/ThemedSafeAreaView';
import { useColors } from '@/hooks/useColorScheme';
import * as LocalAuthentication from 'expo-local-authentication';
import { useState, useEffect, useMemo, useCallback } from 'react';
import { useAuth } from '@/context/AuthContext';
import { AuthMethod, useAuth } from '@/context/AuthContext';
export default function VaultUnlockSettingsScreen() {
const colors = useColors();
const [initialized, setInitialized] = useState(false);
const { setAuthMethods, enabledAuthMethods } = useAuth();
const [hasFaceID, setHasFaceID] = useState(false);
const [isFaceIDEnabled, setIsFaceIDEnabled] = useState(false);
@@ -23,15 +24,25 @@ export default function VaultUnlockSettingsScreen() {
if (enabledAuthMethods.includes('faceid')) {
setIsFaceIDEnabled(true);
}
setInitialized(true);
}, []);
useEffect(() => {
if (isFaceIDEnabled) {
setAuthMethods(['faceid', 'password']);
} else {
setAuthMethods(['password']);
if (!initialized) {
return;
}
}, [isFaceIDEnabled, setAuthMethods]);
// Check if there are actually differences between the current and the new auth methods
const currentAuthMethods = enabledAuthMethods;
const newAuthMethods = isFaceIDEnabled ? ['faceid', 'password'] : ['password'];
if (currentAuthMethods.length === newAuthMethods.length && currentAuthMethods.every(method => newAuthMethods.includes(method))) {
return;
}
console.log('Updating auth methods to', newAuthMethods);
setAuthMethods(newAuthMethods as AuthMethod[]);
}, [isFaceIDEnabled, setAuthMethods, enabledAuthMethods, initialized]);
const handleFaceIDToggle = useCallback(async (value: boolean) => {
if (value && !hasFaceID) {
@@ -117,7 +128,7 @@ export default function VaultUnlockSettingsScreen() {
>
<View style={styles.header}>
<ThemedText style={styles.headerText}>
Choose how you want to unlock your vault
Choose how you want to unlock your vault.
</ThemedText>
</View>
@@ -134,7 +145,7 @@ export default function VaultUnlockSettingsScreen() {
/>
</View>
<ThemedText style={styles.helpText}>
Your vault decryption key will be securely stored on your local device in the iOS Keychain and can only be accessed with your face or fingerprint.
Your vault decryption key will be securely stored on your local device in the iOS Keychain and can be accessed with your face or fingerprint.
</ThemedText>
</View>

View File

@@ -18,6 +18,8 @@ struct AuthMethods: OptionSet {
class SharedCredentialStore {
static let shared = SharedCredentialStore()
private let keychain = Keychain(service: "net.aliasvault.autofill", accessGroup: "group.net.aliasvault.autofill")
.accessibility(.whenPasscodeSetThisDeviceOnly, authenticationPolicy: .biometryAny)
private let encryptionKeyKey = "aliasvault_encryption_key"
private let encryptedDbFileName = "encrypted_db.sqlite"
private let authMethodsKey = "aliasvault_auth_methods"
@@ -36,6 +38,24 @@ class SharedCredentialStore {
enabledAuthMethods = methods
UserDefaults.standard.set(methods.rawValue, forKey: authMethodsKey)
UserDefaults.standard.synchronize()
if !enabledAuthMethods.contains(.faceID) {
// If Face ID is now disabled, remove the persisted key from keychain if it exists
print("Face ID is now disabled, removing key from keychain")
try? keychain.remove(encryptionKeyKey)
}
else {
// If Face ID is now enabled, persist the current key from memory into keychain
print("Face ID is now enabled, persisting key to keychain")
do {
if let key = encryptionKey {
try storeEncryptionKey(base64Key: key.base64EncodedString())
}
} catch {
print("Failed to save existing key from memory to keychain: \(error)")
throw error
}
}
}
// MARK: - Vault Status
@@ -88,18 +108,24 @@ class SharedCredentialStore {
// Store the key in memory
encryptionKey = keyData
print("Stored key in memory")
// Store the key in the keychain if Face ID is enabled
if enabledAuthMethods.contains(.faceID) {
print("Face ID is enabled, storing key in keychain")
do {
try keychain
.authenticationPrompt("Authenticate to unlock your vault")
.authenticationPrompt("Authenticate to save your vault decryption key in the iOS keychain")
.set(keyData, key: encryptionKeyKey)
print("Key saved to keychain")
} catch {
print("Failed to save key to keychain: \(error)")
throw error
}
}
else {
print("Face ID is disabled, not storing key in keychain")
}
}
// MARK: - Database Management