Repository: Crypto-Jaguars/Revo-Marketplace-Expo
Pull request #15: feat: implement user authentication flow with login, signup, and biom…
Author: login: arebisola
Created: 2025-03-26T11:45:58Z | Merged: 2025-03-30T23:39:19Z

This PR implements a complete user authentication flow with the following features:

- Login and Signup screens with form validation
- Secure token storage using expo-secure-store
- Biometric authentication support
- Forgot password functionality
- Protected routes with authentication state management
- Token validation and refresh mechanism

Note: Currently using mock API endpoints for development. Need to integrate with actual authentication API endpoints.


![Screenshot_1742989514](https://github.com/user-attachments/assets/513172bb-3021-4605-9b67-a4491b701ca4)
![Screenshot_1742989508](https://github.com/user-attachments/assets/4c3c640c-afd3-442c-bcfe-7df2072e2af3)
![Screenshot_1742989504](https://github.com/user-attachments/assets/3691e97a-1517-40ed-aaa7-a584e9b341b7)
![Screenshot_1742989501](https://github.com/user-attachments/assets/be7c7c0e-5b8c-4354-bdc5-798bb3c71821)

Test credentials for development:
- Email: test@example.com
- Password: password123


Closes #8 

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **New Features**
  - Introduced dedicated authentication screens for logging in, signing up, and recovering forgotten passwords.
  - Integrated biometric authentication for a faster, more secure sign-in experience.
  - Optimized navigation by automatically directing users based on their authentication status.
  - Enhanced header functionality with an intuitive logout option.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Linked issue #8: Implement User Authentication Flow with Signup/Login and Additional Features
## Overview

This feature introduces a complete user authentication flow, allowing users to create accounts, log in, and access personalized features needed to complete purchases on the Revo-Marketplace-Expo app. 🚀

## Requirements

- Create Login and Signup screens
- Implement form validation using React Hook Form and Zod
- Connect to the authentication API
- Securely store authentication tokens
- Provide user feedback for authentication errors
- Add a "Forgot Password" flow
- Implement a biometric authentication option
- Create logout functionality

## Technical Considerations

1. **Form Validation:**
   - Use React Hook Form in combination with Zod to validate the login and signup forms.

2. **API Integration:**
   - Ensure the screens properly connect with the authentication API.
   - Implement error handling for failed authentication attempts and display clear feedback to users.

3. **Security:**
   - Authentication tokens must be stored securely, adhering to best practices.

4. **Additional Flows:**
   - Create a "Forgot Password" flow that guides users through resetting their password.
   - Consider integrating device-specific biometric authentication where available.

5. **Logout:**
   - Implement logout functionality that safely clears user session data.

## Definition of Done

- [ ] Login and Signup screens are fully functional and integrated with the authentication API.
- [ ] Form validation is implemented using React Hook Form and Zod.
- [ ] Secure storage of authentication tokens is implemented.
- [ ] Error handling and user feedback for authentication failures is in place.
- [ ] A functional "Forgot Password" flow is added.
- [ ] Biometric authentication option is implemented where support exists.
- [ ] Logout functionality clears user session successfully.

## References

- [Revo-Marketplace-Expo README](https://github.com/Crypto-Jaguars/Revo-Marketplace-Expo?tab=readme-ov-file)

Let's work towards making user authentication secure, robust, and user-friendly! 🔐
Linked issue comments: user: login: LaGodxy
created_at: 2025-03-20T00:35:48Z
body: May I try my hand at this?
author_association: NONE
user: login: shamoo53
created_at: 2025-03-20T03:51:15Z
body: hi i have carefully gone through the instruction and understand how to implement User Authentication Flow with Signup/Login and Additional Features , i would like to work on this, kindly assign this issue ETA 12hrs
author_association: NONE
user: login: Diggardson270
created_at: 2025-03-20T14:27:16Z
body: Can I take this
Hello,

I’m excited about the opportunity to contribute to Revolutionary Farmers project and would love to bring my frontend development skills to the team. I specialize in React, Nextjs, JavaScript, Tailwind CSS, and responsive UI design, and I’m eager to help improve the user experience of the platform.

I’ve worked on projects that involved:
✅ Building scalable and responsive web interfaces
✅ Implementing API integrations for dynamic content
✅ Optimizing performance and accessibility for better usability

I’d love to know more about how I can contribute to the project. Are there any open issues or frontend-related tasks that need attention? Looking forward to collaborating!s issue?
author_association: NONE
user: login: arebisola
created_at: 2025-03-20T16:20:47Z
body: Hey there. greetings and salutations I am Arebisola a Front-end Dev I am very excited to contribute to this project
I intend on solving this task by

1. Create Login and Signup Screens:

I’ll design and implement intuitive login and signup screens where users can easily enter their email and password to either log in or create a new account.
I’ll make sure to include a “Forgot Password” link on the login screen for users who need to reset their credentials.
2. Implement Form Validation:

I’ll use React Hook Form to efficiently handle form state and validation. For input validation, I’ll integrate Zod, ensuring email formats are correct, passwords meet security requirements (e.g., minimum length), and required fields are validated properly before submission.
3. Connect to the Authentication API:

I’ll integrate the authentication system with the backend API to handle user logins and signups. I’ll ensure that the app properly handles API responses, stores authentication tokens upon success, and manages errors (like invalid credentials) to provide useful feedback to the user.
4. Securely Store Authentication Tokens:

To enhance security, I’ll store authentication tokens using SecureStore, which will encrypt the tokens and store them securely on the user’s device, preventing unauthorized access to sensitive data.
5. Provide User Feedback for Authentication Errors:

I’ll ensure that any authentication errors are communicated clearly to users by displaying meaningful error messages, such as “Incorrect email or password” when the login fails, or providing a specific error for invalid form entries.
6. Implement Forgot Password Flow:

I’ll develop a user-friendly “Forgot Password” flow, allowing users to reset their password by submitting their email address. The system will send a password reset link to their email, which they can use to set a new password securely.
7. Add Biometric Authentication:

I’ll implement Expo-LocalAuthentication to add biometric authentication (like fingerprint or facial recognition) for users who have biometric authentication supported on their devices. This feature will allow users to log in more quickly and securely without needing to enter their password.
8. Create Logout Functionality:

I’ll implement a simple and secure logout feature that will clear the authentication token from the device’s secure storage, ensuring the user’s session is properly ended. After logging out, users will be redirected back to the login screen.
author_association: CONTRIBUTOR
user: login: respp
created_at: 2025-03-20T17:42:21Z
body: [FE]: Create User Authentication Flow
Hello @salazarsebas!

I’m Renzo Barcos, a Full-Stack developer with two years of experience in Web2 and a strong background in Web3, covering both front-end and back-end. As a Diamond Contributor (Top 1%) and an active member of Dojo Coding⛩️, I have contributed to innovative projects in StarkNet, Stellar, Optimism, and Worldcoin, developing high-performance, scalable, and secure blockchain solutions.

Recently, I developed smart contracts for a project integrated into the Winter Hackathon, and I’m currently a maintainer at Paystell.

More about me: www.renzobarcos.site

I’m excited about the opportunity to collaborate on implementing the user authentication flow for Revo-Marketplace-Expo, and I’d love to take on this issue.

Work Plan
Understand the Requirements:
Review the provided designs and documentation to fully understand the requirements for the Login, Signup, and Forgot Password flows.
Understand the expected behavior of the biometric authentication and logout functionality.
Create the Screens:
Implement Login and Signup screens with form validation using React Hook Form and Zod.
Implement clear error messages and feedback for authentication failures.
Connect to the Authentication API:
Ensure the Login and Signup screens are properly connected to the authentication API.
Implement secure handling of authentication tokens (using cookies or localStorage, depending on the security requirements).
Implement Biometric Authentication (if supported):
Integrate biometric authentication for devices where it's available (e.g., Face ID or fingerprint).
Add "Forgot Password" Flow:
Implement the "Forgot Password" functionality that guides users through resetting their password.
Implement Logout:
Ensure the Logout functionality clears the user session safely and securely.
Testing:
Test the Login, Signup, Forgot Password, and Logout flows for proper integration with the API and expected behavior.
Verify token storage and handle errors gracefully during authentication attempts.
Finalize and Submit:
Commit the changes to a branch named feature/user-authentication-flow.
Open a pull request with a detailed summary of the implementation.
Estimated Time: 2-3 days

Thank you for the opportunity to contribute to making the Revo-Marketplace-Expo authentication flow secure, robust, and user-friendly! 🔐
author_association: CONTRIBUTOR
user: login: davedumto
created_at: 2025-03-20T17:58:23Z
body: Hi, I’m David, a frontend developer skilled in React, Next.js, JavaScript, and TypeScript. For this task, I will:
Build Login and Signup screens with form validation using React Hook Form and Zod.
Integrate with the authentication API and implement secure token storage.
Add a "Forgot Password" flow and biometric authentication (if supported).
Implement logout functionality.
ETA: 8 hours.
author_association: NONE
user: login: ekumamatthew
created_at: 2025-03-20T18:53:53Z
body: May I pick this up?
I am a fullstack Web and mobile developer. 
I will Implement the user auth flow with backend integration.  With design Following the ui provided.
author_association: NONE
user: login: greatest0fallt1me
created_at: 2025-03-21T07:28:25Z
body: ---

## 🚀 **Introduction**  
Hello! I’d love to work on this and help improve the project structure.  
With my experience in **Cairo, StarkNet, and smart contracts**, I can ensure a smooth and efficient implementation.  

---

## 🔥 **About Me**  
I am a **Blockchain Specialist** passionate about delivering efficient and secure blockchain solutions.  

### 🔹 **Skills & Expertise**  
- ### 🔗 **Blockchain Specialist**  
  - 💡 Proficient in **Ethereum**, **Hyperledger**, and **smart contract development**.  
  - 🔒 Ensuring secure and efficient blockchain implementations.  

- ### 💻 **Full-Stack Developer**  
  - ⚙️ **Backend**: Node.js, Express.  
  - 🎨 **Frontend**: React, Next.js for interactive and dynamic user interfaces.  

- ### 📈 **Agile Practitioner**  
  - 🚀 Advocate of **Agile methodologies** and **cross-functional teamwork**.  
  - 📅 Focused on delivering projects on time, within scope, and with high quality.  

---

## ⏳ **Estimated Time**  
I will complete this task within **one day**, ensuring everything is properly updated and thoroughly tested.  

---

🔥 **Let me know if I can get started—excited to contribute!** 🚀  
author_association: CONTRIBUTOR
user: login: hoangkianh
created_at: 2025-03-21T09:48:22Z
body: Could I take over this issue?
author_association: NONE
user: login: nonso7
created_at: 2025-03-22T00:57:33Z
body: Could I be assigned to this?
author_association: NONE

Diff:
diff --git a/app/(auth)/_layout.tsx b/app/(auth)/_layout.tsx
new file mode 100644
index 0000000..054595a
--- /dev/null
+++ b/app/(auth)/_layout.tsx
@@ -0,0 +1,49 @@
+import { useEffect } from 'react';
+import { Redirect, Stack } from 'expo-router';
+import { useAuthStore } from '@/store/auth';
+
+export default function AuthLayout() {
+  const { isAuthenticated, validateToken } = useAuthStore();
+
+  useEffect(() => {
+    const checkAuth = async () => {
+      const isValid = await validateToken();
+      if (isValid) {
+        // Token is valid, user is authenticated
+        return;
+      }
+    };
+    checkAuth();
+  }, []);
+
+  // If the user is authenticated, redirect to the main app
+  if (isAuthenticated) {
+    return <Redirect href="/(tabs)" />;
+  }
+
+  return (
+    <Stack>
+      <Stack.Screen
+        name="login"
+        options={{
+          title: 'Login',
+          headerShown: false,
+        }}
+      />
+      <Stack.Screen
+        name="signup"
+        options={{
+          title: 'Sign Up',
+          headerShown: false,
+        }}
+      />
+      <Stack.Screen
+        name="forgot-password"
+        options={{
+          title: 'Reset Password',
+          headerShown: false,
+        }}
+      />
+    </Stack>
+  );
+} 
\ No newline at end of file
diff --git a/app/(auth)/forgot-password.tsx b/app/(auth)/forgot-password.tsx
new file mode 100644
index 0000000..f6f65fc
--- /dev/null
+++ b/app/(auth)/forgot-password.tsx
@@ -0,0 +1,121 @@
+import React from 'react';
+import { View, StyleSheet, TouchableOpacity, Text } from 'react-native';
+import { useForm } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { router } from 'expo-router';
+import { useColorScheme } from '@/components/useColorScheme';
+import Colors from '@/constants/Colors';
+import { AuthInput } from '@/components/auth/AuthInput';
+import { resetPasswordSchema, type ResetPasswordFormData } from '@/schemas/auth';
+import { useAuthStore } from '@/store/auth';
+
+export default function ForgotPasswordScreen() {
+  const colorScheme = useColorScheme();
+  const { resetPassword, isLoading, error } = useAuthStore();
+  const {
+    register,
+    handleSubmit,
+    formState: { errors },
+  } = useForm<ResetPasswordFormData>({
+    resolver: zodResolver(resetPasswordSchema),
+  });
+
+  const onSubmit = async (data: ResetPasswordFormData) => {
+    await resetPassword(data.email);
+  };
+
+  return (
+    <View style={[styles.container, { backgroundColor: Colors[colorScheme ?? 'light'].background }]}>
+      <View style={styles.form}>
+        <Text style={[styles.title, { color: Colors[colorScheme ?? 'light'].text }]}>
+          Reset Password
+        </Text>
+        
+        <Text style={[styles.description, { color: Colors[colorScheme ?? 'light'].text }]}>
+          Enter your email address and we'll send you instructions to reset your password.
+        </Text>
+
+        <AuthInput
+          label="Email"
+          placeholder="Enter your email"
+          keyboardType="email-address"
+          autoCapitalize="none"
+          {...register('email')}
+          error={errors.email?.message}
+        />
+
+        {error && <Text style={styles.errorText}>{error}</Text>}
+
+        <TouchableOpacity
+          style={[styles.button, { backgroundColor: Colors[colorScheme ?? 'light'].tint }]}
+          onPress={handleSubmit(onSubmit)}
+          disabled={isLoading}
+        >
+          <Text style={styles.buttonText}>
+            {isLoading ? 'Sending...' : 'Send Reset Instructions'}
+          </Text>
+        </TouchableOpacity>
+
+        <View style={styles.links}>
+          <TouchableOpacity onPress={() => router.push('/login')}>
+            <Text style={[styles.link, { color: Colors[colorScheme ?? 'light'].tint }]}>
+              Back to Login
+            </Text>
+          </TouchableOpacity>
+        </View>
+      </View>
+    </View>
+  );
+}
+
+const styles = StyleSheet.create({
+  container: {
+    flex: 1,
+    padding: 20,
+  },
+  form: {
+    flex: 1,
+    justifyContent: 'center',
+    maxWidth: 400,
+    width: '100%',
+    alignSelf: 'center',
+  },
+  title: {
+    fontSize: 32,
+    fontWeight: 'bold',
+    marginBottom: 16,
+    textAlign: 'center',
+  },
+  description: {
+    fontSize: 16,
+    textAlign: 'center',
+    marginBottom: 32,
+    lineHeight: 24,
+  },
+  button: {
+    height: 48,
+    borderRadius: 8,
+    justifyContent: 'center',
+    alignItems: 'center',
+    marginTop: 16,
+  },
+  buttonText: {
+    color: '#fff',
+    fontSize: 16,
+    fontWeight: '600',
+  },
+  links: {
+    marginTop: 24,
+    alignItems: 'center',
+  },
+  link: {
+    fontSize: 16,
+    marginVertical: 8,
+  },
+  errorText: {
+    color: '#ff3b30',
+    fontSize: 14,
+    marginTop: 8,
+    textAlign: 'center',
+  },
+}); 
\ No newline at end of file
diff --git a/app/(auth)/login.tsx b/app/(auth)/login.tsx
new file mode 100644
index 0000000..701c7f8
--- /dev/null
+++ b/app/(auth)/login.tsx
@@ -0,0 +1,168 @@
+import React from 'react';
+import { View, StyleSheet, TouchableOpacity, Text } from 'react-native';
+import { useForm, Controller } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { router } from 'expo-router';
+import { useColorScheme } from '@/components/useColorScheme';
+import Colors from '@/constants/Colors';
+import { AuthInput } from '@/components/auth/AuthInput';
+import { loginSchema, type LoginFormData } from '@/schemas/auth';
+import { useAuthStore } from '@/store/auth';
+import * as LocalAuthentication from 'expo-local-authentication';
+
+export default function LoginScreen() {
+  const colorScheme = useColorScheme();
+  const { login, authenticateWithBiometrics, isLoading, error } = useAuthStore();
+  const {
+    control,
+    handleSubmit,
+    formState: { errors },
+  } = useForm<LoginFormData>({
+    resolver: zodResolver(loginSchema),
+    defaultValues: {
+      email: '',
+      password: '',
+    },
+  });
+
+  const onSubmit = async (data: LoginFormData) => {
+    await login(data.email, data.password);
+  };
+
+  const handleBiometricAuth = async () => {
+    const success = await authenticateWithBiometrics();
+    if (success) {
+      // TODO: Implement biometric login logic
+      console.log('Biometric authentication successful');
+    }
+  };
+
+  return (
+    <View style={[styles.container, { backgroundColor: Colors[colorScheme ?? 'light'].background }]}>
+      <View style={styles.form}>
+        <Text style={[styles.title, { color: Colors[colorScheme ?? 'light'].text }]}>
+          Welcome Back
+        </Text>
+        
+        <Controller
+          control={control}
+          name="email"
+          render={({ field: { onChange, value } }) => (
+            <AuthInput
+              label="Email"
+              placeholder="Enter your email"
+              keyboardType="email-address"
+              autoCapitalize="none"
+              onChangeText={onChange}
+              value={value}
+              error={errors.email?.message}
+            />
+          )}
+        />
+
+        <Controller
+          control={control}
+          name="password"
+          render={({ field: { onChange, value } }) => (
+            <AuthInput
+              label="Password"
+              placeholder="Enter your password"
+              secureTextEntry
+              onChangeText={onChange}
+              value={value}
+              error={errors.password?.message}
+            />
+          )}
+        />
+
+        {error && <Text style={styles.errorText}>{error}</Text>}
+
+        <TouchableOpacity
+          style={[styles.button, { backgroundColor: Colors[colorScheme ?? 'light'].tint }]}
+          onPress={handleSubmit(onSubmit)}
+          disabled={isLoading}
+        >
+          <Text style={styles.buttonText}>
+            {isLoading ? 'Logging in...' : 'Login'}
+          </Text>
+        </TouchableOpacity>
+
+        <TouchableOpacity
+          style={styles.biometricButton}
+          onPress={handleBiometricAuth}
+        >
+          <Text style={[styles.biometricText, { color: Colors[colorScheme ?? 'light'].tint }]}>
+            Login with Biometrics
+          </Text>
+        </TouchableOpacity>
+
+        <View style={styles.links}>
+          <TouchableOpacity onPress={() => router.push('/signup')}>
+            <Text style={[styles.link, { color: Colors[colorScheme ?? 'light'].tint }]}>
+              Don't have an account? Sign up
+            </Text>
+          </TouchableOpacity>
+
+          <TouchableOpacity onPress={() => router.push('/forgot-password')}>
+            <Text style={[styles.link, { color: Colors[colorScheme ?? 'light'].tint }]}>
+              Forgot password?
+            </Text>
+          </TouchableOpacity>
+        </View>
+      </View>
+    </View>
+  );
+}
+
+const styles = StyleSheet.create({
+  container: {
+    flex: 1,
+    padding: 20,
+  },
+  form: {
+    flex: 1,
+    justifyContent: 'center',
+    maxWidth: 400,
+    width: '100%',
+    alignSelf: 'center',
+  },
+  title: {
+    fontSize: 32,
+    fontWeight: 'bold',
+    marginBottom: 32,
+    textAlign: 'center',
+  },
+  button: {
+    height: 48,
+    borderRadius: 8,
+    justifyContent: 'center',
+    alignItems: 'center',
+    marginTop: 16,
+  },
+  buttonText: {
+    color: '#fff',
+    fontSize: 16,
+    fontWeight: '600',
+  },
+  biometricButton: {
+    marginTop: 16,
+    alignItems: 'center',
+  },
+  biometricText: {
+    fontSize: 16,
+  },
+  links: {
+    marginTop: 24,
+    alignItems: 'center',
+  },
+  link: {
+    fontSize: 16,
+    marginVertical: 8,
+  },
+  errorText: {
+    color: '#ff3b30',
+    fontSize: 14,
+    marginTop: 8,
+    textAlign: 'center',
+  },
+}); 
\ No newline at end of file
diff --git a/app/(auth)/signup.tsx b/app/(auth)/signup.tsx
new file mode 100644
index 0000000..68d758f
--- /dev/null
+++ b/app/(auth)/signup.tsx
@@ -0,0 +1,135 @@
+import React from 'react';
+import { View, StyleSheet, TouchableOpacity, Text } from 'react-native';
+import { useForm } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { router } from 'expo-router';
+import { useColorScheme } from '@/components/useColorScheme';
+import Colors from '@/constants/Colors';
+import { AuthInput } from '@/components/auth/AuthInput';
+import { signupSchema, type SignupFormData } from '@/schemas/auth';
+import { useAuthStore } from '@/store/auth';
+
+export default function SignupScreen() {
+  const colorScheme = useColorScheme();
+  const { signup, isLoading, error } = useAuthStore();
+  const {
+    register,
+    handleSubmit,
+    formState: { errors },
+  } = useForm<SignupFormData>({
+    resolver: zodResolver(signupSchema),
+  });
+
+  const onSubmit = async (data: SignupFormData) => {
+    await signup(data.email, data.password, data.name);
+  };
+
+  return (
+    <View style={[styles.container, { backgroundColor: Colors[colorScheme ?? 'light'].background }]}>
+      <View style={styles.form}>
+        <Text style={[styles.title, { color: Colors[colorScheme ?? 'light'].text }]}>
+          Create Account
+        </Text>
+        
+        <AuthInput
+          label="Name"
+          placeholder="Enter your name"
+          autoCapitalize="words"
+          {...register('name')}
+          error={errors.name?.message}
+        />
+
+        <AuthInput
+          label="Email"
+          placeholder="Enter your email"
+          keyboardType="email-address"
+          autoCapitalize="none"
+          {...register('email')}
+          error={errors.email?.message}
+        />
+
+        <AuthInput
+          label="Password"
+          placeholder="Enter your password"
+          secureTextEntry
+          {...register('password')}
+          error={errors.password?.message}
+        />
+
+        <AuthInput
+          label="Confirm Password"
+          placeholder="Confirm your password"
+          secureTextEntry
+          {...register('confirmPassword')}
+          error={errors.confirmPassword?.message}
+        />
+
+        {error && <Text style={styles.errorText}>{error}</Text>}
+
+        <TouchableOpacity
+          style={[styles.button, { backgroundColor: Colors[colorScheme ?? 'light'].tint }]}
+          onPress={handleSubmit(onSubmit)}
+          disabled={isLoading}
+        >
+          <Text style={styles.buttonText}>
+            {isLoading ? 'Creating account...' : 'Sign Up'}
+          </Text>
+        </TouchableOpacity>
+
+        <View style={styles.links}>
+          <TouchableOpacity onPress={() => router.push('/login')}>
+            <Text style={[styles.link, { color: Colors[colorScheme ?? 'light'].tint }]}>
+              Already have an account? Login
+            </Text>
+          </TouchableOpacity>
+        </View>
+      </View>
+    </View>
+  );
+}
+
+const styles = StyleSheet.create({
+  container: {
+    flex: 1,
+    padding: 20,
+  },
+  form: {
+    flex: 1,
+    justifyContent: 'center',
+    maxWidth: 400,
+    width: '100%',
+    alignSelf: 'center',
+  },
+  title: {
+    fontSize: 32,
+    fontWeight: 'bold',
+    marginBottom: 32,
+    textAlign: 'center',
+  },
+  button: {
+    height: 48,
+    borderRadius: 8,
+    justifyContent: 'center',
+    alignItems: 'center',
+    marginTop: 16,
+  },
+  buttonText: {
+    color: '#fff',
+    fontSize: 16,
+    fontWeight: '600',
+  },
+  links: {
+    marginTop: 24,
+    alignItems: 'center',
+  },
+  link: {
+    fontSize: 16,
+    marginVertical: 8,
+  },
+  errorText: {
+    color: '#ff3b30',
+    fontSize: 14,
+    marginTop: 8,
+    textAlign: 'center',
+  },
+}); 
\ No newline at end of file
diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx
index 30914fb..b2e787a 100644
--- a/app/(tabs)/_layout.tsx
+++ b/app/(tabs)/_layout.tsx
@@ -1,11 +1,12 @@
 import React from 'react';
 import FontAwesome from '@expo/vector-icons/FontAwesome';
-import { Link, Tabs } from 'expo-router';
+import { Redirect, Tabs } from 'expo-router';
 import { Pressable } from 'react-native';
 
 import Colors from '@/constants/Colors';
 import { useColorScheme } from '@/components/useColorScheme';
 import { useClientOnlyValue } from '@/components/useClientOnlyValue';
+import { useAuthStore } from '@/store/auth';
 
 // You can explore the built-in icon families and icons on the web at https://icons.expo.fyi/
 function TabBarIcon(props: {
@@ -17,6 +18,12 @@ function TabBarIcon(props: {
 
 export default function TabLayout() {
   const colorScheme = useColorScheme();
+  const { isAuthenticated, logout } = useAuthStore();
+
+  // If the user is not authenticated, redirect to the auth flow
+  if (!isAuthenticated) {
+    return <Redirect href="/(auth)/login" />;
+  }
 
   return (
     <Tabs
@@ -32,18 +39,18 @@ export default function TabLayout() {
           title: 'Tab One',
           tabBarIcon: ({ color }) => <TabBarIcon name="code" color={color} />,
           headerRight: () => (
-            <Link href="/modal" asChild>
-              <Pressable>
-                {({ pressed }) => (
-                  <FontAwesome
-                    name="info-circle"
-                    size={25}
-                    color={Colors[colorScheme ?? 'light'].text}
-                    style={{ marginRight: 15, opacity: pressed ? 0.5 : 1 }}
-                  />
-                )}
-              </Pressable>
-            </Link>
+            <Pressable
+              onPress={logout}
+              style={({ pressed }) => ({
+                opacity: pressed ? 0.5 : 1,
+                marginRight: 15,
+              })}>
+              <FontAwesome
+                name="sign-out"
+                size={25}
+                color={Colors[colorScheme ?? 'light'].text}
+              />
+            </Pressable>
           ),
         }}
       />
diff --git a/app/_layout.tsx b/app/_layout.tsx
index 40cdc48..42a257c 100644
--- a/app/_layout.tsx
+++ b/app/_layout.tsx
@@ -1,12 +1,13 @@
 import FontAwesome from '@expo/vector-icons/FontAwesome';
 import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
 import { useFonts } from 'expo-font';
-import { Stack } from 'expo-router';
+import { Redirect, Slot, Stack } from 'expo-router';
 import * as SplashScreen from 'expo-splash-screen';
 import { useEffect } from 'react';
 import 'react-native-reanimated';
 
 import { useColorScheme } from '@/components/useColorScheme';
+import { useAuthStore } from '@/store/auth';
 
 export {
   // Catch any errors thrown by the Layout component.
@@ -47,10 +48,12 @@ export default function RootLayout() {
 
 function RootLayoutNav() {
   const colorScheme = useColorScheme();
+  const { isAuthenticated } = useAuthStore();
 
   return (
     <ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
       <Stack>
+        <Stack.Screen name="(auth)" options={{ headerShown: false }} />
         <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
         <Stack.Screen name="modal" options={{ presentation: 'modal' }} />
       </Stack>
diff --git a/components/auth/AuthInput.tsx b/components/auth/AuthInput.tsx
new file mode 100644
index 0000000..19611d2
--- /dev/null
+++ b/components/auth/AuthInput.tsx
@@ -0,0 +1,57 @@
+import React from 'react';
+import { TextInput, TextInputProps, View, Text, StyleSheet } from 'react-native';
+import { useColorScheme } from '@/components/useColorScheme';
+import Colors from '@/constants/Colors';
+
+interface AuthInputProps extends TextInputProps {
+  label: string;
+  error?: string;
+}
+
+export function AuthInput({ label, error, ...props }: AuthInputProps) {
+  const colorScheme = useColorScheme();
+
+  return (
+    <View style={styles.container}>
+      <Text style={[styles.label, { color: Colors[colorScheme ?? 'light'].text }]}>
+        {label}
+      </Text>
+      <TextInput
+        style={[
+          styles.input,
+          {
+            backgroundColor: Colors[colorScheme ?? 'light'].background,
+            color: Colors[colorScheme ?? 'light'].text,
+            borderColor: error ? '#ff3b30' : Colors[colorScheme ?? 'light'].text,
+          },
+        ]}
+        placeholderTextColor="#999"
+        {...props}
+      />
+      {error && <Text style={styles.errorText}>{error}</Text>}
+    </View>
+  );
+}
+
+const styles = StyleSheet.create({
+  container: {
+    marginBottom: 16,
+  },
+  label: {
+    fontSize: 16,
+    marginBottom: 8,
+    fontWeight: '500',
+  },
+  input: {
+    height: 48,
+    borderWidth: 1,
+    borderRadius: 8,
+    paddingHorizontal: 16,
+    fontSize: 16,
+  },
+  errorText: {
+    color: '#ff3b30',
+    fontSize: 14,
+    marginTop: 4,
+  },
+}); 
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 78b76b2..89a8f1a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,6 +16,7 @@
         "expo": "~52.0.40",
         "expo-font": "~13.0.4",
         "expo-linking": "~7.0.5",
+        "expo-local-authentication": "~13.8.0",
         "expo-router": "~4.0.19",
         "expo-secure-store": "~14.0.1",
         "expo-splash-screen": "~0.29.22",
@@ -8040,6 +8041,18 @@
         "react-native": "*"
       }
     },
+    "node_modules/expo-local-authentication": {
+      "version": "13.8.0",
+      "resolved": "https://registry.npmjs.org/expo-local-authentication/-/expo-local-authentication-13.8.0.tgz",
+      "integrity": "sha512-h0YA7grVdo3834AS70EUCsalaXrrEnoq+yTvIhRTxiPmzWxUv7rNo5ff+XsIEYNElKPmT/wh/xPV1yo3l3fhGg==",
+      "license": "MIT",
+      "dependencies": {
+        "invariant": "^2.2.4"
+      },
+      "peerDependencies": {
+        "expo": "*"
+      }
+    },
     "node_modules/expo-modules-autolinking": {
       "version": "2.0.8",
       "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-2.0.8.tgz",
diff --git a/package.json b/package.json
index 302d6d7..5f08166 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,7 @@
     "expo": "~52.0.40",
     "expo-font": "~13.0.4",
     "expo-linking": "~7.0.5",
+    "expo-local-authentication": "~13.8.0",
     "expo-router": "~4.0.19",
     "expo-secure-store": "~14.0.1",
     "expo-splash-screen": "~0.29.22",
diff --git a/schemas/auth.ts b/schemas/auth.ts
new file mode 100644
index 0000000..7cc1a4c
--- /dev/null
+++ b/schemas/auth.ts
@@ -0,0 +1,24 @@
+import { z } from 'zod';
+
+export const loginSchema = z.object({
+  email: z.string().email('Invalid email address'),
+  password: z.string().min(8, 'Password must be at least 8 characters'),
+});
+
+export const signupSchema = z.object({
+  name: z.string().min(2, 'Name must be at least 2 characters'),
+  email: z.string().email('Invalid email address'),
+  password: z.string().min(8, 'Password must be at least 8 characters'),
+  confirmPassword: z.string(),
+}).refine((data) => data.password === data.confirmPassword, {
+  message: "Passwords don't match",
+  path: ["confirmPassword"],
+});
+
+export const resetPasswordSchema = z.object({
+  email: z.string().email('Invalid email address'),
+});
+
+export type LoginFormData = z.infer<typeof loginSchema>;
+export type SignupFormData = z.infer<typeof signupSchema>;
+export type ResetPasswordFormData = z.infer<typeof resetPasswordSchema>; 
\ No newline at end of file
diff --git a/store/auth.ts b/store/auth.ts
new file mode 100644
index 0000000..ed6c629
--- /dev/null
+++ b/store/auth.ts
@@ -0,0 +1,209 @@
+import { create } from 'zustand';
+import * as SecureStore from 'expo-secure-store';
+import * as LocalAuthentication from 'expo-local-authentication';
+
+interface User {
+  id: string;
+  email: string;
+  name: string;
+}
+
+interface AuthState {
+  user: User | null;
+  token: string | null;
+  isLoading: boolean;
+  error: string | null;
+  isAuthenticated: boolean;
+  login: (email: string, password: string) => Promise<void>;
+  signup: (email: string, password: string, name: string) => Promise<void>;
+  logout: () => Promise<void>;
+  resetPassword: (email: string) => Promise<void>;
+  authenticateWithBiometrics: () => Promise<boolean>;
+  validateToken: () => Promise<boolean>;
+  refreshToken: () => Promise<void>;
+}
+
+const TOKEN_KEY = 'auth_token';
+const USER_KEY = 'user_data';
+const TOKEN_EXPIRY_KEY = 'token_expiry';
+
+// Test credentials for development
+const TEST_USER = {
+  email: 'test@example.com',
+  password: 'password123',
+  name: 'Test User',
+};
+
+// Token expiration time (24 hours)
+const TOKEN_EXPIRY = 24 * 60 * 60 * 1000;
+
+export const useAuthStore = create<AuthState>((set, get) => ({
+  user: null,
+  token: null,
+  isLoading: false,
+  error: null,
+  isAuthenticated: false,
+
+  validateToken: async () => {
+    try {
+      const token = await SecureStore.getItemAsync(TOKEN_KEY);
+      const expiry = await SecureStore.getItemAsync(TOKEN_EXPIRY_KEY);
+      
+      if (!token || !expiry) {
+        return false;
+      }
+
+      const expiryTime = parseInt(expiry);
+      if (Date.now() > expiryTime) {
+        // Token expired, try to refresh
+        await get().refreshToken();
+        return true;
+      }
+
+      return true;
+    } catch (error) {
+      console.error('Token validation error:', error);
+      return false;
+    }
+  },
+
+  refreshToken: async () => {
+    try {
+      // TODO: Implement actual token refresh API call
+      const newToken = 'test_token_' + Date.now();
+      const expiryTime = Date.now() + TOKEN_EXPIRY;
+
+      await SecureStore.setItemAsync(TOKEN_KEY, newToken);
+      await SecureStore.setItemAsync(TOKEN_EXPIRY_KEY, expiryTime.toString());
+
+      set({ token: newToken });
+    } catch (error) {
+      console.error('Token refresh error:', error);
+      await get().logout();
+    }
+  },
+
+  login: async (email: string, password: string) => {
+    try {
+      set({ isLoading: true, error: null });
+      
+      // For development: Check against test credentials
+      if (email === TEST_USER.email && password === TEST_USER.password) {
+        const user = {
+          id: '1',
+          email: TEST_USER.email,
+          name: TEST_USER.name,
+        };
+        const token = 'test_token_' + Date.now();
+        const expiryTime = Date.now() + TOKEN_EXPIRY;
+
+        await SecureStore.setItemAsync(TOKEN_KEY, token);
+        await SecureStore.setItemAsync(USER_KEY, JSON.stringify(user));
+        await SecureStore.setItemAsync(TOKEN_EXPIRY_KEY, expiryTime.toString());
+
+        set({
+          user,
+          token,
+          isAuthenticated: true,
+          isLoading: false,
+        });
+      } else {
+        throw new Error('Invalid email or password');
+      }
+    } catch (error) {
+      set({
+        error: error instanceof Error ? error.message : 'Login failed',
+        isLoading: false,
+      });
+    }
+  },
+
+  signup: async (email: string, password: string, name: string) => {
+    try {
+      set({ isLoading: true, error: null });
+      
+      // For development: Create a new user
+      const user = {
+        id: Date.now().toString(),
+        email,
+        name,
+      };
+      const token = 'test_token_' + Date.now();
+      const expiryTime = Date.now() + TOKEN_EXPIRY;
+
+      await SecureStore.setItemAsync(TOKEN_KEY, token);
+      await SecureStore.setItemAsync(USER_KEY, JSON.stringify(user));
+      await SecureStore.setItemAsync(TOKEN_EXPIRY_KEY, expiryTime.toString());
+
+      set({
+        user,
+        token,
+        isAuthenticated: true,
+        isLoading: false,
+      });
+    } catch (error) {
+      set({
+        error: error instanceof Error ? error.message : 'Signup failed',
+        isLoading: false,
+      });
+    }
+  },
+
+  logout: async () => {
+    try {
+      await SecureStore.deleteItemAsync(TOKEN_KEY);
+      await SecureStore.deleteItemAsync(USER_KEY);
+      await SecureStore.deleteItemAsync(TOKEN_EXPIRY_KEY);
+      
+      set({
+        user: null,
+        token: null,
+        isAuthenticated: false,
+      });
+    } catch (error) {
+      console.error('Logout error:', error);
+    }
+  },
+
+  resetPassword: async (email: string) => {
+    try {
+      set({ isLoading: true, error: null });
+      
+      // For development: Simulate password reset
+      if (email === TEST_USER.email) {
+        set({ isLoading: false });
+      } else {
+        throw new Error('Email not found');
+      }
+    } catch (error) {
+      set({
+        error: error instanceof Error ? error.message : 'Password reset failed',
+        isLoading: false,
+      });
+    }
+  },
+
+  authenticateWithBiometrics: async () => {
+    try {
+      const hasHardware = await LocalAuthentication.hasHardwareAsync();
+      if (!hasHardware) {
+        throw new Error('Biometric authentication is not available');
+      }
+
+      const isEnrolled = await LocalAuthentication.isEnrolledAsync();
+      if (!isEnrolled) {
+        throw new Error('No biometrics enrolled');
+      }
+
+      const result = await LocalAuthentication.authenticateAsync({
+        promptMessage: 'Authenticate to access your account',
+        fallbackLabel: 'Use password',
+      });
+
+      return result.success;
+    } catch (error) {
+      console.error('Biometric authentication error:', error);
+      return false;
+    }
+  },
+})); 
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index 909e901..4e1e615 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -2,6 +2,8 @@
   "extends": "expo/tsconfig.base",
   "compilerOptions": {
     "strict": true,
+    "jsx": "react-native",
+    "esModuleInterop": true,
     "paths": {
       "@/*": [
         "./*"


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 10**

<details>
<summary>🧹 Nitpick comments (22)</summary><blockquote>

<details>
<summary>app/(auth)/_layout.tsx (2)</summary><blockquote>

`8-17`: **Improve effect dependencies for token validation**

The token validation logic is correct, but the `useEffect` dependency array is empty, which could lead to stale closures if `validateToken` function reference changes.


```diff
-  }, []);
+  }, [validateToken]);
```

---

`8-17`: **Consider explicit handling for token validation failure**

The code correctly handles the case when the token is valid, but there's no explicit handling for token validation failure beyond not redirecting.


```diff
  useEffect(() => {
    const checkAuth = async () => {
      const isValid = await validateToken();
      if (isValid) {
        // Token is valid, user is authenticated
        return;
+     } else {
+       // Consider showing a notification or taking other actions on invalid token
+       console.log('Token validation failed');
      }
    };
    checkAuth();
  }, [validateToken]);
```

</blockquote></details>
<details>
<summary>app/(auth)/signup.tsx (3)</summary><blockquote>

`67-67`: **Distinguish between API errors and form validation errors**

Currently, all errors are displayed in the same way. Consider adding context to distinguish between API errors and form validation errors for better user experience.


```diff
- {error && <Text style={styles.errorText}>{error}</Text>}
+ {error && (
+   <View style={styles.errorContainer}>
+     <Text style={styles.errorTitle}>Error connecting to server:</Text>
+     <Text style={styles.errorText}>{error}</Text>
+   </View>
+ )}
```

Add corresponding styles:

```diff
  errorText: {
    color: '#ff3b30',
    fontSize: 14,
    marginTop: 8,
    textAlign: 'center',
  },
+ errorContainer: {
+   backgroundColor: 'rgba(255, 59, 48, 0.1)',
+   padding: 12,
+   borderRadius: 8,
+   marginTop: 16,
+ },
+ errorTitle: {
+   color: '#ff3b30',
+   fontWeight: 'bold',
+   fontSize: 14,
+   marginBottom: 4,
+   textAlign: 'center',
+ },
```

---

`69-77`: **Add success feedback for user**

Currently, there's no feedback to the user after successful signup before redirection. Consider adding a success message or visual indicator.


```diff
  const onSubmit = async (data: SignupFormData) => {
-   await signup(data.email, data.password, data.name);
+   const success = await signup(data.email, data.password, data.name);
+   if (success) {
+     // Show success message before redirect
+     setSubmitSuccess(true);
+   }
  };

+ const [submitSuccess, setSubmitSuccess] = useState(false);

  return (
    <View style={[styles.container, { backgroundColor: Colors[colorScheme ?? 'light'].background }]}>
      <View style={styles.form}>
        {/* ... form fields ... */}

        {error && <Text style={styles.errorText}>{error}</Text>}
+       {submitSuccess && (
+         <Text style={styles.successText}>Account created successfully! Redirecting...</Text>
+       )}

        <TouchableOpacity
          style={[styles.button, { backgroundColor: Colors[colorScheme ?? 'light'].tint }]}
          onPress={handleSubmit(onSubmit)}
-         disabled={isLoading}
+         disabled={isLoading || submitSuccess}
        >
```

And add the success text style:

```diff
+ successText: {
+   color: '#34c759',
+   fontSize: 14,
+   marginTop: 8,
+   textAlign: 'center',
+ },
```

---

`12-89`: **Add keyboard handling for improved UX**

The form doesn't include keyboard handling, which would improve the user experience, especially on mobile devices.


Wrap the form in a KeyboardAvoidingView and add keyboard dismissal:

```diff
+ import { View, StyleSheet, TouchableOpacity, Text, KeyboardAvoidingView, Platform, TouchableWithoutFeedback, Keyboard } from 'react-native';

  return (
    <View style={[styles.container, { backgroundColor: Colors[colorScheme ?? 'light'].background }]}>
+     <KeyboardAvoidingView
+       behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
+       style={{ flex: 1 }}
+     >
+       <TouchableWithoutFeedback onPress={Keyboard.dismiss}>
          <View style={styles.form}>
            {/* form contents */}
          </View>
+       </TouchableWithoutFeedback>
+     </KeyboardAvoidingView>
    </View>
  );
```

</blockquote></details>
<details>
<summary>app/_layout.tsx (2)</summary><blockquote>

`4-4`: **Verify imports are properly used**

The import statement now includes `Redirect` and `Slot` from expo-router, but `Slot` doesn't appear to be used in this file. Consider removing unused imports to keep the codebase clean.

```diff
-import { Redirect, Slot, Stack } from 'expo-router';
+import { Redirect, Stack } from 'expo-router';
```

---

`10-10`: **Implement auth state initialization**

The `isAuthenticated` state is being retrieved from the auth store, but it's not being used in this file. Consider implementing an effect to initialize the auth state on app load.

```diff
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
import { useFonts } from 'expo-font';
import { Redirect, Stack } from 'expo-router';
import * as SplashScreen from 'expo-splash-screen';
-import { useEffect } from 'react';
+import { useEffect, useState } from 'react';
import 'react-native-reanimated';

import { useColorScheme } from '@/components/useColorScheme';
import { useAuthStore } from '@/store/auth';

// ...

function RootLayoutNav() {
  const colorScheme = useColorScheme();
  const { isAuthenticated } = useAuthStore();
+  const [isInitialized, setIsInitialized] = useState(false);
+
+  useEffect(() => {
+    const initAuth = async () => {
+      try {
+        await useAuthStore.getState().validateToken();
+        setIsInitialized(true);
+      } catch (error) {
+        console.error('Failed to initialize auth:', error);
+        setIsInitialized(true);
+      }
+    };
+
+    initAuth();
+  }, []);
+
+  if (!isInitialized) {
+    return null; // Or a loading indicator
+  }

  return (
    // ...
  );
}
```


Also applies to: 51-51

</blockquote></details>
<details>
<summary>app/(tabs)/_layout.tsx (2)</summary><blockquote>

`21-26`: **Add loading state during authentication check**

The redirect logic correctly handles unauthenticated users, but there's no loading state while the authentication status is being determined, which could lead to flash of content.

```diff
export default function TabLayout() {
  const colorScheme = useColorScheme();
-  const { isAuthenticated, logout } = useAuthStore();
+  const { isAuthenticated, logout, isLoading } = useAuthStore();

+  // Show loading indicator while auth state is being determined
+  if (isLoading) {
+    return (
+      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
+        <ActivityIndicator size="large" color={Colors[colorScheme ?? 'light'].tint} />
+      </View>
+    );
+  }

  // If the user is not authenticated, redirect to the auth flow
  if (!isAuthenticated) {
    return <Redirect href="/(auth)/login" />;
  }
```

Don't forget to add the imports:

```diff
import React from 'react';
import FontAwesome from '@expo/vector-icons/FontAwesome';
import { Redirect, Tabs } from 'expo-router';
-import { Pressable } from 'react-native';
+import { Pressable, View, ActivityIndicator } from 'react-native';
```

---

`42-53`: **Add confirmation dialog for logout**

The logout function is triggered immediately upon pressing the button. Consider adding a confirmation dialog to prevent accidental logouts.

```diff
import React from 'react';
import FontAwesome from '@expo/vector-icons/FontAwesome';
import { Redirect, Tabs } from 'expo-router';
-import { Pressable } from 'react-native';
+import { Pressable, Alert } from 'react-native';

// ...

headerRight: () => (
  <Pressable
-    onPress={logout}
+    onPress={() => {
+      Alert.alert(
+        "Logout",
+        "Are you sure you want to logout?",
+        [
+          { text: "Cancel", style: "cancel" },
+          { 
+            text: "Logout", 
+            onPress: async () => {
+              await logout();
+            },
+            style: "destructive"
+          }
+        ]
+      );
+    }}
    style={({ pressed }) => ({
      opacity: pressed ? 0.5 : 1,
      marginRight: 15,
    })}>
    <FontAwesome
      name="sign-out"
      size={25}
      color={Colors[colorScheme ?? 'light'].text}
    />
  </Pressable>
),
```

</blockquote></details>
<details>
<summary>schemas/auth.ts (2)</summary><blockquote>

`3-6`: **Enhance password validation rules**

The current password validation only checks for minimum length. Consider adding more robust password requirements for better security.

```diff
export const loginSchema = z.object({
  email: z.string().email('Invalid email address'),
-  password: z.string().min(8, 'Password must be at least 8 characters'),
+  password: z.string()
+    .min(8, 'Password must be at least 8 characters')
+    .regex(/[A-Z]/, 'Password must contain at least one uppercase letter')
+    .regex(/[0-9]/, 'Password must contain at least one number')
+    .regex(/[^A-Za-z0-9]/, 'Password must contain at least one special character'),
});
```

---

`8-16`: **Consider additional validation for name field and DRY approach**

The name field could benefit from more specific validation rules. Also, the password validation is duplicated between schemas which violates DRY principles.

```diff
+// Define reusable password schema
+const passwordSchema = z.string()
+  .min(8, 'Password must be at least 8 characters')
+  .regex(/[A-Z]/, 'Password must contain at least one uppercase letter')
+  .regex(/[0-9]/, 'Password must contain at least one number')
+  .regex(/[^A-Za-z0-9]/, 'Password must contain at least one special character');

export const loginSchema = z.object({
  email: z.string().email('Invalid email address'),
-  password: z.string().min(8, 'Password must be at least 8 characters'),
+  password: passwordSchema,
});

export const signupSchema = z.object({
-  name: z.string().min(2, 'Name must be at least 2 characters'),
+  name: z.string()
+    .min(2, 'Name must be at least 2 characters')
+    .max(50, 'Name cannot exceed 50 characters')
+    .regex(/^[a-zA-Z\s'-]+$/, 'Name can only contain letters, spaces, hyphens and apostrophes'),
  email: z.string().email('Invalid email address'),
-  password: z.string().min(8, 'Password must be at least 8 characters'),
+  password: passwordSchema,
  confirmPassword: z.string(),
}).refine((data) => data.password === data.confirmPassword, {
  message: "Passwords don't match",
  path: ["confirmPassword"],
});
```

</blockquote></details>
<details>
<summary>app/(auth)/forgot-password.tsx (3)</summary><blockquote>

`38-45`: **Improve keyboard behavior for better UX**

Add keyboard handling to improve user experience when typing on mobile devices.

```diff
import React from 'react';
-import { View, StyleSheet, TouchableOpacity, Text } from 'react-native';
+import { View, StyleSheet, TouchableOpacity, Text, KeyboardAvoidingView, Platform, TouchableWithoutFeedback, Keyboard } from 'react-native';

// ...

return (
-  <View style={[styles.container, { backgroundColor: Colors[colorScheme ?? 'light'].background }]}>
-    <View style={styles.form}>
+  <KeyboardAvoidingView
+    behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
+    style={[styles.container, { backgroundColor: Colors[colorScheme ?? 'light'].background }]}>
+    <TouchableWithoutFeedback onPress={Keyboard.dismiss}>
+      <View style={styles.form}>

// ... existing component content ...

-      </View>
-    </View>
+      </View>
+    </TouchableWithoutFeedback>
+  </KeyboardAvoidingView>
);
```

---

`102-106`: **Use theme colors for buttonText instead of hardcoded color**

Use the theme colors from the Colors constant to ensure consistency with the app's theme and dark mode support.

```diff
  buttonText: {
-    color: '#fff',
+    color: '#ffffff', // Use white consistently or preferably a theme variable
    fontSize: 16,
    fontWeight: '600',
  },
```

---

`115-120`: **Use theme colors for errorText instead of hardcoded color**

Use the theme colors from the Colors constant to ensure consistency with the app's theme.

```diff
  errorText: {
-    color: '#ff3b30',
+    color: Colors.light.error || '#ff3b30', // Consider adding error color to your Colors theme
    fontSize: 14,
    marginTop: 8,
    textAlign: 'center',
  },
```

</blockquote></details>
<details>
<summary>components/auth/AuthInput.tsx (4)</summary><blockquote>

`23-26`: **Extract hardcoded colors to constants**

The error border color '#ff3b30' is hardcoded. Consider using a color from your Colors constants for better maintainability and theme consistency.

```diff
- borderColor: error ? '#ff3b30' : Colors[colorScheme ?? 'light'].text,
+ borderColor: error ? Colors[colorScheme ?? 'light'].error : Colors[colorScheme ?? 'light'].text,
```

---

`28-28`: **Use theme color for placeholder text**

The placeholder text color is hardcoded as '#999'. For better theme consistency, consider using a color from your Colors constants.

```diff
- placeholderTextColor="#999"
+ placeholderTextColor={Colors[colorScheme ?? 'light'].placeholderText}
```

---

`53-54`: **Extract error text color to constants**

The error text color is hardcoded as '#ff3b30'. Consider using a color from your Colors constants for consistency with other error states.

```diff
- color: '#ff3b30',
+ color: Colors[colorScheme ?? 'light'].error,
```

---

`19-30`: **Add accessibility properties to input**

Enhance accessibility by adding appropriate accessibility properties to the TextInput component.

```diff
 <TextInput
   style={[
     styles.input,
     {
       backgroundColor: Colors[colorScheme ?? 'light'].background,
       color: Colors[colorScheme ?? 'light'].text,
       borderColor: error ? '#ff3b30' : Colors[colorScheme ?? 'light'].text,
     },
   ]}
   placeholderTextColor="#999"
+  accessibilityLabel={label}
+  accessibilityHint={error ? `Error: ${error}` : undefined}
   {...props}
 />
```

</blockquote></details>
<details>
<summary>app/(auth)/login.tsx (4)</summary><blockquote>

`80-88`: **Add accessibility properties to login button**

Enhance accessibility by adding appropriate accessibility properties to the login button.

```diff
 <TouchableOpacity
   style={[styles.button, { backgroundColor: Colors[colorScheme ?? 'light'].tint }]}
   onPress={handleSubmit(onSubmit)}
   disabled={isLoading}
+  accessibilityLabel="Login button"
+  accessibilityHint="Press to log in with your email and password"
+  accessibilityRole="button"
 >
   <Text style={styles.buttonText}>
     {isLoading ? 'Logging in...' : 'Login'}
   </Text>
 </TouchableOpacity>
```

---

`90-97`: **Add accessibility properties to biometric login button**

Enhance accessibility by adding appropriate accessibility properties to the biometric login button.

```diff
 <TouchableOpacity
   style={styles.biometricButton}
   onPress={handleBiometricAuth}
+  accessibilityLabel="Biometric login button"
+  accessibilityHint="Press to log in using biometric authentication"
+  accessibilityRole="button"
 >
   <Text style={[styles.biometricText, { color: Colors[colorScheme ?? 'light'].tint }]}>
     Login with Biometrics
   </Text>
 </TouchableOpacity>
```

---

`143-144`: **Extract button text color to constants**

The button text color is hardcoded as '#fff'. For better theme consistency, consider using a color from your Colors constants.

```diff
- color: '#fff',
+ color: Colors[colorScheme ?? 'light'].buttonText,
```

---

`163-164`: **Extract error text color to constants**

The error text color is hardcoded as '#ff3b30'. Consider using a color from your Colors constants for consistency.

```diff
- color: '#ff3b30',
+ color: Colors[colorScheme ?? 'light'].error,
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between fa428a6d12d3b40e4b1626f61ce3792a48690ee9 and 292558bcf056a061f021234e1492e69eed600bbe.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `package-lock.json` is excluded by `!**/package-lock.json`

</details>

<details>
<summary>📒 Files selected for processing (11)</summary>

* `app/(auth)/_layout.tsx` (1 hunks)
* `app/(auth)/forgot-password.tsx` (1 hunks)
* `app/(auth)/login.tsx` (1 hunks)
* `app/(auth)/signup.tsx` (1 hunks)
* `app/(tabs)/_layout.tsx` (3 hunks)
* `app/_layout.tsx` (2 hunks)
* `components/auth/AuthInput.tsx` (1 hunks)
* `package.json` (1 hunks)
* `schemas/auth.ts` (1 hunks)
* `store/auth.ts` (1 hunks)
* `tsconfig.json` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Definitions (3)</summary>

<details>
<summary>app/(auth)/signup.tsx (3)</summary><blockquote>

<details>
<summary>store/auth.ts (1)</summary>

* `useAuthStore` (40-209)

</details>
<details>
<summary>schemas/auth.ts (2)</summary>

* `SignupFormData` (23-23)
* `signupSchema` (8-16)

</details>
<details>
<summary>components/auth/AuthInput.tsx (1)</summary>

* `AuthInput` (11-34)

</details>

</blockquote></details>
<details>
<summary>app/_layout.tsx (1)</summary><blockquote>

<details>
<summary>store/auth.ts (1)</summary>

* `useAuthStore` (40-209)

</details>

</blockquote></details>
<details>
<summary>app/(tabs)/_layout.tsx (1)</summary><blockquote>

<details>
<summary>store/auth.ts (1)</summary>

* `useAuthStore` (40-209)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>🔇 Additional comments (5)</summary><blockquote>

<details>
<summary>tsconfig.json (1)</summary>

`5-6`: **Configuration looks appropriate for React Native development**

The added TypeScript compiler options are well-suited for React Native development:
- `"jsx": "react-native"` ensures JSX is transformed correctly for React Native applications
- `"esModuleInterop": true` enables CommonJS module compatibility for smooth importing

These settings will properly support the React Native components being added in this PR.

</details>
<details>
<summary>package.json (1)</summary>

`26-26`: **Good addition of biometric authentication dependency**

Adding `expo-local-authentication` is appropriate for implementing the biometric authentication feature mentioned in the PR objectives. The version is consistent with other Expo packages in the project.

</details>
<details>
<summary>app/(auth)/_layout.tsx (1)</summary>

`24-47`: **Stack navigation setup looks good**

The Stack component with screen configuration is well-structured, with appropriate titles and consistent header visibility settings across all authentication screens.

</details>
<details>
<summary>app/(auth)/signup.tsx (1)</summary>

`91-135`: **Style implementation is well-structured**

The styles are well-organized and provide a responsive layout with appropriate spacing and typography. The use of dynamic colors based on the color scheme is a good practice for supporting both light and dark modes.

</details>
<details>
<summary>app/_layout.tsx (1)</summary>

`56-56`: **Ensure proper navigation stack order**

The auth screen is now positioned before the tabs screen in the stack. This is appropriate for the authentication flow, but make sure to verify that this ordering doesn't create unintended behavior with navigation history or animations.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T11:51:44Z

Review comments:
diff_hunk: @@ -0,0 +1,135 @@
+import React from 'react';
+import { View, StyleSheet, TouchableOpacity, Text } from 'react-native';
+import { useForm } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { router } from 'expo-router';
+import { useColorScheme } from '@/components/useColorScheme';
+import Colors from '@/constants/Colors';
+import { AuthInput } from '@/components/auth/AuthInput';
+import { signupSchema, type SignupFormData } from '@/schemas/auth';
+import { useAuthStore } from '@/store/auth';
+
+export default function SignupScreen() {
+  const colorScheme = useColorScheme();
+  const { signup, isLoading, error } = useAuthStore();
+  const {
+    register,
+    handleSubmit,
+    formState: { errors },
+  } = useForm<SignupFormData>({
+    resolver: zodResolver(signupSchema),
+  });
+
+  const onSubmit = async (data: SignupFormData) => {
+    await signup(data.email, data.password, data.name);
+  };
+
+  return (
+    <View style={[styles.container, { backgroundColor: Colors[colorScheme ?? 'light'].background }]}>
+      <View style={styles.form}>
+        <Text style={[styles.title, { color: Colors[colorScheme ?? 'light'].text }]}>
+          Create Account
+        </Text>
+        
+        <AuthInput
+          label="Name"
+          placeholder="Enter your name"
+          autoCapitalize="words"
+          {...register('name')}
+          error={errors.name?.message}
+        />
+
+        <AuthInput
+          label="Email"
+          placeholder="Enter your email"
+          keyboardType="email-address"
+          autoCapitalize="none"
+          {...register('email')}
+          error={errors.email?.message}
+        />
+
+        <AuthInput
+          label="Password"
+          placeholder="Enter your password"
+          secureTextEntry
+          {...register('password')}
+          error={errors.password?.message}
+        />
+
+        <AuthInput
+          label="Confirm Password"
+          placeholder="Confirm your password"
+          secureTextEntry
+          {...register('confirmPassword')}
+          error={errors.confirmPassword?.message}
+        />
path: app/(auth)/signup.tsx
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Improve React Hook Form integration with React Native**

React Native's TextInput doesn't directly support the spread of `register()`. This implementation might not correctly bind the form inputs to React Hook Form.


Consider using the `Controller` component instead:

```diff
- <AuthInput
-   label="Name"
-   placeholder="Enter your name"
-   autoCapitalize="words"
-   {...register('name')}
-   error={errors.name?.message}
- />
+ <Controller
+   control={control}
+   name="name"
+   render={({ field: { onChange, onBlur, value } }) => (
+     <AuthInput
+       label="Name"
+       placeholder="Enter your name"
+       autoCapitalize="words"
+       onChangeText={onChange}
+       onBlur={onBlur}
+       value={value}
+       error={errors.name?.message}
+     />
+   )}
+ />
```

Don't forget to add `control` to your form hook:

```diff
  const {
    register,
    handleSubmit,
+   control,
    formState: { errors },
  } = useForm<SignupFormData>({
    resolver: zodResolver(signupSchema),
  });
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
// In your form hook initialization (e.g., near the top of your component file)
const {
  register,
  handleSubmit,
  control,
  formState: { errors },
} = useForm<SignupFormData>({
  resolver: zodResolver(signupSchema),
});

// ... Within your component's return statement:

<Controller
  control={control}
  name="name"
  render={({ field: { onChange, onBlur, value } }) => (
    <AuthInput
      label="Name"
      placeholder="Enter your name"
      autoCapitalize="words"
      onChangeText={onChange}
      onBlur={onBlur}
      value={value}
      error={errors.name?.message}
    />
  )}
/>

<AuthInput
  label="Email"
  placeholder="Enter your email"
  keyboardType="email-address"
  autoCapitalize="none"
  {...register('email')}
  error={errors.email?.message}
/>

<AuthInput
  label="Password"
  placeholder="Enter your password"
  secureTextEntry
  {...register('password')}
  error={errors.password?.message}
/>

<AuthInput
  label="Confirm Password"
  placeholder="Confirm your password"
  secureTextEntry
  {...register('confirmPassword')}
  error={errors.confirmPassword?.message}
/>
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-26T11:51:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,121 @@
+import React from 'react';
+import { View, StyleSheet, TouchableOpacity, Text } from 'react-native';
+import { useForm } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { router } from 'expo-router';
+import { useColorScheme } from '@/components/useColorScheme';
+import Colors from '@/constants/Colors';
+import { AuthInput } from '@/components/auth/AuthInput';
+import { resetPasswordSchema, type ResetPasswordFormData } from '@/schemas/auth';
+import { useAuthStore } from '@/store/auth';
+
+export default function ForgotPasswordScreen() {
+  const colorScheme = useColorScheme();
+  const { resetPassword, isLoading, error } = useAuthStore();
+  const {
+    register,
+    handleSubmit,
+    formState: { errors },
+  } = useForm<ResetPasswordFormData>({
+    resolver: zodResolver(resetPasswordSchema),
+  });
+
+  const onSubmit = async (data: ResetPasswordFormData) => {
+    await resetPassword(data.email);
+  };
path: app/(auth)/forgot-password.tsx
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Add success handling to the onSubmit function**

The password reset function doesn't handle the success state. Add navigation or a success message after successful reset.

```diff
const onSubmit = async (data: ResetPasswordFormData) => {
  await resetPassword(data.email);
+  // Check if there's no error after reset
+  if (!useAuthStore.getState().error) {
+    // Show success message
+    Alert.alert(
+      "Password Reset",
+      "If an account exists with this email, you will receive instructions to reset your password.",
+      [{ text: "OK", onPress: () => router.push('/login') }]
+    );
+  }
};
```

Don't forget to add the Alert import:

```diff
import React from 'react';
-import { View, StyleSheet, TouchableOpacity, Text } from 'react-native';
+import { View, StyleSheet, TouchableOpacity, Text, Alert } from 'react-native';
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
import React from 'react';
import { View, StyleSheet, TouchableOpacity, Text, Alert } from 'react-native';

  const onSubmit = async (data: ResetPasswordFormData) => {
    await resetPassword(data.email);
    // Check if there's no error after reset
    if (!useAuthStore.getState().error) {
      // Show success message
      Alert.alert(
        "Password Reset",
        "If an account exists with this email, you will receive instructions to reset your password.",
        [{ text: "OK", onPress: () => router.push('/login') }]
      );
    }
  };
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-26T11:51:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,168 @@
+import React from 'react';
+import { View, StyleSheet, TouchableOpacity, Text } from 'react-native';
+import { useForm, Controller } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { router } from 'expo-router';
+import { useColorScheme } from '@/components/useColorScheme';
+import Colors from '@/constants/Colors';
+import { AuthInput } from '@/components/auth/AuthInput';
+import { loginSchema, type LoginFormData } from '@/schemas/auth';
+import { useAuthStore } from '@/store/auth';
+import * as LocalAuthentication from 'expo-local-authentication';
+
+export default function LoginScreen() {
+  const colorScheme = useColorScheme();
+  const { login, authenticateWithBiometrics, isLoading, error } = useAuthStore();
+  const {
+    control,
+    handleSubmit,
+    formState: { errors },
+  } = useForm<LoginFormData>({
+    resolver: zodResolver(loginSchema),
+    defaultValues: {
+      email: '',
+      password: '',
+    },
+  });
+
+  const onSubmit = async (data: LoginFormData) => {
+    await login(data.email, data.password);
+  };
+
+  const handleBiometricAuth = async () => {
+    const success = await authenticateWithBiometrics();
+    if (success) {
+      // TODO: Implement biometric login logic
+      console.log('Biometric authentication successful');
+    }
+  };
path: app/(auth)/login.tsx
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Implement biometric authentication logic**

The biometric authentication function only logs success to the console. Implement the actual authentication logic as indicated by the TODO comment.

```diff
 const handleBiometricAuth = async () => {
   const success = await authenticateWithBiometrics();
   if (success) {
-    // TODO: Implement biometric login logic
-    console.log('Biometric authentication successful');
+    // Automatically login with stored credentials if biometric authentication succeeds
+    try {
+      // Retrieve stored credentials from secure storage or use the test credentials
+      const email = await SecureStore.getItemAsync('biometric_email') || 'test@example.com';
+      const password = await SecureStore.getItemAsync('biometric_password') || 'password123';
+      await login(email, password);
+    } catch (error) {
+      console.error('Biometric login error:', error);
+    }
   }
 };
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
  const handleBiometricAuth = async () => {
    const success = await authenticateWithBiometrics();
    if (success) {
      // Automatically login with stored credentials if biometric authentication succeeds
      try {
        // Retrieve stored credentials from secure storage or use the test credentials
        const email = await SecureStore.getItemAsync('biometric_email') || 'test@example.com';
        const password = await SecureStore.getItemAsync('biometric_password') || 'password123';
        await login(email, password);
      } catch (error) {
        console.error('Biometric login error:', error);
      }
    }
  };
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-26T11:51:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,209 @@
+import { create } from 'zustand';
+import * as SecureStore from 'expo-secure-store';
+import * as LocalAuthentication from 'expo-local-authentication';
+
+interface User {
+  id: string;
+  email: string;
+  name: string;
+}
+
+interface AuthState {
+  user: User | null;
+  token: string | null;
+  isLoading: boolean;
+  error: string | null;
+  isAuthenticated: boolean;
+  login: (email: string, password: string) => Promise<void>;
+  signup: (email: string, password: string, name: string) => Promise<void>;
+  logout: () => Promise<void>;
+  resetPassword: (email: string) => Promise<void>;
+  authenticateWithBiometrics: () => Promise<boolean>;
+  validateToken: () => Promise<boolean>;
+  refreshToken: () => Promise<void>;
+}
+
+const TOKEN_KEY = 'auth_token';
+const USER_KEY = 'user_data';
+const TOKEN_EXPIRY_KEY = 'token_expiry';
+
+// Test credentials for development
+const TEST_USER = {
+  email: 'test@example.com',
+  password: 'password123',
+  name: 'Test User',
+};
+
+// Token expiration time (24 hours)
+const TOKEN_EXPIRY = 24 * 60 * 60 * 1000;
+
+export const useAuthStore = create<AuthState>((set, get) => ({
+  user: null,
+  token: null,
+  isLoading: false,
+  error: null,
+  isAuthenticated: false,
+
+  validateToken: async () => {
+    try {
+      const token = await SecureStore.getItemAsync(TOKEN_KEY);
+      const expiry = await SecureStore.getItemAsync(TOKEN_EXPIRY_KEY);
+      
+      if (!token || !expiry) {
+        return false;
+      }
+
+      const expiryTime = parseInt(expiry);
+      if (Date.now() > expiryTime) {
+        // Token expired, try to refresh
+        await get().refreshToken();
+        return true;
+      }
+
+      return true;
+    } catch (error) {
+      console.error('Token validation error:', error);
+      return false;
+    }
+  },
path: store/auth.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Verify token refresh success before returning true**

In the validateToken method, when a token is expired, the code attempts to refresh it but returns true without verifying if the refresh was successful.

```diff
 validateToken: async () => {
   try {
     const token = await SecureStore.getItemAsync(TOKEN_KEY);
     const expiry = await SecureStore.getItemAsync(TOKEN_EXPIRY_KEY);
     
     if (!token || !expiry) {
       return false;
     }

     const expiryTime = parseInt(expiry);
     if (Date.now() > expiryTime) {
       // Token expired, try to refresh
-      await get().refreshToken();
-      return true;
+      try {
+        await get().refreshToken();
+        // Check if refresh was successful by verifying new token exists
+        const newToken = await SecureStore.getItemAsync(TOKEN_KEY);
+        return !!newToken;
+      } catch (error) {
+        console.error('Token refresh failed:', error);
+        return false;
+      }
     }

     return true;
   } catch (error) {
     console.error('Token validation error:', error);
     return false;
   }
 },
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
  validateToken: async () => {
    try {
      const token = await SecureStore.getItemAsync(TOKEN_KEY);
      const expiry = await SecureStore.getItemAsync(TOKEN_EXPIRY_KEY);
      
      if (!token || !expiry) {
        return false;
      }

      const expiryTime = parseInt(expiry);
      if (Date.now() > expiryTime) {
        // Token expired, try to refresh
        try {
          await get().refreshToken();
          // Check if refresh was successful by verifying new token exists
          const newToken = await SecureStore.getItemAsync(TOKEN_KEY);
          return !!newToken;
        } catch (error) {
          console.error('Token refresh failed:', error);
          return false;
        }
      }

      return true;
    } catch (error) {
      console.error('Token validation error:', error);
      return false;
    }
  },
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-26T11:51:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,209 @@
+import { create } from 'zustand';
+import * as SecureStore from 'expo-secure-store';
+import * as LocalAuthentication from 'expo-local-authentication';
+
+interface User {
+  id: string;
+  email: string;
+  name: string;
+}
+
+interface AuthState {
+  user: User | null;
+  token: string | null;
+  isLoading: boolean;
+  error: string | null;
+  isAuthenticated: boolean;
+  login: (email: string, password: string) => Promise<void>;
+  signup: (email: string, password: string, name: string) => Promise<void>;
+  logout: () => Promise<void>;
+  resetPassword: (email: string) => Promise<void>;
+  authenticateWithBiometrics: () => Promise<boolean>;
+  validateToken: () => Promise<boolean>;
+  refreshToken: () => Promise<void>;
+}
+
+const TOKEN_KEY = 'auth_token';
+const USER_KEY = 'user_data';
+const TOKEN_EXPIRY_KEY = 'token_expiry';
+
+// Test credentials for development
+const TEST_USER = {
+  email: 'test@example.com',
+  password: 'password123',
+  name: 'Test User',
+};
+
+// Token expiration time (24 hours)
+const TOKEN_EXPIRY = 24 * 60 * 60 * 1000;
+
+export const useAuthStore = create<AuthState>((set, get) => ({
+  user: null,
+  token: null,
+  isLoading: false,
+  error: null,
+  isAuthenticated: false,
+
+  validateToken: async () => {
+    try {
+      const token = await SecureStore.getItemAsync(TOKEN_KEY);
+      const expiry = await SecureStore.getItemAsync(TOKEN_EXPIRY_KEY);
+      
+      if (!token || !expiry) {
+        return false;
+      }
+
+      const expiryTime = parseInt(expiry);
+      if (Date.now() > expiryTime) {
+        // Token expired, try to refresh
+        await get().refreshToken();
+        return true;
+      }
+
+      return true;
+    } catch (error) {
+      console.error('Token validation error:', error);
+      return false;
+    }
+  },
+
+  refreshToken: async () => {
+    try {
+      // TODO: Implement actual token refresh API call
+      const newToken = 'test_token_' + Date.now();
+      const expiryTime = Date.now() + TOKEN_EXPIRY;
+
+      await SecureStore.setItemAsync(TOKEN_KEY, newToken);
+      await SecureStore.setItemAsync(TOKEN_EXPIRY_KEY, expiryTime.toString());
+
+      set({ token: newToken });
+    } catch (error) {
+      console.error('Token refresh error:', error);
+      await get().logout();
+    }
+  },
+
+  login: async (email: string, password: string) => {
+    try {
+      set({ isLoading: true, error: null });
+      
+      // For development: Check against test credentials
+      if (email === TEST_USER.email && password === TEST_USER.password) {
+        const user = {
+          id: '1',
+          email: TEST_USER.email,
+          name: TEST_USER.name,
+        };
+        const token = 'test_token_' + Date.now();
+        const expiryTime = Date.now() + TOKEN_EXPIRY;
+
+        await SecureStore.setItemAsync(TOKEN_KEY, token);
+        await SecureStore.setItemAsync(USER_KEY, JSON.stringify(user));
+        await SecureStore.setItemAsync(TOKEN_EXPIRY_KEY, expiryTime.toString());
+
+        set({
+          user,
+          token,
+          isAuthenticated: true,
+          isLoading: false,
+        });
+      } else {
+        throw new Error('Invalid email or password');
+      }
+    } catch (error) {
+      set({
+        error: error instanceof Error ? error.message : 'Login failed',
+        isLoading: false,
+      });
+    }
+  },
+
+  signup: async (email: string, password: string, name: string) => {
+    try {
+      set({ isLoading: true, error: null });
+      
+      // For development: Create a new user
+      const user = {
+        id: Date.now().toString(),
+        email,
+        name,
+      };
+      const token = 'test_token_' + Date.now();
+      const expiryTime = Date.now() + TOKEN_EXPIRY;
path: store/auth.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Implement secure token generation in signup method**

Similar to the login method, the signup method uses a predictable token generation pattern.

```diff
-      const token = 'test_token_' + Date.now();
+      // Generate a more secure token even for development
+      const token = 'dev_' + Array.from(crypto.getRandomValues(new Uint8Array(16)))
+        .map(b => b.toString(16).padStart(2, '0'))
+        .join('');
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
      // Generate a more secure token even for development
      const token = 'dev_' + Array.from(crypto.getRandomValues(new Uint8Array(16)))
        .map(b => b.toString(16).padStart(2, '0'))
        .join('');
      const expiryTime = Date.now() + TOKEN_EXPIRY;
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-26T11:51:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,209 @@
+import { create } from 'zustand';
+import * as SecureStore from 'expo-secure-store';
+import * as LocalAuthentication from 'expo-local-authentication';
+
+interface User {
+  id: string;
+  email: string;
+  name: string;
+}
+
+interface AuthState {
+  user: User | null;
+  token: string | null;
+  isLoading: boolean;
+  error: string | null;
+  isAuthenticated: boolean;
+  login: (email: string, password: string) => Promise<void>;
+  signup: (email: string, password: string, name: string) => Promise<void>;
+  logout: () => Promise<void>;
+  resetPassword: (email: string) => Promise<void>;
+  authenticateWithBiometrics: () => Promise<boolean>;
+  validateToken: () => Promise<boolean>;
+  refreshToken: () => Promise<void>;
+}
+
+const TOKEN_KEY = 'auth_token';
+const USER_KEY = 'user_data';
+const TOKEN_EXPIRY_KEY = 'token_expiry';
+
+// Test credentials for development
+const TEST_USER = {
+  email: 'test@example.com',
+  password: 'password123',
+  name: 'Test User',
+};
+
+// Token expiration time (24 hours)
+const TOKEN_EXPIRY = 24 * 60 * 60 * 1000;
+
+export const useAuthStore = create<AuthState>((set, get) => ({
+  user: null,
+  token: null,
+  isLoading: false,
+  error: null,
+  isAuthenticated: false,
+
+  validateToken: async () => {
+    try {
+      const token = await SecureStore.getItemAsync(TOKEN_KEY);
+      const expiry = await SecureStore.getItemAsync(TOKEN_EXPIRY_KEY);
+      
+      if (!token || !expiry) {
+        return false;
+      }
+
+      const expiryTime = parseInt(expiry);
+      if (Date.now() > expiryTime) {
+        // Token expired, try to refresh
+        await get().refreshToken();
+        return true;
+      }
+
+      return true;
+    } catch (error) {
+      console.error('Token validation error:', error);
+      return false;
+    }
+  },
+
+  refreshToken: async () => {
+    try {
+      // TODO: Implement actual token refresh API call
+      const newToken = 'test_token_' + Date.now();
+      const expiryTime = Date.now() + TOKEN_EXPIRY;
+
+      await SecureStore.setItemAsync(TOKEN_KEY, newToken);
+      await SecureStore.setItemAsync(TOKEN_EXPIRY_KEY, expiryTime.toString());
+
+      set({ token: newToken });
+    } catch (error) {
+      console.error('Token refresh error:', error);
+      await get().logout();
+    }
+  },
path: store/auth.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Implement secure token generation and proper refresh logic**

The current token refresh implementation generates tokens with a predictable pattern and lacks proper API integration.

```diff
 refreshToken: async () => {
   try {
     // TODO: Implement actual token refresh API call
-    const newToken = 'test_token_' + Date.now();
+    // For development: Generate a more secure token
+    const newToken = 'dev_' + Array.from(crypto.getRandomValues(new Uint8Array(16)))
+      .map(b => b.toString(16).padStart(2, '0'))
+      .join('');
     const expiryTime = Date.now() + TOKEN_EXPIRY;

     await SecureStore.setItemAsync(TOKEN_KEY, newToken);
     await SecureStore.setItemAsync(TOKEN_EXPIRY_KEY, expiryTime.toString());

     set({ token: newToken });
   } catch (error) {
     console.error('Token refresh error:', error);
     await get().logout();
   }
 },
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
  refreshToken: async () => {
    try {
      // TODO: Implement actual token refresh API call
      // For development: Generate a more secure token
      const newToken = 'dev_' + Array.from(crypto.getRandomValues(new Uint8Array(16)))
        .map(b => b.toString(16).padStart(2, '0'))
        .join('');
      const expiryTime = Date.now() + TOKEN_EXPIRY;

      await SecureStore.setItemAsync(TOKEN_KEY, newToken);
      await SecureStore.setItemAsync(TOKEN_EXPIRY_KEY, expiryTime.toString());

      set({ token: newToken });
    } catch (error) {
      console.error('Token refresh error:', error);
      await get().logout();
    }
  },
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-26T11:51:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,209 @@
+import { create } from 'zustand';
+import * as SecureStore from 'expo-secure-store';
+import * as LocalAuthentication from 'expo-local-authentication';
+
+interface User {
+  id: string;
+  email: string;
+  name: string;
+}
+
+interface AuthState {
+  user: User | null;
+  token: string | null;
+  isLoading: boolean;
+  error: string | null;
+  isAuthenticated: boolean;
+  login: (email: string, password: string) => Promise<void>;
+  signup: (email: string, password: string, name: string) => Promise<void>;
+  logout: () => Promise<void>;
+  resetPassword: (email: string) => Promise<void>;
+  authenticateWithBiometrics: () => Promise<boolean>;
+  validateToken: () => Promise<boolean>;
+  refreshToken: () => Promise<void>;
+}
+
+const TOKEN_KEY = 'auth_token';
+const USER_KEY = 'user_data';
+const TOKEN_EXPIRY_KEY = 'token_expiry';
+
+// Test credentials for development
+const TEST_USER = {
+  email: 'test@example.com',
+  password: 'password123',
+  name: 'Test User',
+};
+
+// Token expiration time (24 hours)
+const TOKEN_EXPIRY = 24 * 60 * 60 * 1000;
+
+export const useAuthStore = create<AuthState>((set, get) => ({
+  user: null,
+  token: null,
+  isLoading: false,
+  error: null,
+  isAuthenticated: false,
+
+  validateToken: async () => {
+    try {
+      const token = await SecureStore.getItemAsync(TOKEN_KEY);
+      const expiry = await SecureStore.getItemAsync(TOKEN_EXPIRY_KEY);
+      
+      if (!token || !expiry) {
+        return false;
+      }
+
+      const expiryTime = parseInt(expiry);
+      if (Date.now() > expiryTime) {
+        // Token expired, try to refresh
+        await get().refreshToken();
+        return true;
+      }
+
+      return true;
+    } catch (error) {
+      console.error('Token validation error:', error);
+      return false;
+    }
+  },
+
+  refreshToken: async () => {
+    try {
+      // TODO: Implement actual token refresh API call
+      const newToken = 'test_token_' + Date.now();
+      const expiryTime = Date.now() + TOKEN_EXPIRY;
+
+      await SecureStore.setItemAsync(TOKEN_KEY, newToken);
+      await SecureStore.setItemAsync(TOKEN_EXPIRY_KEY, expiryTime.toString());
+
+      set({ token: newToken });
+    } catch (error) {
+      console.error('Token refresh error:', error);
+      await get().logout();
+    }
+  },
+
+  login: async (email: string, password: string) => {
+    try {
+      set({ isLoading: true, error: null });
+      
+      // For development: Check against test credentials
+      if (email === TEST_USER.email && password === TEST_USER.password) {
+        const user = {
+          id: '1',
+          email: TEST_USER.email,
+          name: TEST_USER.name,
+        };
+        const token = 'test_token_' + Date.now();
+        const expiryTime = Date.now() + TOKEN_EXPIRY;
+
+        await SecureStore.setItemAsync(TOKEN_KEY, token);
+        await SecureStore.setItemAsync(USER_KEY, JSON.stringify(user));
+        await SecureStore.setItemAsync(TOKEN_EXPIRY_KEY, expiryTime.toString());
+
+        set({
+          user,
+          token,
+          isAuthenticated: true,
+          isLoading: false,
+        });
+      } else {
+        throw new Error('Invalid email or password');
+      }
+    } catch (error) {
+      set({
+        error: error instanceof Error ? error.message : 'Login failed',
+        isLoading: false,
+      });
+    }
+  },
+
+  signup: async (email: string, password: string, name: string) => {
+    try {
+      set({ isLoading: true, error: null });
+      
+      // For development: Create a new user
+      const user = {
+        id: Date.now().toString(),
+        email,
+        name,
+      };
+      const token = 'test_token_' + Date.now();
+      const expiryTime = Date.now() + TOKEN_EXPIRY;
+
+      await SecureStore.setItemAsync(TOKEN_KEY, token);
+      await SecureStore.setItemAsync(USER_KEY, JSON.stringify(user));
+      await SecureStore.setItemAsync(TOKEN_EXPIRY_KEY, expiryTime.toString());
+
+      set({
+        user,
+        token,
+        isAuthenticated: true,
+        isLoading: false,
+      });
+    } catch (error) {
+      set({
+        error: error instanceof Error ? error.message : 'Signup failed',
+        isLoading: false,
+      });
+    }
+  },
+
+  logout: async () => {
+    try {
+      await SecureStore.deleteItemAsync(TOKEN_KEY);
+      await SecureStore.deleteItemAsync(USER_KEY);
+      await SecureStore.deleteItemAsync(TOKEN_EXPIRY_KEY);
+      
+      set({
+        user: null,
+        token: null,
+        isAuthenticated: false,
+      });
+    } catch (error) {
+      console.error('Logout error:', error);
+    }
+  },
+
+  resetPassword: async (email: string) => {
+    try {
+      set({ isLoading: true, error: null });
+      
+      // For development: Simulate password reset
+      if (email === TEST_USER.email) {
+        set({ isLoading: false });
+      } else {
+        throw new Error('Email not found');
+      }
+    } catch (error) {
+      set({
+        error: error instanceof Error ? error.message : 'Password reset failed',
+        isLoading: false,
+      });
+    }
+  },
+
+  authenticateWithBiometrics: async () => {
+    try {
+      const hasHardware = await LocalAuthentication.hasHardwareAsync();
+      if (!hasHardware) {
+        throw new Error('Biometric authentication is not available');
+      }
+
+      const isEnrolled = await LocalAuthentication.isEnrolledAsync();
+      if (!isEnrolled) {
+        throw new Error('No biometrics enrolled');
+      }
+
+      const result = await LocalAuthentication.authenticateAsync({
+        promptMessage: 'Authenticate to access your account',
+        fallbackLabel: 'Use password',
+      });
+
+      return result.success;
+    } catch (error) {
+      console.error('Biometric authentication error:', error);
+      return false;
+    }
+  },
path: store/auth.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Link biometric authentication to user authentication**

The biometric authentication function currently only verifies biometrics but doesn't connect this to the user's authentication state or stored credentials.

```diff
 authenticateWithBiometrics: async () => {
   try {
     const hasHardware = await LocalAuthentication.hasHardwareAsync();
     if (!hasHardware) {
+      set({ error: 'Biometric authentication is not available on this device' });
       throw new Error('Biometric authentication is not available');
     }

     const isEnrolled = await LocalAuthentication.isEnrolledAsync();
     if (!isEnrolled) {
+      set({ error: 'No biometrics enrolled on this device' });
       throw new Error('No biometrics enrolled');
     }

     const result = await LocalAuthentication.authenticateAsync({
       promptMessage: 'Authenticate to access your account',
       fallbackLabel: 'Use password',
     });

+    if (result.success) {
+      // Check if we have stored credentials for automatic login
+      const storedEmail = await SecureStore.getItemAsync('biometric_email');
+      const storedPassword = await SecureStore.getItemAsync('biometric_password');
+      
+      if (storedEmail && storedPassword) {
+        // Attempt silent login with stored credentials
+        await get().login(storedEmail, storedPassword);
+      }
+    }
+
     return result.success;
   } catch (error) {
     console.error('Biometric authentication error:', error);
+    set({ error: error instanceof Error ? error.message : 'Biometric authentication failed' });
     return false;
   }
 },
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
  authenticateWithBiometrics: async () => {
    try {
      const hasHardware = await LocalAuthentication.hasHardwareAsync();
      if (!hasHardware) {
        set({ error: 'Biometric authentication is not available on this device' });
        throw new Error('Biometric authentication is not available');
      }

      const isEnrolled = await LocalAuthentication.isEnrolledAsync();
      if (!isEnrolled) {
        set({ error: 'No biometrics enrolled on this device' });
        throw new Error('No biometrics enrolled');
      }

      const result = await LocalAuthentication.authenticateAsync({
        promptMessage: 'Authenticate to access your account',
        fallbackLabel: 'Use password',
      });

      if (result.success) {
        // Check if we have stored credentials for automatic login
        const storedEmail = await SecureStore.getItemAsync('biometric_email');
        const storedPassword = await SecureStore.getItemAsync('biometric_password');
        
        if (storedEmail && storedPassword) {
          // Attempt silent login with stored credentials
          await get().login(storedEmail, storedPassword);
        }
      }

      return result.success;
    } catch (error) {
      console.error('Biometric authentication error:', error);
      set({ error: error instanceof Error ? error.message : 'Biometric authentication failed' });
      return false;
    }
  },
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-26T11:51:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,209 @@
+import { create } from 'zustand';
+import * as SecureStore from 'expo-secure-store';
+import * as LocalAuthentication from 'expo-local-authentication';
+
+interface User {
+  id: string;
+  email: string;
+  name: string;
+}
+
+interface AuthState {
+  user: User | null;
+  token: string | null;
+  isLoading: boolean;
+  error: string | null;
+  isAuthenticated: boolean;
+  login: (email: string, password: string) => Promise<void>;
+  signup: (email: string, password: string, name: string) => Promise<void>;
+  logout: () => Promise<void>;
+  resetPassword: (email: string) => Promise<void>;
+  authenticateWithBiometrics: () => Promise<boolean>;
+  validateToken: () => Promise<boolean>;
+  refreshToken: () => Promise<void>;
+}
+
+const TOKEN_KEY = 'auth_token';
+const USER_KEY = 'user_data';
+const TOKEN_EXPIRY_KEY = 'token_expiry';
+
+// Test credentials for development
+const TEST_USER = {
+  email: 'test@example.com',
+  password: 'password123',
+  name: 'Test User',
+};
path: store/auth.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Add warning comment for test credentials**

The test credentials should be clearly marked as development-only and should not be used in production.

```diff
-// Test credentials for development
+// WARNING: Test credentials for development only
+// TODO: Remove before deploying to production
 const TEST_USER = {
   email: 'test@example.com',
   password: 'password123',
   name: 'Test User',
 };
```


> Committable suggestion skipped: line range outside the PR's diff.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-26T11:51:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,209 @@
+import { create } from 'zustand';
+import * as SecureStore from 'expo-secure-store';
+import * as LocalAuthentication from 'expo-local-authentication';
+
+interface User {
+  id: string;
+  email: string;
+  name: string;
+}
+
+interface AuthState {
+  user: User | null;
+  token: string | null;
+  isLoading: boolean;
+  error: string | null;
+  isAuthenticated: boolean;
+  login: (email: string, password: string) => Promise<void>;
+  signup: (email: string, password: string, name: string) => Promise<void>;
+  logout: () => Promise<void>;
+  resetPassword: (email: string) => Promise<void>;
+  authenticateWithBiometrics: () => Promise<boolean>;
+  validateToken: () => Promise<boolean>;
+  refreshToken: () => Promise<void>;
+}
+
+const TOKEN_KEY = 'auth_token';
+const USER_KEY = 'user_data';
+const TOKEN_EXPIRY_KEY = 'token_expiry';
+
+// Test credentials for development
+const TEST_USER = {
+  email: 'test@example.com',
+  password: 'password123',
+  name: 'Test User',
+};
+
+// Token expiration time (24 hours)
+const TOKEN_EXPIRY = 24 * 60 * 60 * 1000;
+
+export const useAuthStore = create<AuthState>((set, get) => ({
+  user: null,
+  token: null,
+  isLoading: false,
+  error: null,
+  isAuthenticated: false,
+
+  validateToken: async () => {
+    try {
+      const token = await SecureStore.getItemAsync(TOKEN_KEY);
+      const expiry = await SecureStore.getItemAsync(TOKEN_EXPIRY_KEY);
+      
+      if (!token || !expiry) {
+        return false;
+      }
+
+      const expiryTime = parseInt(expiry);
+      if (Date.now() > expiryTime) {
+        // Token expired, try to refresh
+        await get().refreshToken();
+        return true;
+      }
+
+      return true;
+    } catch (error) {
+      console.error('Token validation error:', error);
+      return false;
+    }
+  },
+
+  refreshToken: async () => {
+    try {
+      // TODO: Implement actual token refresh API call
+      const newToken = 'test_token_' + Date.now();
+      const expiryTime = Date.now() + TOKEN_EXPIRY;
+
+      await SecureStore.setItemAsync(TOKEN_KEY, newToken);
+      await SecureStore.setItemAsync(TOKEN_EXPIRY_KEY, expiryTime.toString());
+
+      set({ token: newToken });
+    } catch (error) {
+      console.error('Token refresh error:', error);
+      await get().logout();
+    }
+  },
+
+  login: async (email: string, password: string) => {
+    try {
+      set({ isLoading: true, error: null });
+      
+      // For development: Check against test credentials
+      if (email === TEST_USER.email && password === TEST_USER.password) {
+        const user = {
+          id: '1',
+          email: TEST_USER.email,
+          name: TEST_USER.name,
+        };
+        const token = 'test_token_' + Date.now();
+        const expiryTime = Date.now() + TOKEN_EXPIRY;
+
+        await SecureStore.setItemAsync(TOKEN_KEY, token);
+        await SecureStore.setItemAsync(USER_KEY, JSON.stringify(user));
+        await SecureStore.setItemAsync(TOKEN_EXPIRY_KEY, expiryTime.toString());
+
+        set({
+          user,
+          token,
+          isAuthenticated: true,
+          isLoading: false,
+        });
+      } else {
+        throw new Error('Invalid email or password');
+      }
+    } catch (error) {
+      set({
+        error: error instanceof Error ? error.message : 'Login failed',
+        isLoading: false,
+      });
+    }
+  },
+
+  signup: async (email: string, password: string, name: string) => {
+    try {
+      set({ isLoading: true, error: null });
+      
+      // For development: Create a new user
+      const user = {
+        id: Date.now().toString(),
+        email,
+        name,
+      };
+      const token = 'test_token_' + Date.now();
+      const expiryTime = Date.now() + TOKEN_EXPIRY;
+
+      await SecureStore.setItemAsync(TOKEN_KEY, token);
+      await SecureStore.setItemAsync(USER_KEY, JSON.stringify(user));
+      await SecureStore.setItemAsync(TOKEN_EXPIRY_KEY, expiryTime.toString());
+
+      set({
+        user,
+        token,
+        isAuthenticated: true,
+        isLoading: false,
+      });
+    } catch (error) {
+      set({
+        error: error instanceof Error ? error.message : 'Signup failed',
+        isLoading: false,
+      });
+    }
+  },
+
+  logout: async () => {
+    try {
+      await SecureStore.deleteItemAsync(TOKEN_KEY);
+      await SecureStore.deleteItemAsync(USER_KEY);
+      await SecureStore.deleteItemAsync(TOKEN_EXPIRY_KEY);
+      
+      set({
+        user: null,
+        token: null,
+        isAuthenticated: false,
+      });
+    } catch (error) {
+      console.error('Logout error:', error);
+    }
+  },
+
+  resetPassword: async (email: string) => {
+    try {
+      set({ isLoading: true, error: null });
+      
+      // For development: Simulate password reset
+      if (email === TEST_USER.email) {
+        set({ isLoading: false });
+      } else {
+        throw new Error('Email not found');
+      }
+    } catch (error) {
+      set({
+        error: error instanceof Error ? error.message : 'Password reset failed',
+        isLoading: false,
+      });
+    }
+  },
path: store/auth.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Implement actual password reset logic**

The current resetPassword method only verifies if the email matches the test user but doesn't actually reset anything.

```diff
 resetPassword: async (email: string) => {
   try {
     set({ isLoading: true, error: null });
     
+    // TODO: Replace with actual API call in production
     // For development: Simulate password reset
     if (email === TEST_USER.email) {
+      // Simulate password reset email being sent
+      console.log(`Password reset email sent to ${email}`);
       set({ isLoading: false });
+      return;
     } else {
       throw new Error('Email not found');
     }
   } catch (error) {
     set({
       error: error instanceof Error ? error.message : 'Password reset failed',
       isLoading: false,
     });
   }
 },
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
  resetPassword: async (email: string) => {
    try {
      set({ isLoading: true, error: null });
      
      // TODO: Replace with actual API call in production
      // For development: Simulate password reset
      if (email === TEST_USER.email) {
        // Simulate password reset email being sent
        console.log(`Password reset email sent to ${email}`);
        set({ isLoading: false });
        return;
      } else {
        throw new Error('Email not found');
      }
    } catch (error) {
      set({
        error: error instanceof Error ? error.message : 'Password reset failed',
        isLoading: false,
      });
    }
  },
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-26T11:51:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,209 @@
+import { create } from 'zustand';
+import * as SecureStore from 'expo-secure-store';
+import * as LocalAuthentication from 'expo-local-authentication';
+
+interface User {
+  id: string;
+  email: string;
+  name: string;
+}
+
+interface AuthState {
+  user: User | null;
+  token: string | null;
+  isLoading: boolean;
+  error: string | null;
+  isAuthenticated: boolean;
+  login: (email: string, password: string) => Promise<void>;
+  signup: (email: string, password: string, name: string) => Promise<void>;
+  logout: () => Promise<void>;
+  resetPassword: (email: string) => Promise<void>;
+  authenticateWithBiometrics: () => Promise<boolean>;
+  validateToken: () => Promise<boolean>;
+  refreshToken: () => Promise<void>;
+}
+
+const TOKEN_KEY = 'auth_token';
+const USER_KEY = 'user_data';
+const TOKEN_EXPIRY_KEY = 'token_expiry';
+
+// Test credentials for development
+const TEST_USER = {
+  email: 'test@example.com',
+  password: 'password123',
+  name: 'Test User',
+};
+
+// Token expiration time (24 hours)
+const TOKEN_EXPIRY = 24 * 60 * 60 * 1000;
+
+export const useAuthStore = create<AuthState>((set, get) => ({
+  user: null,
+  token: null,
+  isLoading: false,
+  error: null,
+  isAuthenticated: false,
+
+  validateToken: async () => {
+    try {
+      const token = await SecureStore.getItemAsync(TOKEN_KEY);
+      const expiry = await SecureStore.getItemAsync(TOKEN_EXPIRY_KEY);
+      
+      if (!token || !expiry) {
+        return false;
+      }
+
+      const expiryTime = parseInt(expiry);
+      if (Date.now() > expiryTime) {
+        // Token expired, try to refresh
+        await get().refreshToken();
+        return true;
+      }
+
+      return true;
+    } catch (error) {
+      console.error('Token validation error:', error);
+      return false;
+    }
+  },
+
+  refreshToken: async () => {
+    try {
+      // TODO: Implement actual token refresh API call
+      const newToken = 'test_token_' + Date.now();
+      const expiryTime = Date.now() + TOKEN_EXPIRY;
+
+      await SecureStore.setItemAsync(TOKEN_KEY, newToken);
+      await SecureStore.setItemAsync(TOKEN_EXPIRY_KEY, expiryTime.toString());
+
+      set({ token: newToken });
+    } catch (error) {
+      console.error('Token refresh error:', error);
+      await get().logout();
+    }
+  },
+
+  login: async (email: string, password: string) => {
+    try {
+      set({ isLoading: true, error: null });
+      
+      // For development: Check against test credentials
+      if (email === TEST_USER.email && password === TEST_USER.password) {
+        const user = {
+          id: '1',
+          email: TEST_USER.email,
+          name: TEST_USER.name,
+        };
+        const token = 'test_token_' + Date.now();
+        const expiryTime = Date.now() + TOKEN_EXPIRY;
+
+        await SecureStore.setItemAsync(TOKEN_KEY, token);
+        await SecureStore.setItemAsync(USER_KEY, JSON.stringify(user));
+        await SecureStore.setItemAsync(TOKEN_EXPIRY_KEY, expiryTime.toString());
+
+        set({
+          user,
+          token,
+          isAuthenticated: true,
+          isLoading: false,
+        });
+      } else {
+        throw new Error('Invalid email or password');
+      }
+    } catch (error) {
+      set({
+        error: error instanceof Error ? error.message : 'Login failed',
+        isLoading: false,
+      });
+    }
+  },
path: store/auth.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Add TODO for replacing mock authentication with actual API integration**

The login method currently uses hardcoded test credentials. Add a clear TODO to replace this with actual API calls in production.

```diff
 login: async (email: string, password: string) => {
   try {
     set({ isLoading: true, error: null });
     
+    // TODO: Replace with actual API authentication in production
     // For development: Check against test credentials
     if (email === TEST_USER.email && password === TEST_USER.password) {
       const user = {
         id: '1',
         email: TEST_USER.email,
         name: TEST_USER.name,
       };
-      const token = 'test_token_' + Date.now();
+      // Generate a more secure token even for development
+      const token = 'dev_' + Array.from(crypto.getRandomValues(new Uint8Array(16)))
+        .map(b => b.toString(16).padStart(2, '0'))
+        .join('');
       const expiryTime = Date.now() + TOKEN_EXPIRY;

       await SecureStore.setItemAsync(TOKEN_KEY, token);
       await SecureStore.setItemAsync(USER_KEY, JSON.stringify(user));
       await SecureStore.setItemAsync(TOKEN_EXPIRY_KEY, expiryTime.toString());

+      // Store credentials for biometric authentication if needed
+      await SecureStore.setItemAsync('biometric_email', email);
+      await SecureStore.setItemAsync('biometric_password', password);
+
       set({
         user,
         token,
         isAuthenticated: true,
         isLoading: false,
       });
     } else {
       throw new Error('Invalid email or password');
     }
   } catch (error) {
     set({
       error: error instanceof Error ? error.message : 'Login failed',
       isLoading: false,
     });
   }
 },
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
  login: async (email: string, password: string) => {
    try {
      set({ isLoading: true, error: null });
      
      // TODO: Replace with actual API authentication in production
      // For development: Check against test credentials
      if (email === TEST_USER.email && password === TEST_USER.password) {
        const user = {
          id: '1',
          email: TEST_USER.email,
          name: TEST_USER.name,
        };
        // Generate a more secure token even for development
        const token = 'dev_' + Array.from(crypto.getRandomValues(new Uint8Array(16)))
          .map(b => b.toString(16).padStart(2, '0'))
          .join('');
        const expiryTime = Date.now() + TOKEN_EXPIRY;

        await SecureStore.setItemAsync(TOKEN_KEY, token);
        await SecureStore.setItemAsync(USER_KEY, JSON.stringify(user));
        await SecureStore.setItemAsync(TOKEN_EXPIRY_KEY, expiryTime.toString());

        // Store credentials for biometric authentication if needed
        await SecureStore.setItemAsync('biometric_email', email);
        await SecureStore.setItemAsync('biometric_password', password);

        set({
          user,
          token,
          isAuthenticated: true,
          isLoading: false,
        });
      } else {
        throw new Error('Invalid email or password');
      }
    } catch (error) {
      set({
        error: error instanceof Error ? error.message : 'Login failed',
        isLoading: false,
      });
    }
  },
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-26T11:51:42Z
author_association: CONTRIBUTOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-03-26T11:46:07Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough
This pull request introduces a comprehensive authentication flow by adding new screens for login, signup, and forgot-password alongside a dedicated authentication layout. The changes integrate form management using React Hook Form with Zod-based validation. A Zustand-based authentication store is implemented for managing user sessions, including simulated API interactions, token validation, refresh logic, biometric authentication, and logout functionality. Additionally, navigation layouts have been updated for redirection based on the authentication state, and configuration changes support React Native development.

## Changes

| File(s)                                                                                                                                           | Change Summary                                                                                                                                                                 |
|---------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `app/(auth)/_layout.tsx`, `app/(auth)/forgot-password.tsx`, `app/(auth)/login.tsx`, `app/(auth)/signup.tsx`                                       | Added new auth screens/components (AuthLayout, ForgotPasswordScreen, LoginScreen, SignupScreen) with form handling, validation, navigation, and error display.             |
| `app/(tabs)/_layout.tsx`, `app/_layout.tsx`                                                                                                        | Modified navigation layouts to include auth state checks, redirect unauthenticated users, and update header actions for logout.                                               |
| `components/auth/AuthInput.tsx`                                                                                                                  | Introduced a reusable authentication input component with dynamic styling based on color scheme and error state.                                                               |
| `store/auth.ts`, `schemas/auth.ts`, `package.json`, `tsconfig.json`                                                                                | Implemented a Zustand auth store with methods for login, signup, logout, reset password, token validation/refresh, and biometric authentication; added Zod schemas, a biometric dependency, and updated TypeScript options. |

## Sequence Diagram(s)
```mermaid
sequenceDiagram
    participant U as User
    participant AL as AuthLayout
    participant LS as LoginScreen
    participant AS as AuthStore
    participant R as Redirect

    U->>AL: Launch App
    AL->>AS: validateToken()
    AS-->>AL: Token valid/invalid
    alt Token valid
         AL->>R: Redirect to main app
    else Token invalid
         AL->>U: Display auth screens (login/signup/forgot-password)
         U->>LS: Enter credentials or choose biometric option
         LS->>AS: Call login() or authenticateWithBiometrics()
         AS-->>LS: Return authentication result
         LS->>R: Redirect to main app on success
    end
```

## Assessment against linked issues

| Objective                                                                                     | Addressed | Explanation                                                                                                                             |
|-----------------------------------------------------------------------------------------------|-----------|-----------------------------------------------------------------------------------------------------------------------------------------|
| Authentication Interfaces: Login, Signup, Forgot Password (#8)                                | ✅        |                                                                                                                                         |
| Form validation using React Hook Form and Zod (#8)                                            | ✅        |                                                                                                                                         |
| Biometric authentication and logout functionality (#8)                                      | ✅        |                                                                                                                                         |
| API integration and secure token storage (#8)                                                 | ❓        | Uses simulated API calls and tokens are stored in state; explicit secure storage is not implemented.                                    |

## Poem
> I'm a happy bunny in the code,  
> Hopping through each auth episode.  
> From login leaps to signup spry,  
> I watch the tokens flutter by!  
> Ears twitch with joy — our code's aglow! 🐰✨

<!-- walkthrough_end -->
<!-- This is an auto-generated comment: all tool run failures by coderabbit.ai -->

> [!WARNING]
> There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.
> 
> <details>
> <summary>🔧 ESLint</summary>
> 
> > If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.
> 
> <details>
> <summary>app/(auth)/_layout.tsx</summary>
> 
> 
> Oops! Something went wrong! :(
> 
> ESLint: 9.22.0
> 
> ESLint couldn't find an eslint.config.(js|mjs|cjs) file.
> 
> From ESLint v9.0.0, the default configuration file is now eslint.config.js.
> If you are using a .eslintrc.* file, please follow the migration guide
> to update your configuration file to the new format:
> 
> https://eslint.org/docs/latest/use/configure/migration-guide
> 
> If you still have problems after following the migration guide, please stop by
> https://eslint.org/chat/help to chat with the team.
> 
> 
> </details>
> 
> <details>
> <summary>app/(auth)/forgot-password.tsx</summary>
> 
> 
> Oops! Something went wrong! :(
> 
> ESLint: 9.22.0
> 
> ESLint couldn't find an eslint.config.(js|mjs|cjs) file.
> 
> From ESLint v9.0.0, the default configuration file is now eslint.config.js.
> If you are using a .eslintrc.* file, please follow the migration guide
> to update your configuration file to the new format:
> 
> https://eslint.org/docs/latest/use/configure/migration-guide
> 
> If you still have problems after following the migration guide, please stop by
> https://eslint.org/chat/help to chat with the team.
> 
> 
> </details>
> 
> <details>
> <summary>app/(auth)/signup.tsx</summary>
> 
> 
> Oops! Something went wrong! :(
> 
> ESLint: 9.22.0
> 
> ESLint couldn't find an eslint.config.(js|mjs|cjs) file.
> 
> From ESLint v9.0.0, the default configuration file is now eslint.config.js.
> If you are using a .eslintrc.* file, please follow the migration guide
> to update your configuration file to the new format:
> 
> https://eslint.org/docs/latest/use/configure/migration-guide
> 
> If you still have problems after following the migration guide, please stop by
> https://eslint.org/chat/help to chat with the team.
> 
> 
> </details>
> 
> + 6 others
> 
> </details>

<!-- end of auto-generated comment: all tool run failures by coderabbit.ai -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=Crypto-Jaguars/Revo-Marketplace-Expo&utm_content=15):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai plan` to trigger planning for file edits and PR creation.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: sasasamaes
created_at: 2025-03-30T23:39:15Z
body: Great Work @arebisola 🚀
author_association: CONTRIBUTOR