From f8019b3177985f78ada733e2cbd6babed5a8e77c Mon Sep 17 00:00:00 2001 From: minibits-cash Date: Wed, 28 Jan 2026 14:03:53 +0100 Subject: [PATCH] Refactor and align App and POS auth logic --- src/App.tsx | 23 +++-------- src/screens/DeveloperScreen.tsx | 2 +- src/services/keyChain.ts | 68 ++++++++++++++++++++++++++++++++- 3 files changed, 74 insertions(+), 19 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index fa478c99..8241d091 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -52,23 +52,12 @@ function App() { // User authentication (biometrics / PIN) if (userSettingsStore.isAuthOn) { - KeyChain.getOrCreateAuthToken(userSettingsStore.isAuthOn) - .then((authToken) => { - setIsUserAuthenticated(true) - log.trace('[App]', { authToken }) - }) - .catch((e: any) => { - log.warn('[App]', 'User authentication failed', { message: e.message }) - - if (e && typeof e === 'object') { - const errString = JSON.stringify(e) - const isBackPressed = errString.includes('code: 10') - const isCancellPressed = errString.includes('code: 13') - const isIOSCancel = 'code' in e && String(e.code) === '-128' - - if (isCancellPressed || isBackPressed || isIOSCancel) { - RNExitApp.exitApp() - } + KeyChain.authenticateOnAppStart(userSettingsStore.isAuthOn) + .then((result) => { + if (result.success) { + setIsUserAuthenticated(true) + } else if (result.shouldExitApp) { + RNExitApp.exitApp() } }) } else { diff --git a/src/screens/DeveloperScreen.tsx b/src/screens/DeveloperScreen.tsx index 1d0bda61..91591a03 100644 --- a/src/screens/DeveloperScreen.tsx +++ b/src/screens/DeveloperScreen.tsx @@ -34,7 +34,7 @@ import RNExitApp from 'react-native-exit-app' import { TransactionStatus } from '../models/Transaction' import { maxTransactionsInHistory } from '../models/TransactionsStore' import { StaticScreenProps, useNavigation } from '@react-navigation/native' -// +// refresh type Props = StaticScreenProps diff --git a/src/services/keyChain.ts b/src/services/keyChain.ts index ff4c57e2..acce8f6d 100644 --- a/src/services/keyChain.ts +++ b/src/services/keyChain.ts @@ -386,6 +386,53 @@ const removeAuthToken = async function (): Promise { +export type AuthResult = { + success: boolean + shouldExitApp: boolean +} + + +/** + * Authenticate user on app start + * Creates auth token on first use, then requires biometric authentication + * Returns result object with success status and whether app should exit + */ +const authenticateOnAppStart = async function (isAuthOn: boolean): Promise { + try { + const authToken = await getOrCreateAuthToken(isAuthOn) + + if (authToken) { + log.trace('[authenticateOnAppStart]', 'App start authentication successful') + return { success: true, shouldExitApp: false } + } + + log.trace('[authenticateOnAppStart]', 'No auth token found') + return { success: false, shouldExitApp: false } + } catch (e: any) { + // Handle specific error codes for user actions + if (e && typeof e === 'object') { + const errString = JSON.stringify(e) + const isBackPressed = errString.includes('code: 10') + const isCancelPressed = errString.includes('code: 13') + const isIOSCancel = 'code' in e && String(e.code) === '-128' + + if (isBackPressed) { + log.trace('[authenticateOnAppStart]', 'User pressed back button') + return { success: false, shouldExitApp: true } + } + + if (isCancelPressed || isIOSCancel) { + log.trace('[authenticateOnAppStart]', 'User cancelled authentication') + return { success: false, shouldExitApp: true } + } + } + + log.warn('[authenticateOnAppStart]', 'Authentication failed', { message: e.message }) + return { success: false, shouldExitApp: false } + } +} + + async function updateAuthSettings(isAuthOn: boolean) { log.trace('[updateAuthSettings] to', {isAuthOn}) @@ -426,7 +473,25 @@ const authenticatePOSMode = async function (): Promise { log.trace('[authenticatePOSMode]', 'No auth token found') return false } catch (e: any) { - log.trace('[authenticatePOSMode]', 'Authentication cancelled or failed', { message: e.message }) + // Handle specific error codes for user actions + if (e && typeof e === 'object') { + const errString = JSON.stringify(e) + const isBackPressed = errString.includes('code: 10') + const isCancelPressed = errString.includes('code: 13') + const isIOSCancel = 'code' in e && String(e.code) === '-128' + + if (isBackPressed) { + log.trace('[authenticatePOSMode]', 'User pressed back button') + return false + } + + if (isCancelPressed || isIOSCancel) { + log.trace('[authenticatePOSMode]', 'User cancelled authentication') + return false + } + } + + log.trace('[authenticatePOSMode]', 'Authentication failed', { message: e.message }) return false } } @@ -526,6 +591,7 @@ export const KeyChain = { getOrCreateAuthToken, removeAuthToken, updateAuthSettings, + authenticateOnAppStart, authenticatePOSMode, // server JWT tokens