import { safeStorage } from 'electron'; import { getStore } from '../store'; /** * Token storage with three-tier fallback: * 1. OS keychain via keytar (best — encrypted, per-user) * 2. Electron safeStorage + electron-store (encrypted at rest) * 3. Plain electron-store (last resort — e.g. WSL with no keyring) */ let keytar: typeof import('keytar') | null = null; let keytarFailed = false; try { // eslint-disable-next-line @typescript-eslint/no-require-imports keytar = require('keytar') as typeof import('keytar'); } catch { keytarFailed = true; console.log('[Token] keytar native module unavailable'); } function useKeytar(): boolean { return keytar !== null && !keytarFailed; } function canUseSafeStorage(): boolean { try { return safeStorage.isEncryptionAvailable(); } catch { return false; } } const SERVICE_NAME = 'adiuva'; // --- electron-store helpers (with optional safeStorage encryption) --- function readFromStore(providerName: string): string | null { const tokens = getStore().get('encryptedTokens'); const stored = tokens[providerName]; if (!stored) return null; if (canUseSafeStorage()) { try { return safeStorage.decryptString(Buffer.from(stored, 'base64')); } catch { // Stored value might be plaintext from a previous fallback return stored; } } // No encryption available — value is stored as plaintext return stored; } function writeToStore(providerName: string, token: string): void { let value: string; if (canUseSafeStorage()) { value = safeStorage.encryptString(token).toString('base64'); } else { // Last resort: store plaintext (WSL with no keyring) value = token; } const tokens = getStore().get('encryptedTokens'); getStore().set('encryptedTokens', { ...tokens, [providerName]: value }); } function removeFromStore(providerName: string): void { const tokens = getStore().get('encryptedTokens'); const { [providerName]: _, ...rest } = tokens; getStore().set('encryptedTokens', rest); } // --- public API --- /** Read a stored token for the given provider. */ export async function getToken(providerName: string): Promise { if (useKeytar()) { try { return await keytar!.getPassword(SERVICE_NAME, providerName); } catch (err) { console.log('[Token] keytar runtime error, falling back:', (err as Error).message); keytarFailed = true; } } return readFromStore(providerName); } /** Store a token for the given provider. */ export async function setToken(providerName: string, token: string): Promise { if (useKeytar()) { try { await keytar!.setPassword(SERVICE_NAME, providerName, token); return; } catch (err) { console.log('[Token] keytar runtime error, falling back:', (err as Error).message); keytarFailed = true; } } writeToStore(providerName, token); } /** Delete a stored token for the given provider. */ async function deleteToken(providerName: string): Promise { if (useKeytar()) { try { return await keytar!.deletePassword(SERVICE_NAME, providerName); } catch (err) { console.log('[Token] keytar runtime error, falling back:', (err as Error).message); keytarFailed = true; } } removeFromStore(providerName); return true; }