diff --git a/mobile-app/app/(tabs)/(settings)/vault-unlock.tsx b/mobile-app/app/(tabs)/(settings)/vault-unlock.tsx
index 2309a45d2..d13cdffbc 100644
--- a/mobile-app/app/(tabs)/(settings)/vault-unlock.tsx
+++ b/mobile-app/app/(tabs)/(settings)/vault-unlock.tsx
@@ -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() {
>
- Choose how you want to unlock your vault
+ Choose how you want to unlock your vault.
@@ -134,7 +145,7 @@ export default function VaultUnlockSettingsScreen() {
/>
- 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.
diff --git a/mobile-app/ios/CredentialManager/SharedCredentialStore.swift b/mobile-app/ios/CredentialManager/SharedCredentialStore.swift
index d43b2a1ff..7daf6532d 100644
--- a/mobile-app/ios/CredentialManager/SharedCredentialStore.swift
+++ b/mobile-app/ios/CredentialManager/SharedCredentialStore.swift
@@ -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