- 자체 구현(네이티브 플러그인 없음): PIN은 SHA-256 해시만 저장, 기본 OFF 옵트인 - 앱 시작(저장 세션 재진입)·백그라운드 복귀 시 잠금 화면 노출 - 설정에 앱 잠금 토글 + PIN 설정(입력→확인 2단계) 모달 - 갇힘 방지: 잠금 화면에서 'PIN 분실 → 로그아웃'(잠금 해제 후 로그아웃) - 신규/민감정보 앱 신뢰도 향상 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import { reactive } from 'vue'
|
||||
|
||||
// 앱 잠금(PIN). 자체 구현 — 네이티브 플러그인 없이 동작.
|
||||
// PIN 은 평문 저장하지 않고 SHA-256 해시만 보관. 기본 비활성(옵트인).
|
||||
// 갇힘 방지: 잠금 화면에서 'PIN 분실 → 로그아웃' 으로 disable + 로그아웃 가능.
|
||||
const KEY_ENABLED = 'applock:enabled'
|
||||
const KEY_HASH = 'applock:hash'
|
||||
|
||||
export const PIN_LENGTH = 4
|
||||
|
||||
const state = reactive({
|
||||
enabled: localStorage.getItem(KEY_ENABLED) === '1',
|
||||
locked: false,
|
||||
})
|
||||
|
||||
async function hashPin(pin) {
|
||||
const data = new TextEncoder().encode('dondwaeji-applock:' + pin)
|
||||
const buf = await crypto.subtle.digest('SHA-256', data)
|
||||
return Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, '0')).join('')
|
||||
}
|
||||
|
||||
export const appLock = {
|
||||
state,
|
||||
/** PIN 설정 + 잠금 활성화 */
|
||||
async setPin(pin) {
|
||||
localStorage.setItem(KEY_HASH, await hashPin(pin))
|
||||
localStorage.setItem(KEY_ENABLED, '1')
|
||||
state.enabled = true
|
||||
},
|
||||
/** 입력 PIN 검증 */
|
||||
async verify(pin) {
|
||||
const stored = localStorage.getItem(KEY_HASH)
|
||||
return !!stored && (await hashPin(pin)) === stored
|
||||
},
|
||||
/** 잠금 해제(설정 끄기) */
|
||||
disable() {
|
||||
localStorage.removeItem(KEY_HASH)
|
||||
localStorage.removeItem(KEY_ENABLED)
|
||||
state.enabled = false
|
||||
state.locked = false
|
||||
},
|
||||
/** 잠금 걸기(활성화된 경우에만) */
|
||||
lock() {
|
||||
if (state.enabled) state.locked = true
|
||||
},
|
||||
/** 잠금 풀기(이번 세션) */
|
||||
unlock() {
|
||||
state.locked = false
|
||||
},
|
||||
/** 저장된 세션이 있는지(앱 시작 시 잠금 여부 판단용) */
|
||||
hasSession() {
|
||||
return !!localStorage.getItem('token')
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user