Files
Hotel-Booking/client/ZUSTAND_AUTH_GUIDE.md
Iliyan Angelov 824eec6190 Hotel Booking
2025-11-16 14:19:13 +02:00

6.7 KiB

useAuthStore - Zustand Authentication Store

Hoàn thành Chức năng 3

📦 Files đã tạo:

  1. src/store/useAuthStore.ts - Zustand store quản lý auth
  2. src/services/api/apiClient.ts - Axios client với interceptors
  3. src/services/api/authService.ts - Auth API service
  4. .env.example - Template cho environment variables

🎯 Tính năng đã implement:

State Management:

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

Actions:

  • login(credentials) - Đăng nhập
  • register(data) - Đăng ký tài khoản mới
  • logout() - Đăng xuất
  • setUser(user) - Cập nhật thông tin user
  • refreshAuthToken() - Làm mới token
  • forgotPassword(data) - Quên mật khẩu
  • resetPassword(data) - Đặt lại mật khẩu
  • initializeAuth() - Khởi tạo auth từ localStorage
  • clearError() - Xóa error message

📝 Cách sử dụng:

1. Khởi tạo trong App.tsx:

import useAuthStore from './store/useAuthStore';

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

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

  // ...
}

2. Sử dụng trong 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 sau khi login
    } catch (error) {
      // Error đã được xử lý bởi store
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* Form fields */}
      {error && <div>{error}</div>}
      <button disabled={isLoading}>
        {isLoading ? 'Đang xử lý...' : 'Đăng nhập'}
      </button>
    </form>
  );
};

3. Sử dụng trong Register Form:

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

  const handleSubmit = async (data) => {
    try {
      await register(data);
      navigate('/login'); // Redirect về login
    } catch (error) {
      // Error được hiển thị qua toast
    }
  };

  // ...
};

4. Logout:

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

  const handleLogout = async () => {
    await logout();
    // Auto redirect về login nếu cần
  };

  return <button onClick={handleLogout}>Đăng xuất</button>;
};

5. Hiển thị thông tin user:

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

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

🔐 LocalStorage Persistence:

Store tự động lưu và đọc từ localStorage:

  • token - JWT access token
  • refreshToken - JWT refresh token
  • userInfo - Thông tin user

Khi reload page, auth state được khôi phục tự động qua initializeAuth().

🌐 API Integration:

Base URL Configuration:

Tạo file .env trong thư mục client/:

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

API Endpoints được sử dụng:

  • POST /api/auth/login - Đăng nhập
  • POST /api/auth/register - Đăng ký
  • POST /api/auth/logout - Đăng xuất
  • GET /api/auth/profile - Lấy profile
  • POST /api/auth/refresh-token - Refresh token
  • POST /api/auth/forgot-password - Quên mật khẩu
  • POST /api/auth/reset-password - Đặt lại mật khẩu

🛡️ Security Features:

  1. Auto Token Injection:

    • Axios interceptor tự động thêm token vào headers
    Authorization: Bearer <token>
    
  2. Auto Logout on 401:

    • Khi token hết hạn (401), tự động logout và redirect về login
  3. Token Refresh:

    • Có thể refresh token khi sắp hết hạn
  4. Password Hashing:

    • Backend xử lý bcrypt hashing

📱 Toast Notifications:

Store tự động hiển thị toast cho các events:

  • Login thành công
  • Đăng ký thành công
  • Logout
  • Login thất bại
  • Đăng ký thất bại
  • API errors

🔄 Component Updates:

ProtectedRoute:

// TRƯỚC (với props)
<ProtectedRoute isAuthenticated={isAuthenticated}>
  <Dashboard />
</ProtectedRoute>

// SAU (tự động lấy từ store)
<ProtectedRoute>
  <Dashboard />
</ProtectedRoute>

AdminRoute:

// TRƯỚC (với props)
<AdminRoute userInfo={userInfo}>
  <AdminPanel />
</AdminRoute>

// SAU (tự động lấy từ store)
<AdminRoute>
  <AdminPanel />
</AdminRoute>

LayoutMain:

Vẫn nhận props từ App.tsx để hiển thị Header/Navbar:

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

🧪 Testing:

Để test authentication flow:

  1. Tạo file .env:

    cp .env.example .env
    
  2. Ensure backend đang chạy:

    cd server
    npm run dev
    
  3. Chạy frontend:

    cd client
    npm run dev
    
  4. Test flow:

    • Truy cập /register → Đăng ký tài khoản
    • Truy cập /login → Đăng nhập
    • Truy cập /dashboard → Xem dashboard (protected)
    • Click logout → Xóa session
    • Reload page → Auth state được khôi phục

🚀 Next Steps:

Chức năng 4: Form Login

  • Tạo LoginPage với React Hook Form + Yup
  • Tích hợp với useAuthStore
  • UX enhancements (loading, show/hide password, remember me)

Chức năng 5: Form Register

  • Tạo RegisterPage với validation
  • Tích hợp với useAuthStore

Chức năng 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;
}

Kết quả đạt được:

  1. Toàn bộ thông tin user được quản lý tập trung
  2. Duy trì đăng nhập sau khi reload trang
  3. Dễ dàng truy cập userInfo trong mọi component
  4. Auto token management
  5. Type-safe với TypeScript
  6. Clean code, dễ maintain