6.3 KiB
6.3 KiB
useAuthStore - Zustand Authentication Store
✅ Function 3 Completed
📦 Files Created:
src/store/useAuthStore.ts- Zustand store managing authsrc/services/api/apiClient.ts- Axios client with interceptorssrc/services/api/authService.ts- Auth API service.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 tokenrefreshToken- JWT refresh tokenuserInfo- 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- LoginPOST /api/auth/register- RegisterPOST /api/auth/logout- LogoutGET /api/auth/profile- Get profilePOST /api/auth/refresh-token- Refresh tokenPOST /api/auth/forgot-password- Forgot passwordPOST /api/auth/reset-password- Reset password
🛡️ Security Features:
-
Auto Token Injection:
- Axios interceptor automatically adds token to headers
Authorization: Bearer <token> -
Auto Logout on 401:
- When token expires (401), automatically logout and redirect to login
-
Token Refresh:
- Can refresh token when about to expire
-
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:
-
Create
.envfile:cp .env.example .env -
Ensure backend is running:
cd server npm run dev -
Run frontend:
cd client npm run dev -
Test flow:
- Access
/register→ Register account - Access
/login→ Login - Access
/dashboard→ View dashboard (protected) - Click logout → Clear session
- Reload page → Auth state restored
- Access
🚀 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:
- ✅ All user information managed centrally
- ✅ Maintain login after page reload
- ✅ Easy access to userInfo in any component
- ✅ Auto token management
- ✅ Type-safe with TypeScript
- ✅ Clean code, easy to maintain