Yaltopia-Ticket-Admin/dev-docs/LOGIN_API_DOCUMENTATION.md

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

  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
  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

# 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

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