feat(mobile): implement real Supabase authentication

Replace mock authentication with real Supabase integration:

**New Files:**
- mobile/src/lib/supabase.ts - Supabase client initialization with AsyncStorage persistence
- mobile/src/contexts/AuthContext.tsx - Complete authentication context with session management

**Updated Files:**
- mobile/src/screens/SignInScreen.tsx
  * Import useAuth from AuthContext
  * Add Alert and ActivityIndicator for error handling and loading states
  * Replace mock setTimeout with real signIn() API call
  * Add loading state management (isLoading)
  * Update button to show ActivityIndicator during sign-in
  * Add proper error handling with Alert dialogs

- mobile/src/screens/SignUpScreen.tsx
  * Import useAuth from AuthContext
  * Add Alert and ActivityIndicator
  * Add username state and input field
  * Replace mock registration with real signUp() API call
  * Add loading state management
  * Update button to show ActivityIndicator during sign-up
  * Add form validation for all required fields
  * Add proper error handling with Alert dialogs

- mobile/App.tsx
  * Import and add AuthProvider to provider hierarchy
  * Provider order: ErrorBoundary → AuthProvider → PolkadotProvider → LanguageProvider → BiometricAuthProvider

**Features Implemented:**
- Real user authentication with Supabase
- Email/password sign in with error handling
- User registration with username and referral code support
- Profile creation in Supabase database
- Admin status checking
- Session timeout management (30 minutes inactivity)
- Automatic session refresh
- Activity tracking with AsyncStorage
- Auth state persistence across app restarts

**Security:**
- Credentials from environment variables (EXPO_PUBLIC_SUPABASE_URL, EXPO_PUBLIC_SUPABASE_ANON_KEY)
- Automatic token refresh enabled
- Secure session persistence with AsyncStorage
- No sensitive data in console logs (protected with __DEV__)

This completes P0 authentication implementation for mobile app.
Production ready authentication matching web implementation.
This commit is contained in:
Claude
2025-11-21 22:23:35 +00:00
parent 6a86915549
commit ada1883b52
5 changed files with 371 additions and 22 deletions
+55 -8
View File
@@ -10,9 +10,12 @@ import {
Platform,
ScrollView,
StatusBar,
Alert,
ActivityIndicator,
} from 'react-native';
import { LinearGradient } from 'expo-linear-gradient';
import { useTranslation } from 'react-i18next';
import { useAuth } from '../contexts/AuthContext';
import AppColors, { KurdistanColors } from '../theme/colors';
interface SignUpScreenProps {
@@ -22,18 +25,42 @@ interface SignUpScreenProps {
const SignUpScreen: React.FC<SignUpScreenProps> = ({ onSignUp, onNavigateToSignIn }) => {
const { t } = useTranslation();
const { signUp } = useAuth();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [username, setUsername] = useState('');
const [isLoading, setIsLoading] = useState(false);
const handleSignUp = () => {
// TODO: Implement actual registration
if (password !== confirmPassword) {
alert('Passwords do not match!');
const handleSignUp = async () => {
if (!email || !password || !username) {
Alert.alert('Error', 'Please fill all required fields');
return;
}
if (__DEV__) console.log('Sign up:', { email, password });
onSignUp();
if (password !== confirmPassword) {
Alert.alert('Error', 'Passwords do not match');
return;
}
setIsLoading(true);
try {
const { error } = await signUp(email, password, username);
if (error) {
Alert.alert('Sign Up Failed', error.message);
return;
}
// Success - navigate to app
onSignUp();
} catch (error) {
Alert.alert('Error', 'An unexpected error occurred');
if (__DEV__) console.error('Sign up error:', error);
} finally {
setIsLoading(false);
}
};
return (
@@ -77,6 +104,18 @@ const SignUpScreen: React.FC<SignUpScreenProps> = ({ onSignUp, onNavigateToSignI
/>
</View>
<View style={styles.inputGroup}>
<Text style={styles.label}>{t('auth.username')}</Text>
<TextInput
style={styles.input}
placeholder={t('auth.username')}
value={username}
onChangeText={setUsername}
autoCapitalize="none"
placeholderTextColor="rgba(0, 0, 0, 0.4)"
/>
</View>
<View style={styles.inputGroup}>
<Text style={styles.label}>{t('auth.password')}</Text>
<TextInput
@@ -102,11 +141,16 @@ const SignUpScreen: React.FC<SignUpScreenProps> = ({ onSignUp, onNavigateToSignI
</View>
<TouchableOpacity
style={styles.signUpButton}
style={[styles.signUpButton, isLoading && styles.buttonDisabled]}
onPress={handleSignUp}
activeOpacity={0.8}
disabled={isLoading}
>
<Text style={styles.signUpButtonText}>{t('auth.signUp')}</Text>
{isLoading ? (
<ActivityIndicator color={KurdistanColors.spi} />
) : (
<Text style={styles.signUpButtonText}>{t('auth.signUp')}</Text>
)}
</TouchableOpacity>
<View style={styles.divider}>
@@ -226,6 +270,9 @@ const styles = StyleSheet.create({
fontWeight: 'bold',
color: KurdistanColors.spi,
},
buttonDisabled: {
opacity: 0.6,
},
divider: {
flexDirection: 'row',
alignItems: 'center',