/* * 생체인식(지문·Face) 로컬 잠금 레이어 — WISE 모바일 패턴 이식. * 레퍼런스: guardia-messenger/app/uiws/_lib/constants/biometric.ts (문자 그대로 차용). * * 성격: 2FA 로그인을 대체하지 않는다 — 이미 로그인된 세션에 대한 "로컬 재인증/잠금" 레이어. * 방어적 동적 require: expo-local-authentication 미설치 기기에서도 graceful(미지원 취급). * OS만 생체 데이터를 다루며, 앱은 성공 불린(boolean)만 받는다(생체 원본 미보관). * pref 키는 kintex 전용 접두어로 분리(WISE `wise_*`와 격리 — CLAUDE.md 토큰키 분리 원칙). */ import AsyncStorage from '@react-native-async-storage/async-storage'; // eslint-disable-next-line @typescript-eslint/no-explicit-any let LocalAuth: any = null; try { // eslint-disable-next-line @typescript-eslint/no-var-requires LocalAuth = require('expo-local-authentication'); } catch { LocalAuth = null; } const PREF_KEY = 'kintex.biometric'; // '1' | '0' /** 기기가 생체인증 하드웨어 + OS 등록(지문/Face)을 모두 지원하는지. */ export async function isBiometricSupported(): Promise { if (!LocalAuth) return false; try { const hasHw = await LocalAuth.hasHardwareAsync(); const enrolled = await LocalAuth.isEnrolledAsync(); return !!(hasHw && enrolled); } catch { return false; } } /** 하드웨어만 보유(OS 미등록 포함) — "생체 등록 유도" 안내 노출 판정용. */ export async function isBiometricHardwareAvailable(): Promise { if (!LocalAuth) return false; try { return !!(await LocalAuth.hasHardwareAsync()); } catch { return false; } } /* * 세션(콜드 스타트) 단위 잠금 통과 플래그 — 모듈 전역. * 콜드 스타트(JS 번들 재로드) 시 자연 초기화 → 재실행마다 잠금 1회. * 포커스마다 반복 프롬프트를 막고, 인증 직후 무한 재프롬프트를 방지한다. */ let sessionUnlocked = false; export function isSessionUnlocked(): boolean { return sessionUnlocked; } export function markSessionUnlocked(): void { sessionUnlocked = true; } export function resetSessionUnlocked(): void { sessionUnlocked = false; } /** 사용자 토글(pref) 조회 — 기본 비활성('0'/미설정). */ export async function isBiometricEnabled(): Promise { try { return (await AsyncStorage.getItem(PREF_KEY)) === '1'; } catch { return false; } } /** 사용자 토글(pref) 설정. */ export async function setBiometricEnabled(on: boolean): Promise { try { await AsyncStorage.setItem(PREF_KEY, on ? '1' : '0'); } catch { /* graceful */ } } /** 생체 인증 프롬프트. 성공 시 true. 미지원/취소/실패 시 false(생체 원본 미노출). */ export async function authenticateBiometric(): Promise { if (!LocalAuth) return false; try { const res = await LocalAuth.authenticateAsync({ promptMessage: 'KINTEX 생체인식 잠금 해제', cancelLabel: '취소', disableDeviceFallback: false, }); return !!res?.success; } catch { return false; } }