8.0 KiB
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):
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):
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
{
"email": "admin@example.com",
"password": "your-password"
}
Option 2: Phone + Password (if backend supports)
{
"phone": "+1234567890",
"password": "your-password"
}
Example Request
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)
{
"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/1.1 200 OK
Set-Cookie: access_token=<jwt>; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=900
Set-Cookie: refresh_token=<jwt>; 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
{
"message": "Invalid email or password",
"statusCode": 401
}
403 Forbidden - Account inactive or not admin
{
"message": "Account is inactive",
"statusCode": 403
}
400 Bad Request - Validation error
{
"message": "Validation failed",
"errors": [
{
"field": "email",
"message": "Invalid email format"
}
],
"statusCode": 400
}
429 Too Many Requests - Rate limit exceeded
{
"message": "Too many login attempts. Please try again later.",
"statusCode": 429,
"retryAfter": 900
}
500 Internal Server Error - Server error
{
"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
- Validate response contains user data
- Check if user.role === 'ADMIN'
- Store access_token (if provided)
- Store user data in localStorage
- Show success toast
- 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
- Password Hashing: bcrypt with salt rounds >= 12
- Rate Limiting: 5 attempts per 15 minutes per IP
- Account Lockout: Lock after 5 failed attempts
- Role Verification: Ensure user.role === 'ADMIN'
- Active Status Check: Verify user.isActive === true
- Token Generation: JWT with proper expiration
- Audit Logging: Log all login attempts
Recommended
- httpOnly Cookies: Store tokens in cookies, not response body
- Refresh Tokens: Long-lived tokens for session renewal
- 2FA Support: Two-factor authentication
- IP Whitelisting: Restrict admin access by IP
- Session Management: Track active sessions
- Email Notifications: Alert on new login
Testing
Manual Testing
# 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)
VITE_BACKEND_API_URL=http://localhost:3000/api/v1
Production (.env.production)
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:
- HTTPS only in production
- Cookie support enabled
- Role verification
- Error handling
- Loading states
- Form validation
- Token storage
- 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
Support
For issues with login:
- Check browser console for errors
- Check network tab for API response
- Verify environment variables
- Check backend logs
- Test with curl/Postman