diff --git a/AUTHENTICATION_GUIDE.md b/AUTHENTICATION_GUIDE.md
new file mode 100644
index 0000000..529f8d8
--- /dev/null
+++ b/AUTHENTICATION_GUIDE.md
@@ -0,0 +1,526 @@
+# Authentication Implementation Guide
+
+## Overview
+This guide provides a comprehensive approach to implementing authentication in web applications, covering frontend login components, backend authentication, route protection, and session management.
+
+## Frontend Implementation
+
+### 1. Login Component Structure
+
+```jsx
+// components/LoginForm.jsx
+import React, { useState } from 'react';
+import { useAuth } from '../context/AuthContext';
+
+const LoginForm = () => {
+ const [credentials, setCredentials] = useState({ username: '', password: '' });
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState('');
+ const { login } = useAuth();
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ setLoading(true);
+ setError('');
+
+ try {
+ await login(credentials);
+ } catch (err) {
+ setError(err.message || 'Login failed');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ return (
+
+ );
+};
+
+export default LoginForm;
+```
+
+### 2. Authentication Context
+
+```jsx
+// context/AuthContext.jsx
+import React, { createContext, useContext, useState, useEffect } from 'react';
+import { authAPI } from '../services/authService';
+
+const AuthContext = createContext();
+
+export const useAuth = () => {
+ const context = useContext(AuthContext);
+ if (!context) {
+ throw new Error('useAuth must be used within AuthProvider');
+ }
+ return context;
+};
+
+export const AuthProvider = ({ children }) => {
+ const [user, setUser] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [token, setToken] = useState(localStorage.getItem('token'));
+
+ useEffect(() => {
+ if (token) {
+ validateToken();
+ } else {
+ setLoading(false);
+ }
+ }, [token]);
+
+ const validateToken = async () => {
+ try {
+ const userData = await authAPI.validateToken(token);
+ setUser(userData);
+ } catch (error) {
+ logout();
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const login = async (credentials) => {
+ const response = await authAPI.login(credentials);
+ const { user: userData, token: authToken } = response;
+
+ setUser(userData);
+ setToken(authToken);
+ localStorage.setItem('token', authToken);
+ };
+
+ const logout = () => {
+ setUser(null);
+ setToken(null);
+ localStorage.removeItem('token');
+ };
+
+ const value = {
+ user,
+ login,
+ logout,
+ loading,
+ isAuthenticated: !!user
+ };
+
+ return (
+
+ {children}
+
+ );
+};
+```
+
+### 3. Protected Route Component
+
+```jsx
+// components/ProtectedRoute.jsx
+import React from 'react';
+import { useAuth } from '../context/AuthContext';
+import LoginForm from './LoginForm';
+import LoadingSpinner from './LoadingSpinner';
+
+const ProtectedRoute = ({ children }) => {
+ const { isAuthenticated, loading } = useAuth();
+
+ if (loading) {
+ return ;
+ }
+
+ if (!isAuthenticated) {
+ return ;
+ }
+
+ return children;
+};
+
+export default ProtectedRoute;
+```
+
+### 4. App Structure with Authentication
+
+```jsx
+// App.jsx
+import React from 'react';
+import { AuthProvider } from './context/AuthContext';
+import ProtectedRoute from './components/ProtectedRoute';
+import MainApp from './components/MainApp';
+
+function App() {
+ return (
+
+
+
+ );
+}
+
+export default App;
+```
+
+## Backend Implementation
+
+### 5. Authentication Service
+
+```javascript
+// services/authService.js
+const API_BASE_URL = process.env.REACT_APP_API_URL || 'http://localhost:3001/api';
+
+export const authAPI = {
+ login: async (credentials) => {
+ const response = await fetch(`${API_BASE_URL}/auth/login`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(credentials),
+ });
+
+ if (!response.ok) {
+ const error = await response.json();
+ throw new Error(error.message || 'Login failed');
+ }
+
+ return response.json();
+ },
+
+ validateToken: async (token) => {
+ const response = await fetch(`${API_BASE_URL}/auth/validate`, {
+ headers: {
+ 'Authorization': `Bearer ${token}`,
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error('Token validation failed');
+ }
+
+ return response.json();
+ },
+
+ logout: async (token) => {
+ await fetch(`${API_BASE_URL}/auth/logout`, {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${token}`,
+ },
+ });
+ }
+};
+```
+
+### 6. API Interceptor for Authenticated Requests
+
+```javascript
+// utils/apiClient.js
+import { useAuth } from '../context/AuthContext';
+
+const createAPIClient = () => {
+ const baseURL = process.env.REACT_APP_API_URL || 'http://localhost:3001/api';
+
+ const apiClient = async (endpoint, options = {}) => {
+ const token = localStorage.getItem('token');
+
+ const config = {
+ headers: {
+ 'Content-Type': 'application/json',
+ ...(token && { 'Authorization': `Bearer ${token}` }),
+ ...options.headers,
+ },
+ ...options,
+ };
+
+ const response = await fetch(`${baseURL}${endpoint}`, config);
+
+ if (response.status === 401) {
+ localStorage.removeItem('token');
+ window.location.href = '/login';
+ return;
+ }
+
+ if (!response.ok) {
+ const error = await response.json();
+ throw new Error(error.message || 'Request failed');
+ }
+
+ return response.json();
+ };
+
+ return apiClient;
+};
+
+export default createAPIClient();
+```
+
+## Backend Server Implementation (Node.js/Express)
+
+### 7. Authentication Middleware
+
+```javascript
+// middleware/auth.js
+const jwt = require('jsonwebtoken');
+const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
+
+const authenticateToken = (req, res, next) => {
+ const authHeader = req.headers['authorization'];
+ const token = authHeader && authHeader.split(' ')[1];
+
+ if (!token) {
+ return res.status(401).json({ message: 'Access token required' });
+ }
+
+ jwt.verify(token, JWT_SECRET, (err, user) => {
+ if (err) {
+ return res.status(403).json({ message: 'Invalid or expired token' });
+ }
+ req.user = user;
+ next();
+ });
+};
+
+module.exports = { authenticateToken };
+```
+
+### 8. Auth Routes
+
+```javascript
+// routes/auth.js
+const express = require('express');
+const bcrypt = require('bcrypt');
+const jwt = require('jsonwebtoken');
+const { authenticateToken } = require('../middleware/auth');
+
+const router = express.Router();
+const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
+
+// Mock user database (replace with your database)
+const users = [
+ {
+ id: 1,
+ username: 'admin',
+ password: '$2b$10$hash', // bcrypt hash of 'password'
+ email: 'admin@example.com'
+ }
+];
+
+// Login endpoint
+router.post('/login', async (req, res) => {
+ const { username, password } = req.body;
+
+ try {
+ const user = users.find(u => u.username === username);
+ if (!user) {
+ return res.status(401).json({ message: 'Invalid credentials' });
+ }
+
+ const validPassword = await bcrypt.compare(password, user.password);
+ if (!validPassword) {
+ return res.status(401).json({ message: 'Invalid credentials' });
+ }
+
+ const token = jwt.sign(
+ { id: user.id, username: user.username },
+ JWT_SECRET,
+ { expiresIn: '24h' }
+ );
+
+ res.json({
+ user: { id: user.id, username: user.username, email: user.email },
+ token
+ });
+ } catch (error) {
+ res.status(500).json({ message: 'Server error' });
+ }
+});
+
+// Token validation endpoint
+router.get('/validate', authenticateToken, (req, res) => {
+ const user = users.find(u => u.id === req.user.id);
+ if (!user) {
+ return res.status(404).json({ message: 'User not found' });
+ }
+
+ res.json({
+ id: user.id,
+ username: user.username,
+ email: user.email
+ });
+});
+
+// Logout endpoint
+router.post('/logout', authenticateToken, (req, res) => {
+ // In a real application, you might want to blacklist the token
+ res.json({ message: 'Logged out successfully' });
+});
+
+module.exports = router;
+```
+
+## CSS Styles
+
+### 9. Login Form Styles
+
+```css
+/* styles/login.css */
+.login-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ min-height: 100vh;
+ background-color: #f5f5f5;
+}
+
+.login-form {
+ background: white;
+ padding: 2rem;
+ border-radius: 8px;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+ width: 100%;
+ max-width: 400px;
+}
+
+.login-form h2 {
+ text-align: center;
+ margin-bottom: 2rem;
+ color: #333;
+}
+
+.form-group {
+ margin-bottom: 1rem;
+}
+
+.form-group input {
+ width: 100%;
+ padding: 0.75rem;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ font-size: 1rem;
+}
+
+.form-group input:focus {
+ outline: none;
+ border-color: #007bff;
+}
+
+.login-form button {
+ width: 100%;
+ padding: 0.75rem;
+ background-color: #007bff;
+ color: white;
+ border: none;
+ border-radius: 4px;
+ font-size: 1rem;
+ cursor: pointer;
+ transition: background-color 0.2s;
+}
+
+.login-form button:hover {
+ background-color: #0056b3;
+}
+
+.login-form button:disabled {
+ background-color: #6c757d;
+ cursor: not-allowed;
+}
+
+.error-message {
+ background-color: #f8d7da;
+ color: #721c24;
+ padding: 0.75rem;
+ border-radius: 4px;
+ margin-bottom: 1rem;
+ border: 1px solid #f5c6cb;
+}
+
+.loading-spinner {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ min-height: 100vh;
+}
+```
+
+## Implementation Checklist
+
+### Frontend Setup
+- [ ] Create AuthContext for state management
+- [ ] Implement LoginForm component
+- [ ] Add ProtectedRoute wrapper
+- [ ] Set up API client with token management
+- [ ] Handle token persistence in localStorage
+- [ ] Add loading states and error handling
+
+### Backend Setup
+- [ ] Create authentication middleware
+- [ ] Implement login/validate/logout endpoints
+- [ ] Set up JWT token generation and verification
+- [ ] Add password hashing (bcrypt)
+- [ ] Secure routes with authentication middleware
+
+### Security Considerations
+- [ ] Use HTTPS in production
+- [ ] Implement proper CORS policies
+- [ ] Add rate limiting to login endpoints
+- [ ] Use secure JWT secrets
+- [ ] Implement token refresh mechanism
+- [ ] Add logout functionality that invalidates tokens
+
+### Testing
+- [ ] Test login/logout flow
+- [ ] Verify protected routes work correctly
+- [ ] Test token expiration handling
+- [ ] Validate error states and user feedback
+
+## Environment Variables
+
+```bash
+# Frontend (.env)
+REACT_APP_API_URL=http://localhost:3001/api
+
+# Backend (.env)
+JWT_SECRET=your-super-secure-secret-key
+PORT=3001
+DB_CONNECTION_STRING=your-database-url
+```
+
+## Usage Instructions
+
+1. **Setup**: Wrap your app with `AuthProvider`
+2. **Protection**: Wrap protected content with `ProtectedRoute`
+3. **Authentication**: Use `useAuth()` hook to access auth state
+4. **API Calls**: Use the configured API client for authenticated requests
+
+This guide provides a complete authentication system that can be adapted to any React application with a Node.js backend.
\ No newline at end of file
diff --git a/admin/package-lock.json b/admin/package-lock.json
index 6255092..e41aef0 100644
--- a/admin/package-lock.json
+++ b/admin/package-lock.json
@@ -9,6 +9,7 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
+ "@azure/msal-browser": "^4.22.0",
"axios": "^1.6.0",
"chart.js": "^4.5.0",
"highlight.js": "^11.9.0",
@@ -22,6 +23,27 @@
"vite": "^5.0.10"
}
},
+ "node_modules/@azure/msal-browser": {
+ "version": "4.22.0",
+ "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.22.0.tgz",
+ "integrity": "sha512-JLWHzAW1aZ/L190Th56jN+2t3T1dMvXOs1obXYLEr3ZWi81vVmBCt0di3mPvTTOiWoE0Cf/4hVQ/LINilqjObA==",
+ "license": "MIT",
+ "dependencies": {
+ "@azure/msal-common": "15.12.0"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/@azure/msal-common": {
+ "version": "15.12.0",
+ "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.12.0.tgz",
+ "integrity": "sha512-4ucXbjVw8KJ5QBgnGJUeA07c8iznwlk5ioHIhI4ASXcXgcf2yRFhWzYOyWg/cI49LC9ekpFJeQtO3zjDTbl6TQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
"node_modules/@babel/helper-string-parser": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
diff --git a/admin/package.json b/admin/package.json
index 57471ff..efb8846 100644
--- a/admin/package.json
+++ b/admin/package.json
@@ -17,6 +17,7 @@
"author": "",
"license": "ISC",
"dependencies": {
+ "@azure/msal-browser": "^4.22.0",
"axios": "^1.6.0",
"chart.js": "^4.5.0",
"highlight.js": "^11.9.0",
diff --git a/admin/src/App.vue b/admin/src/App.vue
index 5feaec8..53ba2e3 100644
--- a/admin/src/App.vue
+++ b/admin/src/App.vue
@@ -18,11 +18,11 @@
{{ currentUser?.role === 'admin' ? 'đ' : 'đ¤' }}
{{ currentUser?.name }}
-
+
Limited Access ({{ currentUser.allowedAgents.length }} agents)
- Full Access
+ {{ currentUser?.allowedAgents && currentUser.allowedAgents.length === 0 ? 'No Access' : 'Full Access' }}
@@ -31,45 +31,68 @@
-
+
+