diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c11575..b901d9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- Centralized Authentication System with Advanced Security + - Implemented new AuthService for unified authentication management + - Added support for multiple authentication methods (private key, external signer, ephemeral) + - Created secure logout protocol to prevent unexpected state during sign-out + - Implemented SigningQueue for better transaction handling and atomicity + - Added AuthStateManager for centralized state management + - Created AuthProvider component for React integration + - Implemented feature flag system for gradual rollout + - Added test page for verification of authentication features + - Enhanced security with proper error propagation and state handling + - Created clear documentation for the new authentication architecture + - Built with TypeScript for type safety and developer experience + - Added backward compatibility with legacy authentication +g - Enhanced Avatar System with Robohash Integration - Consolidated avatar implementation into ui/avatar.tsx component - Added RobohashAvatar and RobohashFallback components diff --git a/app/(tabs)/library/_layout.tsx b/app/(tabs)/library/_layout.tsx index 60b8a17..e95bab8 100644 --- a/app/(tabs)/library/_layout.tsx +++ b/app/(tabs)/library/_layout.tsx @@ -53,14 +53,16 @@ export default function LibraryLayout() { component={TemplatesScreen} options={{ title: 'Templates' }} /> - {/* Only show Programs tab in development builds */} + {/* Only show Development tab in development builds */} {!IS_PRODUCTION && ( )} + + {/* Auth Test tab temporarily removed - see auth information in Development tab instead */} ); diff --git a/app/(tabs)/library/programs.tsx b/app/(tabs)/library/programs.tsx index 76517cc..4c7f68c 100644 --- a/app/(tabs)/library/programs.tsx +++ b/app/(tabs)/library/programs.tsx @@ -497,9 +497,56 @@ export default function ProgramsScreen() { Nostr + + setActiveTab('auth')} + > + + Auth + {/* Tab Content */} + {activeTab === 'auth' && ( + + + Authentication System Test + + + + + + Auth Test Available + + + + + The Centralized Authentication System has been implemented and is ready for testing. + + + You can access the standalone test page by adding the AuthProvider to the root app layout, which + is not included in this development tab to avoid conflicts with the existing authentication system. + + + The new authentication system includes: + + + • Support for multiple authentication methods + • Secure logout protocol with proper state handling + • SigningQueue for atomic Nostr event signing + • Feature flag system for controlled rollout + • Type-safe interfaces with improved error handling + + + The standalone test page (app/test/auth-test.tsx) can be accessed directly when the AuthProvider + is enabled in the app's root layout. + + + + + + )} {activeTab === 'database' && ( @@ -895,4 +942,4 @@ export default function ProgramsScreen() { )} ); -} \ No newline at end of file +} diff --git a/app/_layout.tsx b/app/_layout.tsx index c18cf61..d808bba 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -17,9 +17,10 @@ import { SettingsDrawerProvider } from '@/lib/contexts/SettingsDrawerContext'; import SettingsDrawer from '@/components/SettingsDrawer'; import RelayInitializer from '@/components/RelayInitializer'; import OfflineIndicator from '@/components/OfflineIndicator'; -import { useNDKStore } from '@/lib/stores/ndk'; +import { useNDKStore, FLAGS } from '@/lib/stores/ndk'; import { useWorkoutStore } from '@/stores/workoutStore'; import { ConnectivityService } from '@/lib/db/services/ConnectivityService'; +import { AuthProvider } from '@/lib/auth/AuthProvider'; // Import splash screens with improved fallback mechanism let SplashComponent: React.ComponentType<{onFinish: () => void}>; let useVideoSplash = false; @@ -226,11 +227,29 @@ export default function RootLayout() { {/* Ensure SettingsDrawerProvider wraps everything */} - {/* Add RelayInitializer here - it loads relay data once NDK is available */} - - - {/* Add OfflineIndicator to show network status */} - + {/* Add AuthProvider when using new auth system */} + {(() => { + const ndk = useNDKStore.getState().ndk; + if (ndk && FLAGS.useNewAuthSystem) { + return ( + + {/* Add RelayInitializer here - it loads relay data once NDK is available */} + + + {/* Add OfflineIndicator to show network status */} + + + ); + } else { + return ( + <> + {/* Legacy approach without AuthProvider */} + + + + ); + } + })()} diff --git a/app/test/auth-test.tsx b/app/test/auth-test.tsx new file mode 100644 index 0000000..7344e9d --- /dev/null +++ b/app/test/auth-test.tsx @@ -0,0 +1,226 @@ +import React from 'react'; +import { View, StyleSheet, ScrollView, Button, Text, Platform } from 'react-native'; +import { useAuthState, useAuth } from '@/lib/auth/AuthProvider'; +import AuthStatus from '@/components/auth/AuthStatus'; +import { StatusBar } from 'expo-status-bar'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { Stack } from 'expo-router'; + +/** + * Test page for the new authentication system + */ +export default function AuthTestPage() { + const { top, bottom } = useSafeAreaInsets(); + const authState = useAuthState(); + const { authService } = useAuth(); + const [privateKey, setPrivateKey] = React.useState(''); + + // Login with private key + const handleLoginWithPrivateKey = async () => { + try { + // For testing, just use a generated key or a newly generated one + if (privateKey) { + await authService.loginWithPrivateKey(privateKey); + } else { + await authService.createEphemeralKey(); + } + } catch (error) { + console.error("Login error:", error); + } + }; + + // Create ephemeral key + const handleCreateEphemeralKey = async () => { + try { + await authService.createEphemeralKey(); + } catch (error) { + console.error("Ephemeral key error:", error); + } + }; + + // Generate signing operations for testing + const handleSimulateSigningOperations = async () => { + // We can only test this if we're authenticated + if (authState.status !== 'authenticated') { + console.log("Can't simulate signing operations when not authenticated"); + return; + } + + // Simulate signing 3 operations with delays + for (let i = 0; i < 3; i++) { + // Create a minimal NostrEvent for testing + const event = { + id: `event-${i}`, + pubkey: authState.user.pubkey, + content: `Test event ${i}`, + kind: 1, + created_at: Math.floor(Date.now() / 1000), + tags: [], + sig: '', + }; + + // Create a proper SigningOperation + const operation = { + event: event as any, // Type assertion to satisfy NostrEvent requirement + timestamp: Date.now(), + resolve: () => {}, + reject: () => {}, + }; + + // Start signing + authState.setSigningInProgress(true, operation); + + // After 1 second, complete the operation + setTimeout(() => { + authState.setSigningInProgress(false, operation); + }, 1000 * (i + 1)); + } + }; + + return ( + + + + + + Authentication System Test + + {/* Current authentication status */} + + Current Status + + + + {/* Authentication actions */} + + Authentication Actions + + +