# Login API Documentation ## Endpoint ``` POST /api/v1/auth/login ``` ## Description Login user with email or phone number. This endpoint authenticates users using either email address or phone number along with password. ## Current Implementation ### Frontend Code **API Client** (`src/lib/api-client.ts`): ```typescript export const adminApiHelpers = { // Auth - uses publicApi (no token required) login: (data: { email: string; password: string }) => publicApi.post('/auth/login', data), // ... } ``` **Login Page** (`src/pages/login/index.tsx`): ```typescript const handleLogin = async (e: React.FormEvent) => { e.preventDefault() setIsLoading(true) try { const response = await adminApiHelpers.login({ email, password }) const { access_token, user } = response.data // Check if user is admin if (user.role !== 'ADMIN') { toast.error("Access denied. Admin privileges required.") return } // Store credentials if (access_token) { localStorage.setItem('access_token', access_token) } localStorage.setItem('user', JSON.stringify(user)) toast.success("Login successful!") navigate(from, { replace: true }) } catch (error: any) { const message = error.response?.data?.message || "Invalid email or password" toast.error(message) } finally { setIsLoading(false) } } ``` ## Request ### Headers ``` Content-Type: application/json ``` ### Body (JSON) **Option 1: Email + Password** ```json { "email": "admin@example.com", "password": "your-password" } ``` **Option 2: Phone + Password** (if backend supports) ```json { "phone": "+1234567890", "password": "your-password" } ``` ### Example Request ```bash curl -X POST https://api.yourdomain.com/api/v1/auth/login \ -H "Content-Type: application/json" \ -d '{ "email": "admin@example.com", "password": "password123" }' ``` ## Response ### Success Response (200 OK) **Option 1: With Access Token in Body** (localStorage fallback) ```json { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "user": { "id": "user-id-123", "email": "admin@example.com", "firstName": "John", "lastName": "Doe", "role": "ADMIN", "isActive": true, "createdAt": "2024-01-01T00:00:00.000Z" } } ``` **Option 2: With httpOnly Cookies** (recommended) ```http HTTP/1.1 200 OK Set-Cookie: access_token=; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=900 Set-Cookie: refresh_token=; HttpOnly; Secure; SameSite=Strict; Path=/auth/refresh; Max-Age=604800 Content-Type: application/json { "user": { "id": "user-id-123", "email": "admin@example.com", "firstName": "John", "lastName": "Doe", "role": "ADMIN", "isActive": true, "createdAt": "2024-01-01T00:00:00.000Z" } } ``` ### Error Responses **401 Unauthorized** - Invalid credentials ```json { "message": "Invalid email or password", "statusCode": 401 } ``` **403 Forbidden** - Account inactive or not admin ```json { "message": "Account is inactive", "statusCode": 403 } ``` **400 Bad Request** - Validation error ```json { "message": "Validation failed", "errors": [ { "field": "email", "message": "Invalid email format" } ], "statusCode": 400 } ``` **429 Too Many Requests** - Rate limit exceeded ```json { "message": "Too many login attempts. Please try again later.", "statusCode": 429, "retryAfter": 900 } ``` **500 Internal Server Error** - Server error ```json { "message": "Internal server error", "statusCode": 500 } ``` ## Frontend Behavior ### 1. Form Validation - Email: Required, valid email format - Password: Required, minimum 8 characters - Show/hide password toggle ### 2. Loading State - Disable form during submission - Show "Logging in..." button text - Prevent multiple submissions ### 3. Success Flow 1. Validate response contains user data 2. Check if user.role === 'ADMIN' 3. Store access_token (if provided) 4. Store user data in localStorage 5. Show success toast 6. Redirect to dashboard or original destination ### 4. Error Handling - Display user-friendly error messages - Show toast notification - Keep form enabled for retry - Don't expose sensitive error details ### 5. Security Features - HTTPS only in production - httpOnly cookies support - CSRF protection (SameSite cookies) - Automatic token refresh - Role-based access control ## Backend Requirements ### Must Implement 1. **Password Hashing**: bcrypt with salt rounds >= 12 2. **Rate Limiting**: 5 attempts per 15 minutes per IP 3. **Account Lockout**: Lock after 5 failed attempts 4. **Role Verification**: Ensure user.role === 'ADMIN' 5. **Active Status Check**: Verify user.isActive === true 6. **Token Generation**: JWT with proper expiration 7. **Audit Logging**: Log all login attempts ### Recommended 1. **httpOnly Cookies**: Store tokens in cookies, not response body 2. **Refresh Tokens**: Long-lived tokens for session renewal 3. **2FA Support**: Two-factor authentication 4. **IP Whitelisting**: Restrict admin access by IP 5. **Session Management**: Track active sessions 6. **Email Notifications**: Alert on new login ## Testing ### Manual Testing ```bash # Test with valid credentials curl -X POST http://localhost:3000/api/v1/auth/login \ -H "Content-Type: application/json" \ -d '{"email":"admin@example.com","password":"password123"}' # Test with invalid credentials curl -X POST http://localhost:3000/api/v1/auth/login \ -H "Content-Type: application/json" \ -d '{"email":"admin@example.com","password":"wrong"}' # Test with non-admin user curl -X POST http://localhost:3000/api/v1/auth/login \ -H "Content-Type: application/json" \ -d '{"email":"user@example.com","password":"password123"}' ``` ### Automated Testing See `src/pages/login/__tests__/index.test.tsx` for component tests. ## Environment Variables ### Development (`.env`) ```env VITE_BACKEND_API_URL=http://localhost:3000/api/v1 ``` ### Production (`.env.production`) ```env VITE_BACKEND_API_URL=https://api.yourdomain.com/api/v1 ``` ## API Client Configuration The login endpoint uses the `publicApi` instance which: - Does NOT require authentication - Does NOT send Authorization header - DOES send cookies (`withCredentials: true`) - DOES handle CORS properly ## Flow Diagram ``` User enters credentials ↓ Form validation ↓ POST /api/v1/auth/login ↓ Backend validates credentials ↓ Backend checks role === 'ADMIN' ↓ Backend generates tokens ↓ Backend returns user + tokens ↓ Frontend checks role === 'ADMIN' ↓ Frontend stores tokens ↓ Frontend redirects to dashboard ``` ## Security Checklist Backend: - [ ] Passwords hashed with bcrypt - [ ] Rate limiting enabled - [ ] Account lockout implemented - [ ] HTTPS enforced - [ ] CORS configured properly - [ ] httpOnly cookies used - [ ] Audit logging enabled - [ ] Input validation - [ ] SQL injection prevention Frontend: - [x] HTTPS only in production - [x] Cookie support enabled - [x] Role verification - [x] Error handling - [x] Loading states - [x] Form validation - [x] Token storage - [x] Automatic token refresh ## Troubleshooting ### Issue: "Network Error" **Solution:** Check API URL in environment variables ### Issue: "CORS Error" **Solution:** Backend must allow credentials and specific origin ### Issue: "Invalid credentials" for valid user **Solution:** Check backend password hashing and comparison ### Issue: "Access denied" for admin user **Solution:** Verify user.role === 'ADMIN' in database ### Issue: Token not persisting **Solution:** Check if backend is setting httpOnly cookies or returning access_token ## Related Documentation - [Authentication Setup](./AUTHENTICATION.md) - [API Standards](./API_STANDARDS.md) - [Security Checklist](./SECURITY_CHECKLIST.md) - [Backend Requirements](./SECURITY_CHECKLIST.md#backend-security) ## Support For issues with login: 1. Check browser console for errors 2. Check network tab for API response 3. Verify environment variables 4. Check backend logs 5. Test with curl/Postman