monorepo/web/maplepress-frontend/docs/API/LOGIN_API.md

12 KiB

Login API Implementation

This document describes the complete implementation of the user login feature for the MaplePress frontend, integrated with the MaplePress backend API.

Overview

The login feature allows existing users to authenticate with their email and password credentials. Upon successful login, users receive authentication tokens and are automatically logged in to their dashboard.

Backend API Endpoint

Endpoint: POST /api/v1/login Authentication: None required (public endpoint) Documentation: /cloud/maplepress-backend/docs/API.md (lines 168-228)

Request Structure

{
  "email": "user@example.com",
  "password": "SecurePassword123!"
}

Response Structure

{
  "user_id": "550e8400-e29b-41d4-a716-446655440000",
  "user_email": "user@example.com",
  "user_name": "John Doe",
  "user_role": "user",
  "tenant_id": "650e8400-e29b-41d4-a716-446655440000",
  "session_id": "750e8400-e29b-41d4-a716-446655440000",
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "access_expiry": "2024-10-24T12:15:00Z",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_expiry": "2024-10-31T00:00:00Z",
  "login_at": "2024-10-24T00:00:00Z"
}

Key Differences from Registration

The login endpoint differs from registration in several ways:

  • Simpler Request: Only email and password required
  • No Tenant Details in Response: Tenant name/slug not included
  • Login Timestamp: Includes login_at instead of created_at
  • Existing Session: Authenticates existing user, doesn't create new account

Frontend Implementation

1. LoginService (src/services/API/LoginService.js)

Handles direct communication with the backend login API.

Key Features:

  • Request validation (required fields)
  • Request body formatting (snake_case for backend)
  • Response transformation (camelCase for frontend)
  • User-friendly error message mapping
  • Rate limit error handling
  • Account lockout detection

Methods:

  • login(credentials) - Main login method

Usage:

import LoginService from './services/API/LoginService';

const response = await LoginService.login({
  email: "user@example.com",
  password: "SecurePassword123!",
});

2. AuthManager Enhancement (src/services/Manager/AuthManager.js)

Updated to support login functionality while maintaining registration support.

New/Updated Methods:

  • login(email, password) - Login and store auth data
  • storeAuthData(authResponse) - Updated to handle optional tenant fields

Key Features:

  • Handles both registration and login responses
  • Gracefully handles missing tenant name/slug from login
  • Maintains same token storage mechanism
  • Consistent session management

Login Flow:

const authManager = useAuth().authManager;

// Login
await authManager.login("user@example.com", "password123");

// Check authentication
if (authManager.isAuthenticated()) {
  const user = authManager.getUser();
  const tenant = authManager.getTenant();
}

3. Login Page (src/pages/Auth/Login.jsx)

Simple and clean login form ready for production use.

Form Fields:

  • Email Address (required)
  • Password (required)

Features:

  • Email validation (HTML5 + backend)
  • Loading state during submission
  • Error message display
  • Navigation to registration
  • Navigation back to home

Data Flow

User enters credentials
    ↓
Login.jsx validates data
    ↓
AuthManager.login(email, password)
    ↓
LoginService.login(credentials)
    ↓
HTTP POST to /api/v1/login
    ↓
Backend validates credentials
    ↓
Backend returns tokens and user data
    ↓
LoginService transforms response
    ↓
AuthManager stores tokens in localStorage
    ↓
User redirected to /dashboard

Validation Rules

Frontend Validation

  1. Email: Required, valid email format (HTML5)
  2. Password: Required (no client-side length check for login)

Backend Validation

Backend performs:

  • Email format validation
  • Email normalization (lowercase, trim)
  • Password verification against stored hash
  • Rate limiting checks
  • Account lockout checks

Error Handling

Error Messages

Errors are mapped to user-friendly messages:

Backend Error Frontend Message
"Invalid email or password" "Invalid email or password. Please try again."
"X attempts remaining" Original message with attempt counter
"locked" or "Too many" "Account temporarily locked due to too many failed attempts. Please try again later."
"invalid email" "Invalid email address."

Rate Limiting & Account Lockout

The backend implements sophisticated rate limiting:

Per-IP Rate Limiting:

  • Limit: Multiple attempts per 15 minutes
  • Response: 429 Too Many Requests
  • Header: Retry-After: 900 (15 minutes)

Per-Account Rate Limiting:

  • Limit: 5 failed attempts
  • Lockout: 30 minutes after 5 failures
  • Response: 429 Too Many Requests
  • Header: Retry-After: 1800 (30 minutes)
  • Warning: Shows remaining attempts when ≤ 3

Behavior:

  1. First 2 failures: Generic error message
  2. 3rd-5th failure: Shows remaining attempts
  3. After 5th failure: Account locked for 30 minutes
  4. Successful login: Resets all counters

Security Event Logging

Backend logs security events for:

  • Failed login attempts (with email hash)
  • Successful logins (with email hash)
  • IP rate limit exceeded
  • Account lockouts

Security Features

  1. Token Storage: Tokens stored in localStorage
  2. Token Expiry: Automatic expiry checking
  3. Rate Limiting: Backend enforces rate limits
  4. Account Lockout: Protects against brute force
  5. Email Normalization: Prevents bypass via casing
  6. Secure Logging: PII never logged, only hashes
  7. Password Hash Verification: Uses bcrypt on backend

Token Lifetime

  • Access Token: 15 minutes
  • Refresh Token: 7 days
  • Session: 14 days (max inactivity)

Testing the Login Flow

1. Prerequisites

Ensure you have a registered user. If not, register first:

# Navigate to registration
http://localhost:5173/register

# Or use curl to register via backend
curl -X POST http://localhost:8000/api/v1/register \
  -H "Content-Type: application/json" \
  -d '{
    "email": "test@example.com",
    "password": "SecurePass123!",
    "name": "Test User",
    "tenant_name": "Test Corp",
    "tenant_slug": "test-corp",
    "agree_terms_of_service": true
  }'

2. Start Services

# Backend
cd cloud/maplepress-backend
task dev

# Frontend
cd web/maplepress-frontend
npm run dev

3. Navigate to Login

Open browser to: http://localhost:5173/login

4. Fill Form

5. Submit

Click "Sign In" button

6. Expected Result

  • Loading state appears ("Signing in...")
  • Request sent to backend
  • Tokens stored in localStorage
  • User redirected to /dashboard
  • Dashboard shows user information

7. Verify in Browser Console

// Check stored tokens
localStorage.getItem('maplepress_access_token')
localStorage.getItem('maplepress_user')
localStorage.getItem('maplepress_tenant')

// Check service instance
window.maplePressServices.authManager.isAuthenticated() // true
window.maplePressServices.authManager.getUser()
// Returns: { id: "...", email: "test@example.com", name: "Test User", role: "..." }

window.maplePressServices.authManager.getTenant()
// Returns: { id: "...", name: null, slug: null }
// Note: name/slug are null because login endpoint doesn't provide them

Error Testing

Test Invalid Credentials

// In login form, enter:
Email: test@example.com
Password: WrongPassword123

// Expected: "Invalid email or password. Please try again."

Test Rate Limiting

// Attempt login 3 times with wrong password
// Expected on 3rd attempt: "Invalid email or password. 2 attempts remaining before account lockout."

// Attempt 2 more times
// Expected on 5th attempt: "Account temporarily locked due to too many failed attempts. Please try again later."

Test Missing Fields

// Leave email blank
// Expected: Browser validation error (HTML5 required)

// Leave password blank
// Expected: Browser validation error (HTML5 required)

Differences from Registration

Feature Registration Login
Request Fields 10+ fields 2 fields only
Response Fields Includes tenant name/slug No tenant name/slug
Timestamp created_at login_at
Creates User Yes No
Creates Tenant Yes No
Terms Agreement Required Not needed
Organization Info Required Not needed

Token Storage Compatibility

Both registration and login use the same storage mechanism:

// Storage Keys (7 total)
maplepress_access_token     // JWT access token
maplepress_refresh_token    // JWT refresh token
maplepress_access_expiry    // ISO date string
maplepress_refresh_expiry   // ISO date string
maplepress_user            // JSON: {id, email, name, role}
maplepress_tenant          // JSON: {id, name, slug}
maplepress_session_id      // Session UUID

Note: After login, tenant.name and tenant.slug will be null. This is expected behavior. If needed, fetch tenant details separately using the /api/v1/tenants/{id} endpoint.

Session Persistence

Authentication state persists across:

  • Page refreshes
  • Browser restarts (if localStorage not cleared)
  • Tab changes

Session is cleared on:

  • User logout
  • Token expiry detection
  • Manual localStorage clear

Integration with Dashboard

The dashboard automatically checks authentication:

// Dashboard.jsx
useEffect(() => {
  if (!authManager.isAuthenticated()) {
    navigate("/login");
    return;
  }

  const userData = authManager.getUser();
  setUser(userData);
}, [authManager, navigate]);

API Client Configuration

The ApiClient automatically handles:

  • JSON content-type headers
  • Request/response transformation
  • Error parsing
  • Authentication headers (for protected endpoints)

Note: Login endpoint doesn't require authentication headers, but subsequent API calls will use the stored access token.

Future Enhancements

Planned Features

  • "Remember Me" checkbox (longer session)
  • "Forgot Password" link
  • Social authentication (Google, GitHub)
  • Two-factor authentication (2FA/TOTP)
  • Session management (view active sessions)
  • Device fingerprinting
  • Suspicious login detection

Security Improvements

  • CSRF token implementation
  • HTTP-only cookie option
  • Session fingerprinting
  • Geolocation tracking
  • Email notification on new login
  • Passwordless login option

Troubleshooting

"Invalid email or password" but credentials are correct

Possible causes:

  1. Email case sensitivity - backend normalizes to lowercase
  2. Extra whitespace in password field
  3. User not yet registered
  4. Account locked due to previous failed attempts

Solution:

  • Wait 30 minutes if account is locked
  • Try registering if user doesn't exist
  • Check browser console for detailed errors

Tokens not being stored

Possible causes:

  1. localStorage disabled in browser
  2. Private/Incognito mode restrictions
  3. Browser extension blocking storage

Solution:

  • Enable localStorage in browser settings
  • Use regular browser window
  • Disable blocking extensions temporarily

Redirected back to login after successful login

Possible causes:

  1. Token expiry detection triggered
  2. Token format invalid
  3. localStorage cleared between operations

Solution:

  • Check browser console for errors
  • Verify localStorage contains tokens
  • Check token expiry dates

Created Files

src/services/API/LoginService.js

Modified Files

src/services/Manager/AuthManager.js
src/pages/Auth/Login.jsx

Backend Reference Files

cloud/maplepress-backend/docs/API.md
cloud/maplepress-backend/internal/interface/http/dto/gateway/login_dto.go
cloud/maplepress-backend/internal/interface/http/handler/gateway/login_handler.go

Support

For issues:

  1. Check backend logs: docker logs mapleopentech_backend
  2. Check browser console for errors
  3. Verify backend is running on port 8000
  4. Test backend endpoint directly with curl
  5. Check rate limiting status (wait 30 minutes if locked)