mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-04-04 23:05:19 -04:00
144 lines
4.8 KiB
TypeScript
144 lines
4.8 KiB
TypeScript
import NativeVaultManager from '@/specs/NativeVaultManager';
|
|
|
|
export type AuthMethod = 'faceid' | 'password';
|
|
|
|
export type UnlockResult = {
|
|
success: boolean;
|
|
error?: string;
|
|
redirectToUnlock?: boolean;
|
|
};
|
|
|
|
/**
|
|
* Centralized vault unlock logic that handles both biometric and PIN unlock.
|
|
* This utility is used by initialize.tsx, reinitialize.tsx, and unlock.tsx to avoid code duplication.
|
|
*/
|
|
export class VaultUnlockHelper {
|
|
/**
|
|
* Attempt to unlock the vault using available authentication methods.
|
|
* Priority: Biometric -> PIN -> Manual unlock
|
|
* If biometric fails/is cancelled and PIN is enabled, automatically falls back to PIN.
|
|
*
|
|
* @param params Configuration for unlock attempt
|
|
* @returns Promise<UnlockResult> indicating success/failure and any actions needed
|
|
*/
|
|
static async attemptAutomaticUnlock(params: {
|
|
enabledAuthMethods: AuthMethod[];
|
|
unlockVault: () => Promise<boolean>; // dbContext.unlockVault for biometric
|
|
}): Promise<UnlockResult> {
|
|
const { enabledAuthMethods, unlockVault } = params;
|
|
|
|
// Check which authentication methods are available
|
|
const isFaceIDEnabled = enabledAuthMethods.includes('faceid');
|
|
const isPinEnabled = await NativeVaultManager.isPinEnabled();
|
|
|
|
// Try biometric unlock first (Face ID / Touch ID)
|
|
if (isFaceIDEnabled) {
|
|
try {
|
|
const isUnlocked = await unlockVault();
|
|
if (isUnlocked) {
|
|
return { success: true };
|
|
}
|
|
// Biometric failed - fall through to PIN fallback below
|
|
console.log('Biometric unlock returned false, trying PIN fallback if available');
|
|
} catch (error) {
|
|
// Biometric error - fall through to PIN fallback below
|
|
console.error('Biometric unlock error:', error);
|
|
}
|
|
|
|
// Biometric failed or was cancelled - try PIN fallback if available
|
|
if (isPinEnabled) {
|
|
return this.attemptPinUnlock();
|
|
}
|
|
|
|
// No PIN fallback available
|
|
return {
|
|
success: false,
|
|
error: 'Biometric unlock failed',
|
|
redirectToUnlock: true,
|
|
};
|
|
}
|
|
|
|
// Biometric not enabled - try PIN unlock directly
|
|
if (isPinEnabled) {
|
|
return this.attemptPinUnlock();
|
|
}
|
|
|
|
// No automatic unlock method available - manual unlock required
|
|
return {
|
|
success: false,
|
|
error: 'No automatic unlock method available',
|
|
redirectToUnlock: true,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Attempt PIN unlock.
|
|
* @returns Promise<UnlockResult> indicating success/failure
|
|
*/
|
|
private static async attemptPinUnlock(): Promise<UnlockResult> {
|
|
try {
|
|
await NativeVaultManager.showPinUnlock();
|
|
|
|
// Verify vault is now unlocked
|
|
const isNowUnlocked = await NativeVaultManager.isVaultUnlocked();
|
|
if (!isNowUnlocked) {
|
|
return {
|
|
success: false,
|
|
error: 'PIN unlock failed',
|
|
redirectToUnlock: true,
|
|
};
|
|
}
|
|
return { success: true };
|
|
} catch (error) {
|
|
// User cancelled or PIN unlock failed
|
|
const errorMessage = error instanceof Error ? error.message : 'PIN unlock failed or cancelled';
|
|
if (!errorMessage.includes('cancelled') && !errorMessage.includes('canceled')) {
|
|
console.error('PIN unlock error:', error);
|
|
}
|
|
return {
|
|
success: false,
|
|
error: errorMessage,
|
|
redirectToUnlock: true,
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Authenticate user for a specific action (e.g., mobile unlock confirmation).
|
|
* Uses the native authenticateUser which automatically detects and uses the appropriate method.
|
|
*
|
|
* @param title Authentication prompt title
|
|
* @param subtitle Authentication prompt subtitle
|
|
* @param allowedMethods Optional array of allowed auth methods ('biometric', 'pin', 'password'). If null, all enabled methods are allowed.
|
|
* @param buttonText Optional custom text for the unlock/confirm button. If null, defaults to "Unlock".
|
|
* @returns Promise<boolean> indicating if authentication succeeded
|
|
*/
|
|
static async authenticateForAction(
|
|
title: string,
|
|
subtitle: string,
|
|
allowedMethods: string[] | null = null,
|
|
buttonText: string | null = null
|
|
): Promise<boolean> {
|
|
try {
|
|
const authenticated = await NativeVaultManager.authenticateUser(title, subtitle, allowedMethods, buttonText);
|
|
return authenticated;
|
|
} catch (error) {
|
|
console.error('Authentication error:', error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if any automatic unlock method is available.
|
|
* @param enabledAuthMethods The enabled authentication methods
|
|
* @returns Promise<boolean> indicating if automatic unlock is possible
|
|
*/
|
|
static async hasAutomaticUnlockMethod(
|
|
enabledAuthMethods: AuthMethod[]
|
|
): Promise<boolean> {
|
|
const isFaceIDEnabled = enabledAuthMethods.includes('faceid');
|
|
const isPinEnabled = await NativeVaultManager.isPinEnabled();
|
|
return isFaceIDEnabled || isPinEnabled;
|
|
}
|
|
}
|