diff --git a/CHANGELOG.md b/CHANGELOG.md index 074b85b..2feb80d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- Authentication persistence debugging tools + - Created dedicated AuthPersistenceTest screen for diagnosing credential issues + - Added comprehensive SecureStore key visualization + - Implemented manual key migration triggering + - Added test key creation functionality for simulating scenarios + - Built key clearing utilities for testing from scratch + - Added interactive testing workflow with detailed instructions + - Enhanced error handling with better messaging + - React Query Android Profile Optimization System - Added platform-specific timeouts for network operations - Created fallback UI system for handling network delays @@ -17,6 +26,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved user experience during temporary API failures ### Improved +- Authentication initialization sequence + - Added proper awaiting of NDK relay connections + - Implemented credential migration before authentication starts + - Enhanced AuthProvider with improved initialization flow + - Added robustness against race conditions during startup + - Implemented proper detection of stored credentials + - Created key migration system between storage locations + - Enhanced app_layout.tsx to ensure proper initialization order + - Added detailed technical documentation for the auth system + - Profile loading performance dramatically enhanced - Added ultra-early content display after just 500ms - Implemented progressive content loading with three-tier system @@ -48,6 +67,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Created comprehensive logging documentation ### Fixed +- Authentication storage key inconsistencies + - Fixed inconsistent key naming between different auth systems + - Implemented consistent SECURE_STORE_KEYS constants + - Created migration utility in secureStorage.ts + - Added key migration from legacy to standardized locations + - Fixed AuthProvider and AuthStateManager to use the same keys + - Enhanced NDK store to use standardized key constants + - Added migration status tracking to prevent duplicate migrations + - Created diagnostic tool for checking credential storage + - Fixed ReactQueryAuthProvider to use the same key constants + - Added detailed documentation in authentication_persistence_debug_guide.md + - Private key authentication persistence - Fixed inconsistent storage key naming between legacy and React Query auth systems - Standardized on 'nostr_privkey' for all private key storage @@ -82,6 +113,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added comprehensive logging for better debugging - Fixed race conditions in authentication state transitions - Implemented initialization tracking to prevent duplicate auth operations + - Added waiting for NDK pool initialization before auth operations + - Created one-time migration system for legacy credentials + - Fixed delayed authentication restoration with improved sequence checks + - Enhanced credential consistency verification at startup + - Added test tools for diagnosing and fixing authentication issues - Android profile screen hanging issues - Fixed infinite loading state on profile screen with proper timeouts @@ -698,51 +734,4 @@ g - Added type safety for complex operations - Improved error handling throughout relay management -# Changelog - March 8, 2025 - -## Added -- Database schema upgrade to version 5 - - Added workouts, workout_exercises, and workout_sets tables - - Added templates and template_exercises tables - - Added publication_queue table for offline-first functionality - - Added app_status table for connectivity tracking -- New database services - - WorkoutService for managing workout data persistence - - Enhanced TemplateService for template management - - NostrWorkoutService for Nostr event conversion - - Updated PublicationQueueService for offline publishing -- React hooks for database access - - useWorkouts hook for workout operations - - useTemplates hook for template operations -- Improved workout completion flow - - Three-tier storage approach (Local Only, Publish Complete, Publish Limited) - - Template modification options (keep original, update, save as new) - - Enhanced social sharing capabilities - - Detailed workout summary with statistics -- Enhanced database debugging tools - - Added proper error handling and logging - - Improved transaction management - - Added connectivity status tracking - -## Fixed -- Missing workout and template table errors -- Incomplete data storage issues -- Template management synchronization -- Nostr event conversion between app models and Nostr protocol -- Workout persistence across app sessions -- Database transaction handling in workout operations -- Template reference handling in workout records - -## Improved -- Workout store persistence layer - - Enhanced integration with database services - - Better error handling for database operations - - Improved Nostr connectivity detection -- Template management workflow - - Proper versioning and attribution - - Enhanced modification tracking - - Better user control over template sharing -- Overall data persistence architecture - - Consistent service-based approach - - Improved type safety - - Enhanced error +# Changelog - March 8, diff --git a/app/_layout.tsx b/app/_layout.tsx index 5914254..7515e53 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -23,6 +23,8 @@ import { useWorkoutStore } from '@/stores/workoutStore'; import { ConnectivityService } from '@/lib/db/services/ConnectivityService'; import { AuthProvider } from '@/lib/auth/AuthProvider'; import { ReactQueryAuthProvider } from '@/lib/auth/ReactQueryAuthProvider'; +import { QueryClientProvider } from '@tanstack/react-query'; +import { createQueryClient } from '@/lib/queryClient'; // Import splash screens with improved fallback mechanism let SplashComponent: React.ComponentType<{onFinish: () => void}>; @@ -115,6 +117,8 @@ export default function RootLayout() { const { colorScheme, isDarkColorScheme } = useColorScheme(); const { init } = useNDKStore(); const initializationPromise = React.useRef | null>(null); + // Create a query client instance that can be used by both auth systems + const queryClient = React.useMemo(() => createQueryClient(), []); // Start app initialization immediately React.useEffect(() => { @@ -135,12 +139,23 @@ export default function RootLayout() { // Start database initialization and NDK initialization in parallel const initPromises = []; - // Initialize NDK with timeout - const ndkPromise = init().catch(error => { - console.error('NDK initialization error:', error); - // Continue even if NDK fails - return { offlineMode: true }; - }); + // Initialize NDK with credentials migration first + const ndkPromise = (async () => { + try { + // Import and run key migration before NDK init + const { migrateKeysIfNeeded } = await import('@/lib/auth/persistence/secureStorage'); + console.log('Running pre-NDK credential migration...'); + await migrateKeysIfNeeded(); + + // Now initialize NDK + console.log('Starting NDK initialization...'); + return await init(); + } catch (error) { + console.error('NDK initialization error:', error); + // Continue even if NDK fails + return { offlineMode: true }; + } + })(); initPromises.push(ndkPromise); // Load favorites from SQLite (local operation) @@ -232,7 +247,7 @@ export default function RootLayout() { {/* Conditionally render authentication providers based on feature flag */} {FLAGS.useReactQueryAuth ? ( /* Use React Query Auth system */ - + {/* React Query specific components */} @@ -253,26 +268,30 @@ export default function RootLayout() { ) : ( - /* Use Legacy Auth system */ - - - - - - - - - - {/* Settings drawer needs to be outside the navigation stack */} - - - - + /* Use Legacy Auth system but still provide QueryClientProvider and NDKContext for data fetching */ + + + + + + + + + + + + {/* Settings drawer needs to be outside the navigation stack */} + + + + + + )} diff --git a/app/test/auth-persistence-test.tsx b/app/test/auth-persistence-test.tsx new file mode 100644 index 0000000..b596eba --- /dev/null +++ b/app/test/auth-persistence-test.tsx @@ -0,0 +1,271 @@ +import React, { useState, useEffect } from 'react'; +import { View, Text, StyleSheet, Button, ScrollView, Platform, TouchableOpacity } from 'react-native'; +import { Stack, useRouter } from 'expo-router'; +import { X } from 'lucide-react-native'; +import * as SecureStore from 'expo-secure-store'; +import { SECURE_STORE_KEYS } from '@/lib/auth/constants'; +import { useAuthStore } from '@/lib/auth/AuthStateManager'; +import { useNDKCurrentUser } from '@/lib/hooks/useNDK'; +import { migrateKeysIfNeeded, resetMigration } from '@/lib/auth/persistence/secureStorage'; + +/** + * Test screen for debugging authentication persistence issues + * This component shows the current state of authentication and stored credentials + */ +export default function AuthPersistenceTest() { + const [storageInfo, setStorageInfo] = useState({ + standardPrivateKey: 'Checking...', + legacyPrivateKey: 'Checking...', + ndkStoreKey: 'Checking...', + pubkey: 'Checking...', + externalSigner: 'Checking...', + migrationStatus: 'Checking...' + }); + + // Get auth states from both stores (legacy and React Query) + const authState = useAuthStore((state) => state); + const { isAuthenticated, currentUser } = useNDKCurrentUser(); + const router = useRouter(); + + const checkStorage = async () => { + try { + // Check all possible storage keys + const standardPrivateKey = await SecureStore.getItemAsync(SECURE_STORE_KEYS.PRIVATE_KEY); + const legacyPrivateKey = await SecureStore.getItemAsync('powr.private_key'); + const ndkStoreKey = await SecureStore.getItemAsync('nostr_privkey'); + const pubkey = await SecureStore.getItemAsync(SECURE_STORE_KEYS.PUBKEY); + const externalSigner = await SecureStore.getItemAsync(SECURE_STORE_KEYS.EXTERNAL_SIGNER); + const migrationStatus = await SecureStore.getItemAsync('auth_migration_v1_completed'); + + // Update state with results + setStorageInfo({ + standardPrivateKey: standardPrivateKey + ? `Found (${standardPrivateKey.length} chars)` + : 'Not found', + legacyPrivateKey: legacyPrivateKey + ? `Found (${legacyPrivateKey.length} chars)` + : 'Not found', + ndkStoreKey: ndkStoreKey + ? `Found (${ndkStoreKey.length} chars)` + : 'Not found', + pubkey: pubkey + ? `Found (${pubkey.substring(0, 8)}...)` + : 'Not found', + externalSigner: externalSigner + ? 'Found' + : 'Not found', + migrationStatus: migrationStatus === 'true' + ? 'Completed' + : 'Not run' + }); + } catch (error: any) { + const errorMsg = error?.message || 'Unknown error'; + console.error('Error checking storage:', errorMsg); + setStorageInfo({ + standardPrivateKey: `Error: ${errorMsg}`, + legacyPrivateKey: `Error: ${errorMsg}`, + ndkStoreKey: `Error: ${errorMsg}`, + pubkey: `Error: ${errorMsg}`, + externalSigner: `Error: ${errorMsg}`, + migrationStatus: `Error: ${errorMsg}` + }); + } + }; + + // Check storage when component mounts + useEffect(() => { + checkStorage(); + }, []); + + return ( + <> + Platform.OS === 'ios' ? ( + router.back()} style={{ marginLeft: 15 }}> + + + ) : undefined, + headerRight: () => Platform.OS !== 'ios' ? ( + router.back()} style={{ marginRight: 15 }}> + + + ) : undefined, + }} + /> + + + Auth Persistence Debugger + + + Zustand Auth State + Status: {authState.status} + {authState.status === 'authenticated' && ( + <> + User: {authState.user?.pubkey?.substring(0, 8)}... + Method: {authState.method || 'Unknown'} + + )} + + + + React Query Auth State + Status: {isAuthenticated ? 'authenticated' : 'unauthenticated'} + {isAuthenticated && currentUser?.pubkey && ( + User: {currentUser.pubkey.substring(0, 8)}... + )} + User Profile: {currentUser?.profile?.name || 'Not loaded'} + + + + Secure Storage Status + Standard Key ({SECURE_STORE_KEYS.PRIVATE_KEY}): + {storageInfo.standardPrivateKey} + + Legacy Key (powr.private_key): + {storageInfo.legacyPrivateKey} + + NDK Store Key (nostr_privkey): + {storageInfo.ndkStoreKey} + + Public Key: + {storageInfo.pubkey} + + External Signer: + {storageInfo.externalSigner} + + Migration Status: + {storageInfo.migrationStatus} + + + +