From d22248425e6c6bde8c48c65d978df592e219e184 Mon Sep 17 00:00:00 2001 From: Chandresh Kerkar Date: Sat, 20 Dec 2025 20:22:45 +0530 Subject: [PATCH] Updated With Dev Login and Deploy md --- AUTH_ROUTING_FIX.md | 285 -------------------------------------- AUTO_LOGIN_FIX.md | 161 --------------------- SIGNIN_REDIRECT_FIX.md | 139 ------------------- SIGNUP_DUPLICATE_CHECK.md | 128 ----------------- SIGNUP_FIX_SUMMARY.md | 119 ---------------- START_ROUTE_FIX.md | 124 ----------------- 6 files changed, 956 deletions(-) delete mode 100644 AUTH_ROUTING_FIX.md delete mode 100644 AUTO_LOGIN_FIX.md delete mode 100644 SIGNIN_REDIRECT_FIX.md delete mode 100644 SIGNUP_DUPLICATE_CHECK.md delete mode 100644 SIGNUP_FIX_SUMMARY.md delete mode 100644 START_ROUTE_FIX.md diff --git a/AUTH_ROUTING_FIX.md b/AUTH_ROUTING_FIX.md deleted file mode 100644 index db3dd22..0000000 --- a/AUTH_ROUTING_FIX.md +++ /dev/null @@ -1,285 +0,0 @@ -# Authentication & Routing Fix - Security Expert Review - -## Critical Issues Found & Fixed - -### 🔴 **CRITICAL ISSUE #1: JWT Token Missing token_version** - -**Problem**: -- Access tokens were not including `token_version` in the JWT payload -- Backend middleware checks for `token_version` and rejects tokens if it doesn't match -- This would cause valid tokens to be rejected, forcing users to re-login - -**Security Impact**: -- Users would be logged out unexpectedly -- Global logout functionality wouldn't work properly -- Token invalidation on logout-all-devices wouldn't work - -**Fix Applied**: -```javascript -// tokenService.js - signAccessToken() -const payload = { - sub: user.id, - phone_number: user.phone_number, - role: user.role, - user_type: user.user_type || null, - token_version: user.token_version || 1, // ✅ ADDED -}; -``` - -**File**: `farm-auth-service/src/services/tokenService.js` - ---- - -### 🔴 **CRITICAL ISSUE #2: Start Route Always Shows Landing Screen** - -**Problem**: -- App always started with `startDestination = Graph.AUTH` (landing screen) -- Even when user was already logged in, they would see landing screen briefly -- Navigation to MAIN graph only happened after async auth check completed -- Caused poor UX and made it seem like user wasn't logged in - -**Root Cause**: -- Race condition between: - 1. NavHost creation with fixed startDestination - 2. MainViewModel async auth check - 3. LaunchedEffect reacting to auth state change - -**Fix Applied**: -```kotlin -// AppNavigation.kt -val startDestination = remember(authState) { - when (authState) { - is AuthState.Authenticated -> Graph.MAIN // ✅ Start at MAIN if logged in - is AuthState.Unauthenticated -> Graph.AUTH - is AuthState.Unknown -> Graph.AUTH - } -} -``` - -**File**: `LivingAi_Lg/app/src/main/java/com/example/livingai_lg/ui/navigation/AppNavigation.kt` - ---- - -### 🔴 **CRITICAL ISSUE #3: Network Errors Setting Unauthenticated State** - -**Problem**: -- Network errors during token validation were setting `authState = Unauthenticated` -- This triggered navigation to AUTH graph even though tokens were still valid -- User would see landing screen even when logged in (just offline) - -**Security Impact**: -- Tokens were not being cleared (good) -- But navigation was changing (bad UX) -- User appeared logged out when they weren't - -**Fix Applied**: -```kotlin -// MainViewModel.kt -if (isNetworkError) { - // Don't change auth state - keep it as Unknown - // Don't clear tokens - they might still be valid - _userState.value = UserState.Error("Network error...") - return@launch -} -``` - -**File**: `LivingAi_Lg/app/src/main/java/com/example/livingai_lg/ui/MainViewModel.kt` - ---- - -### 🔴 **CRITICAL ISSUE #4: Slow Initial Auth Check** - -**Problem**: -- MainViewModel.init() started async check -- AuthState started as `Unknown` -- NavHost created before auth check completed -- Caused delay in showing correct screen - -**Fix Applied**: -```kotlin -// MainViewModel.kt - init block -val hasTokens = tokenManager.getAccessToken() != null && - tokenManager.getRefreshToken() != null -if (hasTokens) { - checkAuthStatus() // Async validation -} else { - _authState.value = AuthState.Unauthenticated // Immediate -} -``` - -**File**: `LivingAi_Lg/app/src/main/java/com/example/livingai_lg/ui/MainViewModel.kt` - ---- - -## Security Audit Results - -### ✅ **JWT & Refresh Token Implementation** - -#### Backend (Node.js) -1. ✅ **Token Signing**: Includes all required claims (sub, phone_number, role, user_type, token_version) -2. ✅ **Token Validation**: Properly validates signature, expiry, and token_version -3. ✅ **Refresh Token Rotation**: Tokens rotate on each refresh -4. ✅ **Token Reuse Detection**: Detects and prevents replay attacks -5. ✅ **Device Binding**: Tokens bound to device_id -6. ✅ **Token Hashing**: Refresh tokens hashed with bcrypt -7. ✅ **Idle Timeout**: 3 days of inactivity expires refresh tokens -8. ✅ **Global Logout**: Token versioning enables logout-all-devices - -#### Frontend (Android) -1. ✅ **Secure Storage**: EncryptedSharedPreferences with AES256_GCM -2. ✅ **Auto-Refresh**: Ktor Auth plugin handles 401 responses -3. ✅ **Token Persistence**: Tokens saved synchronously (commit) -4. ✅ **Error Handling**: Distinguishes network vs auth errors -5. ✅ **State Management**: Proper auth state flow - -### ⚠️ **Issues Fixed** - -1. ✅ JWT payload now includes `token_version` -2. ✅ Start route is dynamic based on auth state -3. ✅ Network errors don't change auth state -4. ✅ Faster initial auth check (synchronous token check) - ---- - -## How It Works Now - -### App Startup Flow - -1. **MainActivity.onCreate()** - - Creates MainViewModel - - MainViewModel.init() checks tokens synchronously - - Sets authState immediately (Authenticated/Unauthenticated) or Unknown - -2. **AppNavigation** - - Reads authState - - Determines startDestination: - - `Authenticated` → `Graph.MAIN` (Buy Animals screen) - - `Unauthenticated` → `Graph.AUTH` (Landing screen) - - `Unknown` → `Graph.AUTH` (Landing screen, while checking) - -3. **Token Validation** (if tokens exist) - - Async validation in background - - If valid → authState = Authenticated → Navigate to MAIN - - If invalid → authState = Unauthenticated → Stay on AUTH - - If network error → authState stays Unknown → Show error, keep tokens - -### User Experience - -#### ✅ **Logged In User** -- App opens → Immediately shows MAIN graph (Buy Animals) -- No flash of landing screen -- Smooth experience - -#### ✅ **First Time User** -- App opens → Shows Landing screen -- Can sign up or sign in -- After login → Navigates to MAIN graph - -#### ✅ **Offline User (with valid tokens)** -- App opens → Shows MAIN graph -- If API call fails → Shows error message -- Tokens preserved, user stays logged in -- When online → Automatically works - -#### ✅ **Expired Tokens** -- App opens → Shows Landing screen -- User needs to sign in again -- Tokens cleared automatically - ---- - -## Testing Checklist - -### ✅ Test Scenarios - -1. **Fresh Install** - - [ ] App opens to Landing screen - - [ ] Can sign up - - [ ] After signup, navigates to MAIN graph - - [ ] Tokens saved - -2. **Logged In User - Normal Reopen** - - [ ] App opens directly to MAIN graph - - [ ] No landing screen flash - - [ ] User stays logged in - -3. **Logged In User - Offline** - - [ ] App opens to MAIN graph - - [ ] Network error shown but user stays logged in - - [ ] When online, works normally - -4. **Expired Refresh Token** - - [ ] App opens to Landing screen - - [ ] User needs to sign in - - [ ] Tokens cleared - -5. **Token Refresh** - - [ ] Access token expires (15 min) - - [ ] Auto-refresh happens - - [ ] User stays logged in - - [ ] No interruption - ---- - -## Security Verification - -### JWT Token Structure -```json -{ - "sub": "user-uuid", - "phone_number": "+919876543210", - "role": "seller_buyer", - "user_type": "user", - "token_version": 1, // ✅ Now included - "high_assurance": false, - "iat": 1234567890, - "exp": 1234568790 -} -``` - -### Token Validation Flow -1. Extract token from Authorization header -2. Verify signature with secret -3. Check expiry (exp claim) -4. Validate token_version matches database -5. Check user exists and not deleted -6. Attach user to request - -### Refresh Token Flow -1. Verify refresh token signature -2. Check token_id exists in database -3. Verify token hash matches -4. Check not revoked -5. Check not expired -6. Check idle timeout (3 days) -7. Rotate token (revoke old, issue new) -8. Return new access + refresh tokens - ---- - -## Summary - -**All critical issues have been fixed:** - -1. ✅ JWT tokens now include `token_version` -2. ✅ Start route is dynamic (no landing screen flash for logged-in users) -3. ✅ Network errors don't log users out -4. ✅ Faster initial auth check -5. ✅ Better navigation logic -6. ✅ Improved error handling - -**Security Status**: ✅ **SECURE** -- All security best practices followed -- Token rotation working -- Global logout supported -- Device binding enforced -- Token reuse detection active - -**User Experience**: ✅ **IMPROVED** -- No landing screen flash for logged-in users -- Smooth navigation -- Better offline handling -- Clear error messages - -The authentication system is now production-ready and secure. - diff --git a/AUTO_LOGIN_FIX.md b/AUTO_LOGIN_FIX.md deleted file mode 100644 index 1d92413..0000000 --- a/AUTO_LOGIN_FIX.md +++ /dev/null @@ -1,161 +0,0 @@ -# Auto-Login Fix - Token Persistence Issue - -## Problem -User reported that after "clearing the app", they were logged out and had to re-enter phone number and OTP to sign in. - -## Root Cause Analysis - -### What "Clearing the App" Means -There are different ways to "clear" an app in Android: - -1. **Force Stop** (Settings → Apps → [App] → Force Stop) - - ✅ **Expected**: Tokens should persist - - Tokens stored in EncryptedSharedPreferences should remain - -2. **Clear App Data** (Settings → Apps → [App] → Storage → Clear Data) - - ⚠️ **Expected**: Tokens will be deleted - - This deletes ALL app data including EncryptedSharedPreferences - - User will need to sign in again (this is normal Android behavior) - -3. **Uninstall/Reinstall** - - ⚠️ **Expected**: Tokens will be deleted - - User will need to sign in again - -4. **Close/Reopen App** (Normal usage) - - ✅ **Expected**: Tokens should persist - - User should remain logged in - -### Issues Found - -1. **Network Errors Clearing Tokens** - - **Problem**: If there was a network error during token validation, tokens were being cleared - - **Impact**: User would be logged out even if tokens were still valid - - **Fix**: Distinguish between network errors and authentication errors - -2. **Token Save Timing** - - **Problem**: Using `.apply()` for token storage (asynchronous) - - **Impact**: Tokens might not be saved immediately before app closes - - **Fix**: Changed to `.commit()` for synchronous save (ensures tokens are saved) - -## Fixes Applied - -### 1. Improved Error Handling in MainViewModel -**File**: `MainViewModel.kt` - -**Changes**: -- Added network error detection -- Only clear tokens on authentication errors, not network errors -- Better error messages for users - -**Logic**: -```kotlin -if (isNetworkError) { - // Don't clear tokens - they might still be valid - // User might be offline - return@launch -} -``` - -### 2. Synchronous Token Saving -**File**: `TokenManager.kt` - -**Changes**: -- Changed from `.apply()` to `.commit()` for token saving -- Ensures tokens are saved synchronously before app closes - -**Before**: -```kotlin -.apply() // Asynchronous - might not complete before app closes -``` - -**After**: -```kotlin -.commit() // Synchronous - ensures tokens are saved immediately -``` - -## How It Works Now - -### Normal App Usage (Close/Reopen) -1. User signs in → Tokens saved to EncryptedSharedPreferences -2. User closes app → Tokens remain in storage -3. User reopens app → `MainViewModel.init()` checks for tokens -4. If tokens exist → Validates tokens -5. If tokens valid → User automatically logged in ✅ -6. If tokens expired → Attempts refresh -7. If refresh succeeds → User logged in ✅ -8. If refresh fails → User needs to sign in again - -### Network Error Handling -1. App starts → Checks for tokens -2. Network error occurs → **Tokens NOT cleared** -3. User sees "Network error" message -4. When network available → Tokens still valid, user can retry - -### Authentication Error Handling -1. App starts → Checks for tokens -2. Authentication error (401, invalid token) → **Tokens cleared** -3. User needs to sign in again - -## Testing Scenarios - -### ✅ Should Keep User Logged In -- Close app normally and reopen -- Force stop app and reopen -- Restart phone and reopen app -- Network error during token validation (tokens preserved) - -### ⚠️ Will Log User Out (Expected Behavior) -- Clear app data from Android settings -- Uninstall and reinstall app -- Refresh token expired (7 days of inactivity) -- Authentication error (invalid/expired tokens) - -## Important Notes - -1. **Clearing App Data**: If user clears app data from Android settings, tokens will be deleted. This is **expected Android behavior** - clearing app data removes all stored data. - -2. **Token Expiration**: - - Access tokens: 15 minutes - - Refresh tokens: 7 days (with activity) - - If refresh token expires, user must sign in again - -3. **Network Errors**: Network errors no longer cause tokens to be cleared. User will see an error message but tokens remain valid. - -## User Experience - -### Before Fix -- ❌ Network errors could log user out -- ❌ Tokens might not be saved if app closed quickly -- ❌ Unclear error messages - -### After Fix -- ✅ Network errors don't log user out -- ✅ Tokens saved synchronously (guaranteed) -- ✅ Clear error messages (network vs auth errors) -- ✅ Better user experience - -## Debugging - -To check if tokens are being saved: -1. Sign in to the app -2. Check logs for "User authenticated successfully" -3. Close app completely -4. Reopen app -5. Check logs for token validation - -If tokens are missing: -- Check if app data was cleared -- Check if refresh token expired -- Check logs for authentication errors - -## Summary - -The fix ensures: -1. ✅ Tokens persist when app is closed/reopened normally -2. ✅ Network errors don't clear tokens -3. ✅ Tokens are saved synchronously -4. ✅ Better error handling and user feedback -5. ✅ Clear distinction between network and auth errors - -**Note**: If user clears app data from Android settings, they will need to sign in again. This is normal Android behavior and cannot be prevented. - diff --git a/SIGNIN_REDIRECT_FIX.md b/SIGNIN_REDIRECT_FIX.md deleted file mode 100644 index 89a1ae0..0000000 --- a/SIGNIN_REDIRECT_FIX.md +++ /dev/null @@ -1,139 +0,0 @@ -# Sign-In Redirect Fix - ChooseServiceScreen - -## Issue -After successful sign-in, users should be redirected to `ChooseServiceScreen` instead of other screens. - -## Changes Made - -### 1. Updated MAIN Graph Start Destination -**File**: `MainNavGraph.kt` - -Changed the start destination of MAIN graph to `ChooseServiceScreen`: -```kotlin -navigation( - route = Graph.MAIN, - startDestination = AppScreen.chooseService("1") // ✅ ChooseServiceScreen -) -``` - -### 2. Simplified Sign-In Navigation -**File**: `AuthNavGraph.kt` - -Simplified the `onSuccess` callback to navigate directly to `Graph.MAIN`, which automatically uses the start destination (`ChooseServiceScreen`): - -**Before**: -```kotlin -onSuccess = { - // Complex navigation with delays and multiple steps - navController.navigate(Graph.MAIN) { ... } - // Then navigate to ChooseServiceScreen after delay - CoroutineScope(Dispatchers.Main).launch { - delay(200) - navController.navigate(AppScreen.chooseService("1")) { ... } - } -} -``` - -**After**: -```kotlin -onSuccess = { - // Simple navigation - MAIN graph starts at ChooseServiceScreen - navController.navigate(Graph.MAIN) { - popUpTo(Graph.AUTH) { inclusive = true } - launchSingleTop = true - } -} -``` - -### 3. Updated Sign-In Flow Comments -**File**: `OTPScreen.kt` - -Updated comments to clarify that sign-in navigates to ChooseServiceScreen: -```kotlin -if (isSignInFlow) { - // For existing users (sign-in), navigate to ChooseServiceScreen - android.util.Log.d("OTPScreen", "Existing user - navigating to ChooseServiceScreen") - onSuccess() // This navigates to ChooseServiceScreen -} -``` - -## Navigation Flow - -### Sign-In Flow: -1. User enters phone number → `SignInScreen` -2. User enters OTP → `OTPScreen` -3. OTP verified → `authManager.login()` succeeds -4. Tokens saved → `mainViewModel.refreshAuthStatus()` called -5. `onSuccess()` callback triggered -6. Navigate to `Graph.MAIN` → **ChooseServiceScreen** ✅ - -### Sign-Up Flow: -1. User fills signup form → `SignUpScreen` -2. User enters OTP → `OTPScreen` -3. OTP verified → User created/updated -4. Signup API called → Profile updated -5. Navigate to `Graph.MAIN` → **ChooseServiceScreen** ✅ - -### App Startup Flow: -1. App opens → `MainViewModel` checks tokens -2. If tokens valid → `authState = Authenticated` -3. `AppNavigation` sets `startDestination = Graph.MAIN` -4. `Graph.MAIN` starts at **ChooseServiceScreen** ✅ - -## Route Structure - -### MAIN Graph: -- **Start Destination**: `choose_service/1` (ChooseServiceScreen) -- **Other Routes**: - - `buy_animals` (BuyScreen) - - `create_profile/{name}` (CreateProfileScreen) - - etc. - -### AUTH Graph: -- **Start Destination**: `landing` (LandingScreen) -- **Other Routes**: - - `sign_in` (SignInScreen) - - `sign_up` (SignUpScreen) - - `otp/{phoneNumber}/{name}` (OTPScreen) - - etc. - -## Testing - -### ✅ Test Sign-In: -1. Open app → Landing screen -2. Click "Sign In" -3. Enter phone number -4. Enter OTP -5. After successful verification → Should navigate to **ChooseServiceScreen** ✅ - -### ✅ Test Sign-Up: -1. Open app → Landing screen -2. Click "Sign Up" -3. Fill form and enter OTP -4. After successful signup → Should navigate to **ChooseServiceScreen** ✅ - -### ✅ Test App Reopen (Logged In): -1. Sign in to app -2. Close app completely -3. Reopen app -4. Should open directly to **ChooseServiceScreen** ✅ - -## Summary - -✅ **Sign-in redirect fixed**: -- After successful sign-in → Navigates to **ChooseServiceScreen** -- After successful sign-up → Navigates to **ChooseServiceScreen** -- App reopen (logged in) → Opens to **ChooseServiceScreen** - -✅ **Navigation simplified**: -- Removed complex navigation with delays -- Uses graph start destination automatically -- Cleaner, more maintainable code - -✅ **Consistent routing**: -- All authenticated users go to ChooseServiceScreen -- All unauthenticated users go to LandingScreen -- No navigation inconsistencies - -The sign-in flow now correctly redirects users to ChooseServiceScreen after successful authentication. - diff --git a/SIGNUP_DUPLICATE_CHECK.md b/SIGNUP_DUPLICATE_CHECK.md deleted file mode 100644 index 8aab0bb..0000000 --- a/SIGNUP_DUPLICATE_CHECK.md +++ /dev/null @@ -1,128 +0,0 @@ -# Signup Duplicate Phone Number Check - -## Problem -When a user tries to sign up with a phone number that is already registered in the database, they should be shown a message directing them to sign in instead of proceeding with signup. - -## Solution Implemented - -### 1. Updated SignUpScreen (Android App) -**File**: `LivingAi_Lg/app/src/main/java/com/example/livingai_lg/ui/screens/auth/SignUpScreen.kt` - -**Changes**: -- Added a check before requesting OTP to verify if the user already exists -- If user exists and is fully registered (has a name), shows a toast message: "This phone number is already registered. Please sign in instead." -- Automatically navigates to the sign-in screen -- If user doesn't exist or is in the middle of signup (no name), proceeds with normal signup flow - -**Flow**: -1. User fills signup form and clicks "Sign Up" -2. App calls `checkUser()` API to verify if phone number is registered -3. If user exists → Show message and navigate to sign-in -4. If user doesn't exist → Proceed with OTP request and signup - -### 2. Enhanced check-user Endpoint (Backend) -**File**: `farm-auth-service/src/routes/authRoutes.js` - -**Changes**: -- Updated the `/auth/check-user` endpoint to check if user has a name (fully registered) -- Returns `user_exists: true` only if: - - User exists in database - - User has a name (not just created by verify-otp) -- Returns `user_exists: false` if: - - User doesn't exist, OR - - User exists but has no name (incomplete signup - allow them to continue) - -**Logic**: -```javascript -// Check if user exists and has a name (fully registered) -const found = await db.query( - `SELECT id, name FROM users - WHERE (phone_number = $1 OR phone_number = $2) - AND deleted = FALSE`, - phoneSearchParams -); - -if (found.rows.length === 0) { - // User not found - allow signup - return { user_exists: false }; -} - -const user = found.rows[0]; -const isFullyRegistered = user.name && user.name.trim() !== ''; - -if (isFullyRegistered) { - // User is fully registered - should sign in - return { user_exists: true, message: 'User is already registered. Please sign in instead.' }; -} else { - // User exists but incomplete - allow signup to continue - return { user_exists: false }; -} -``` - -## User Experience - -### Scenario 1: New User -1. User enters phone number that doesn't exist in database -2. Clicks "Sign Up" -3. System checks → User doesn't exist -4. Proceeds with OTP request and signup flow ✅ - -### Scenario 2: Fully Registered User -1. User enters phone number that exists and has a name -2. Clicks "Sign Up" -3. System checks → User exists and is fully registered -4. Shows toast: "This phone number is already registered. Please sign in instead." -5. Automatically navigates to sign-in screen ✅ - -### Scenario 3: Incomplete Signup -1. User previously started signup (verify-otp created user but didn't complete) -2. User enters same phone number again -3. Clicks "Sign Up" -4. System checks → User exists but has no name -5. Proceeds with signup to complete registration ✅ - -## Benefits - -1. **Prevents Duplicate Accounts**: Users can't create multiple accounts with the same phone number -2. **Better UX**: Clear message directing users to sign in if already registered -3. **Handles Edge Cases**: Users who started but didn't complete signup can still finish -4. **Automatic Navigation**: Seamlessly redirects to sign-in screen - -## Testing - -To test the implementation: - -1. **Test New User Signup**: - - Enter a new phone number - - Should proceed with signup normally - -2. **Test Existing User**: - - Enter a phone number that's already registered - - Should show message and navigate to sign-in - -3. **Test Incomplete Signup**: - - Start signup but don't complete (verify OTP but don't finish) - - Try to sign up again with same number - - Should allow completion of signup - -## API Response Examples - -### User Exists (Fully Registered) -```json -{ - "success": true, - "message": "User is already registered. Please sign in instead.", - "user_exists": true -} -``` - -### User Doesn't Exist or Incomplete -```json -{ - "success": false, - "error": "USER_NOT_FOUND", - "message": "User is not registered. Please sign up to create a new account.", - "user_exists": false -} -``` - diff --git a/SIGNUP_FIX_SUMMARY.md b/SIGNUP_FIX_SUMMARY.md deleted file mode 100644 index b59c94b..0000000 --- a/SIGNUP_FIX_SUMMARY.md +++ /dev/null @@ -1,119 +0,0 @@ -# Signup Flow Fix - Database Entry Creation - -## Problem -Successful signups were not creating entries in the database. The database was empty even after successful signup attempts. - -## Root Cause -1. **verify-otp endpoint** was returning 404 error if user didn't exist, preventing new user creation -2. **signup endpoint** was only populating minimal fields (phone_number, name, country_code) and missing important data like language and timezone from device_info - -## Solution Implemented - -### 1. Fixed verify-otp Endpoint (Lines 431-465) -- **Before**: Returned 404 error if user didn't exist -- **After**: Creates a minimal user entry if user doesn't exist (for signup flow) -- Now creates user with: - - `phone_number` (encrypted) - - `country_code` (extracted from phone number) - - `language` (from device_info) - - `timezone` (from device_info) -- User is created without a name initially, which will be added by the signup endpoint - -### 2. Enhanced signup Endpoint (Lines 959-978, 810-831) -- **Before**: Only inserted phone_number, name, and country_code -- **After**: Now also populates: - - `language` (from device_info.language_code) - - `timezone` (from device_info.timezone) -- When updating existing user (created by verify-otp), also updates language and timezone - -### 3. Device Information Storage -- Device information is already properly stored in `user_devices` table via upsert operation -- Includes: platform, model, os_version, app_version, language_code, timezone - -## Complete Signup Flow - -1. **User requests OTP** → OTP is sent to phone number -2. **User verifies OTP** → `verify-otp` endpoint: - - Verifies OTP code - - Creates minimal user entry if user doesn't exist - - Creates/updates device entry in `user_devices` table - - Returns access_token and refresh_token -3. **User completes signup** → `signup` endpoint: - - Updates user with name and location data - - Updates language and timezone if provided - - Creates location entry if location data provided - - Returns updated user info and tokens - -## Database Fields Populated - -### Users Table -- ✅ `id` - Auto-generated UUID -- ✅ `phone_number` - Encrypted phone number -- ✅ `name` - User's name (from signup) -- ✅ `country_code` - Extracted from phone number -- ✅ `language` - From device_info.language_code -- ✅ `timezone` - From device_info.timezone -- ✅ `is_active` - Default TRUE -- ✅ `roles` - Default ['seller_buyer'] -- ✅ `active_role` - Default 'seller_buyer' -- ✅ `user_type` - Default 'user' -- ✅ `token_version` - Default 1 -- ✅ `deleted` - Default FALSE -- ✅ `created_at` - Auto-generated timestamp -- ✅ `updated_at` - Auto-updated timestamp -- ✅ `last_login_at` - Updated on login/signup - -### User Devices Table -- ✅ `user_id` - Foreign key to users -- ✅ `device_identifier` - Device ID -- ✅ `device_platform` - From device_info.platform -- ✅ `device_model` - From device_info.model -- ✅ `os_version` - From device_info.os_version -- ✅ `app_version` - From device_info.app_version -- ✅ `language_code` - From device_info.language_code -- ✅ `timezone` - From device_info.timezone -- ✅ `last_seen_at` - Updated on each request -- ✅ `is_active` - Set to TRUE - -### Locations Table (Optional) -- ✅ Created if state, district, or city_village provided -- ✅ Linked to user via user_id - -## Testing - -To verify the fix works: - -1. **Check users table:** -```sql -SELECT id, phone_number, name, country_code, language, timezone, created_at, last_login_at -FROM users -WHERE deleted = FALSE -ORDER BY created_at DESC; -``` - -2. **Check user_devices table:** -```sql -SELECT ud.*, u.name, u.phone_number -FROM user_devices ud -JOIN users u ON ud.user_id = u.id -WHERE u.deleted = FALSE -ORDER BY ud.created_at DESC; -``` - -3. **Check locations table:** -```sql -SELECT l.*, u.name, u.phone_number -FROM locations l -JOIN users u ON l.user_id = u.id -WHERE u.deleted = FALSE -ORDER BY l.created_at DESC; -``` - -## Notes - -- Phone numbers are encrypted before storage (field-level encryption) -- Device information is automatically collected from Android app -- All timestamps are in UTC -- Users are soft-deleted (deleted flag) rather than hard-deleted -- Device entries are upserted (created or updated) on each login/signup - diff --git a/START_ROUTE_FIX.md b/START_ROUTE_FIX.md deleted file mode 100644 index bb02198..0000000 --- a/START_ROUTE_FIX.md +++ /dev/null @@ -1,124 +0,0 @@ -# Start Route Fix - ChooseServiceScreen for Authenticated Users - -## Issue -User requested that authenticated users should be directed to `ChooseServiceScreen` instead of `BuyScreen` when they open the app. - -## Changes Made - -### 1. Updated MAIN Graph Start Destination -**File**: `MainNavGraph.kt` - -**Before**: -```kotlin -navigation( - route = Graph.MAIN, - startDestination = AppScreen.BUY_ANIMALS -) -``` - -**After**: -```kotlin -navigation( - route = Graph.MAIN, - startDestination = AppScreen.chooseService("1") // ChooseServiceScreen with default profileId -) -``` - -### 2. Navigation Flow - -#### Authenticated User Flow: -1. App starts → `MainViewModel.init()` checks tokens -2. If tokens exist → `authState = Authenticated` -3. `AppNavigation` reads `authState` -4. `startDestination` = `Graph.MAIN` (which starts at `ChooseServiceScreen`) -5. User sees `ChooseServiceScreen` ✅ - -#### Unauthenticated User Flow: -1. App starts → `MainViewModel.init()` checks tokens -2. No tokens → `authState = Unauthenticated` -3. `AppNavigation` reads `authState` -4. `startDestination` = `Graph.AUTH` (which starts at `LandingScreen`) -5. User sees `LandingScreen` ✅ - -## Route Structure - -### MAIN Graph Routes: -- **Start Destination**: `choose_service/1` (ChooseServiceScreen) -- **Other Routes**: - - `buy_animals` (BuyScreen) - - `create_profile/{name}` (CreateProfileScreen) - - etc. - -### AUTH Graph Routes: -- **Start Destination**: `landing` (LandingScreen) -- **Other Routes**: - - `sign_in` (SignInScreen) - - `sign_up` (SignUpScreen) - - `otp/{phoneNumber}/{name}` (OTPScreen) - - etc. - -## JWT Verification Logic - -### Backend (Node.js) -1. ✅ Access tokens include `token_version` in payload -2. ✅ Middleware validates token signature, expiry, and version -3. ✅ Refresh tokens rotate on each use -4. ✅ Token reuse detection active -5. ✅ Device binding enforced - -### Frontend (Android) -1. ✅ Tokens stored in EncryptedSharedPreferences -2. ✅ Auto-refresh on 401 responses (Ktor Auth plugin) -3. ✅ Synchronous token save (commit) -4. ✅ Network errors don't clear tokens -5. ✅ Fast initial auth check (synchronous token check) - -## User Experience - -### ✅ Logged In User -- App opens → **ChooseServiceScreen** (no landing screen flash) -- Can select service type -- Navigate to BuyScreen after selection - -### ✅ First Time User -- App opens → **LandingScreen** -- Can sign up or sign in -- After login → Navigate to ChooseServiceScreen - -### ✅ Offline User (with valid tokens) -- App opens → **ChooseServiceScreen** -- Network error shown but user stays logged in -- When online → Works normally - -## Testing - -1. **Test Authenticated User**: - - Sign in to app - - Close app completely - - Reopen app - - Should open directly to **ChooseServiceScreen** ✅ - -2. **Test Unauthenticated User**: - - Clear app data or sign out - - Open app - - Should open to **LandingScreen** ✅ - -3. **Test JWT Verification**: - - Valid tokens → ChooseServiceScreen - - Expired tokens → LandingScreen - - Invalid tokens → LandingScreen - -## Summary - -✅ **Start route correctly set**: -- Authenticated users → `ChooseServiceScreen` (route: `choose_service/1`) -- Unauthenticated users → `LandingScreen` (route: `landing`) - -✅ **JWT and refresh token logic verified**: -- Token validation working correctly -- Auto-refresh working -- Token versioning working -- Security best practices followed - -The routing now correctly directs users based on their authentication status. -