WellNuo/services/wifiPasswordStore.ts
Sergei bbc45ddb5f Implement secure WiFi password storage using SecureStore
- Create wifiPasswordStore service for encrypted password storage
- Replace AsyncStorage with SecureStore for WiFi credentials
- Add automatic migration from AsyncStorage to SecureStore
- Integrate WiFi password cleanup into logout process
- Add comprehensive test suite for password storage operations
- Update setup-wifi screen to use secure storage

Security improvements:
- WiFi passwords now stored encrypted via expo-secure-store
- Passwords automatically cleared on user logout
- Seamless migration for existing users

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-29 11:13:37 -08:00

149 lines
4.2 KiB
TypeScript

/**
* WiFi Password Secure Storage Service
*
* Provides secure storage for WiFi passwords using expo-secure-store.
* Replaces AsyncStorage for improved security.
*/
import * as SecureStore from 'expo-secure-store';
const WIFI_PASSWORDS_KEY = 'WIFI_PASSWORDS';
const LEGACY_SINGLE_PASSWORD_KEY = 'LAST_WIFI_PASSWORD';
export interface WiFiPasswordMap {
[ssid: string]: string;
}
/**
* Save WiFi password for a specific network
* @param ssid Network SSID
* @param password Network password
*/
export async function saveWiFiPassword(ssid: string, password: string): Promise<void> {
try {
// Get existing passwords
const existing = await getAllWiFiPasswords();
// Add/update the password
existing[ssid] = password;
// Save back to SecureStore
await SecureStore.setItemAsync(WIFI_PASSWORDS_KEY, JSON.stringify(existing));
console.log('[WiFiPasswordStore] Password saved for network:', ssid);
} catch (error) {
console.error('[WiFiPasswordStore] Failed to save password:', error);
throw error;
}
}
/**
* Get WiFi password for a specific network
* @param ssid Network SSID
* @returns Password or undefined if not found
*/
export async function getWiFiPassword(ssid: string): Promise<string | undefined> {
try {
const passwords = await getAllWiFiPasswords();
return passwords[ssid];
} catch (error) {
console.error('[WiFiPasswordStore] Failed to get password:', error);
return undefined;
}
}
/**
* Get all saved WiFi passwords
* @returns Map of SSID to password
*/
export async function getAllWiFiPasswords(): Promise<WiFiPasswordMap> {
try {
const stored = await SecureStore.getItemAsync(WIFI_PASSWORDS_KEY);
if (stored) {
return JSON.parse(stored);
}
return {};
} catch (error) {
console.error('[WiFiPasswordStore] Failed to get all passwords:', error);
return {};
}
}
/**
* Remove WiFi password for a specific network
* @param ssid Network SSID
*/
export async function removeWiFiPassword(ssid: string): Promise<void> {
try {
const existing = await getAllWiFiPasswords();
// Remove the password
delete existing[ssid];
// Save back to SecureStore
if (Object.keys(existing).length > 0) {
await SecureStore.setItemAsync(WIFI_PASSWORDS_KEY, JSON.stringify(existing));
} else {
// If no passwords left, remove the key entirely
await SecureStore.deleteItemAsync(WIFI_PASSWORDS_KEY);
}
console.log('[WiFiPasswordStore] Password removed for network:', ssid);
} catch (error) {
console.error('[WiFiPasswordStore] Failed to remove password:', error);
throw error;
}
}
/**
* Clear all saved WiFi passwords
* Should be called on logout
*/
export async function clearAllWiFiPasswords(): Promise<void> {
try {
await SecureStore.deleteItemAsync(WIFI_PASSWORDS_KEY);
console.log('[WiFiPasswordStore] All WiFi passwords cleared');
} catch (error) {
console.error('[WiFiPasswordStore] Failed to clear passwords:', error);
throw error;
}
}
/**
* Migrate WiFi passwords from AsyncStorage to SecureStore
* This function should be called once during app startup to migrate existing data
*/
export async function migrateFromAsyncStorage(): Promise<void> {
try {
const AsyncStorage = require('@react-native-async-storage/async-storage').default;
// Check if migration already done
const existing = await SecureStore.getItemAsync(WIFI_PASSWORDS_KEY);
if (existing) {
console.log('[WiFiPasswordStore] Migration already completed');
return;
}
// Try to get old data from AsyncStorage
const oldPasswords = await AsyncStorage.getItem('WIFI_PASSWORDS');
if (oldPasswords) {
// Migrate to SecureStore
await SecureStore.setItemAsync(WIFI_PASSWORDS_KEY, oldPasswords);
// Remove from AsyncStorage
await AsyncStorage.removeItem('WIFI_PASSWORDS');
await AsyncStorage.removeItem(LEGACY_SINGLE_PASSWORD_KEY);
console.log('[WiFiPasswordStore] Successfully migrated passwords from AsyncStorage');
} else {
console.log('[WiFiPasswordStore] No passwords to migrate');
}
} catch (error) {
console.error('[WiFiPasswordStore] Migration failed:', error);
// Don't throw - migration failure shouldn't break the app
}
}