Files
Hotel-Booking/client/ZUSTAND_AUTH_GUIDE.md
Iliyan Angelov 93d4c1df80 update
2025-11-16 15:12:43 +02:00

6.3 KiB

useAuthStore - Zustand Authentication Store

Function 3 Completed

📦 Files Created:

  1. src/store/useAuthStore.ts - Zustand store managing auth
  2. src/services/api/apiClient.ts - Axios client with interceptors
  3. src/services/api/authService.ts - Auth API service
  4. .env.example - Template for environment variables

🎯 Features Implemented:

State Management:

interface AuthState {
  token: string | null;
  refreshToken: string | null;
  userInfo: UserInfo | null;
  isAuthenticated: boolean;
  isLoading: boolean;
  error: string | null;
}

Actions:

  • login(credentials) - Login
  • register(data) - Register new account
  • logout() - Logout
  • setUser(user) - Update user information
  • refreshAuthToken() - Refresh token
  • forgotPassword(data) - Forgot password
  • resetPassword(data) - Reset password
  • initializeAuth() - Initialize auth from localStorage
  • clearError() - Clear error message

📝 Usage:

1. Initialize in App.tsx:

import useAuthStore from './store/useAuthStore';

function App() {
  const { 
    isAuthenticated, 
    userInfo, 
    logout, 
    initializeAuth 
  } = useAuthStore();

  useEffect(() => {
    initializeAuth();
  }, [initializeAuth]);

  // ...
}

2. Use in Login Form:

import useAuthStore from '../store/useAuthStore';

const LoginPage = () => {
  const { login, isLoading, error } = useAuthStore();
  const navigate = useNavigate();

  const handleSubmit = async (data) => {
    try {
      await login(data);
      navigate('/dashboard'); // Redirect after login
    } catch (error) {
      // Error has been handled by store
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* Form fields */}
      {error && <div>{error}</div>}
      <button disabled={isLoading}>
        {isLoading ? 'Processing...' : 'Login'}
      </button>
    </form>
  );
};

3. Use in Register Form:

const RegisterPage = () => {
  const { register, isLoading } = useAuthStore();
  const navigate = useNavigate();

  const handleSubmit = async (data) => {
    try {
      await register(data);
      navigate('/login'); // Redirect to login
    } catch (error) {
      // Error displayed via toast
    }
  };

  // ...
};

4. Logout:

const Header = () => {
  const { logout } = useAuthStore();

  const handleLogout = async () => {
    await logout();
    // Auto redirect to login if needed
  };

  return <button onClick={handleLogout}>Logout</button>;
};

5. Display user information:

const Profile = () => {
  const { userInfo } = useAuthStore();

  return (
    <div>
      <h1>Hello, {userInfo?.name}</h1>
      <p>Email: {userInfo?.email}</p>
      <p>Role: {userInfo?.role}</p>
    </div>
  );
};

🔐 LocalStorage Persistence:

Store automatically saves and reads from localStorage:

  • token - JWT access token
  • refreshToken - JWT refresh token
  • userInfo - User information

When page reloads, auth state is automatically restored via initializeAuth().

🌐 API Integration:

Base URL Configuration:

Create .env file in client/ directory:

VITE_API_URL=http://localhost:3000
VITE_ENV=development

API Endpoints Used:

  • POST /api/auth/login - Login
  • POST /api/auth/register - Register
  • POST /api/auth/logout - Logout
  • GET /api/auth/profile - Get profile
  • POST /api/auth/refresh-token - Refresh token
  • POST /api/auth/forgot-password - Forgot password
  • POST /api/auth/reset-password - Reset password

🛡️ Security Features:

  1. Auto Token Injection:

    • Axios interceptor automatically adds token to headers
    Authorization: Bearer <token>
    
  2. Auto Logout on 401:

    • When token expires (401), automatically logout and redirect to login
  3. Token Refresh:

    • Can refresh token when about to expire
  4. Password Hashing:

    • Backend handles bcrypt hashing

📱 Toast Notifications:

Store automatically displays toast for events:

  • Login successful
  • Registration successful
  • Logout
  • Login failed
  • Registration failed
  • API errors

🔄 Component Updates:

ProtectedRoute:

// BEFORE (with props)
<ProtectedRoute isAuthenticated={isAuthenticated}>
  <Dashboard />
</ProtectedRoute>

// AFTER (automatically gets from store)
<ProtectedRoute>
  <Dashboard />
</ProtectedRoute>

AdminRoute:

// BEFORE (with props)
<AdminRoute userInfo={userInfo}>
  <AdminPanel />
</AdminRoute>

// AFTER (automatically gets from store)
<AdminRoute>
  <AdminPanel />
</AdminRoute>

LayoutMain:

Still receives props from App.tsx to display Header/Navbar:

<LayoutMain 
  isAuthenticated={isAuthenticated}
  userInfo={userInfo}
  onLogout={handleLogout}
/>

🧪 Testing:

To test authentication flow:

  1. Create .env file:

    cp .env.example .env
    
  2. Ensure backend is running:

    cd server
    npm run dev
    
  3. Run frontend:

    cd client
    npm run dev
    
  4. Test flow:

    • Access /register → Register account
    • Access /login → Login
    • Access /dashboard → View dashboard (protected)
    • Click logout → Clear session
    • Reload page → Auth state restored

🚀 Next Steps:

Function 4: Login Form

  • Create LoginPage with React Hook Form + Yup
  • Integrate with useAuthStore
  • UX enhancements (loading, show/hide password, remember me)

Function 5: Register Form

  • Create RegisterPage with validation
  • Integrate with useAuthStore

Function 6-7: Password Reset Flow

  • ForgotPasswordPage
  • ResetPasswordPage

📚 TypeScript Types:

interface LoginCredentials {
  email: string;
  password: string;
  rememberMe?: boolean;
}

interface RegisterData {
  name: string;
  email: string;
  password: string;
  phone?: string;
}

interface UserInfo {
  id: number;
  name: string;
  email: string;
  phone?: string;
  avatar?: string;
  role: string;
  createdAt?: string;
}

Results Achieved:

  1. All user information managed centrally
  2. Maintain login after page reload
  3. Easy access to userInfo in any component
  4. Auto token management
  5. Type-safe with TypeScript
  6. Clean code, easy to maintain