# Admin API Implementation (Account Management) This document describes the implementation of the Admin API endpoints for the MaplePress frontend, integrated with the MaplePress backend API. ## Overview The Admin API endpoints provide administrative operations for managing user accounts, specifically handling account lockouts that occur due to failed login attempts. These endpoints implement CWE-307 protection (Improper Restriction of Excessive Authentication Attempts) by allowing administrators to check lock status and manually unlock accounts. **⚠️ SECURITY**: These endpoints require admin authentication and should only be accessible to users with admin or root roles. ## Backend API Endpoints ### Check Account Lock Status **Endpoint**: `GET /api/v1/admin/account-status` **Authentication**: Required (JWT token with admin role) **Query Parameters**: `email` (required) ### Unlock Locked Account **Endpoint**: `POST /api/v1/admin/unlock-account` **Authentication**: Required (JWT token with admin role) **Source Files**: - `cloud/maplepress-backend/internal/interface/http/handler/admin/account_status_handler.go` - `cloud/maplepress-backend/internal/interface/http/handler/admin/unlock_account_handler.go` ## Request/Response Structures ### Check Account Status Request ``` GET /api/v1/admin/account-status?email=user@example.com ``` **Headers Required:** - `Authorization: JWT {access_token}` (admin role required) ### Check Account Status Response ```json { "email": "user@example.com", "is_locked": true, "failed_attempts": 5, "remaining_time": "5 minutes 30 seconds", "remaining_seconds": 330 } ``` **When Account Not Locked:** ```json { "email": "user@example.com", "is_locked": false, "failed_attempts": 2 } ``` ### Unlock Account Request ```json { "email": "user@example.com" } ``` **Headers Required:** - `Content-Type: application/json` - `Authorization: JWT {access_token}` (admin role required) ### Unlock Account Response ```json { "success": true, "message": "Account unlocked successfully", "email": "user@example.com" } ``` **When Account Not Locked:** ```json { "success": true, "message": "Account is not locked", "email": "user@example.com" } ``` ## Frontend Implementation ### AdminService (`src/services/API/AdminService.js`) Handles all admin operations for account management. **Key Features:** - Check account lock status with detailed information - Unlock locked accounts (with security event logging) - Helper to check if account needs unlocking - Client-side email validation - Remaining time formatting - Admin role enforcement with clear error messages **Methods:** #### `getAccountStatus(email)` Check if a user account is locked due to failed login attempts. ```javascript import AdminService from './services/API/AdminService'; const status = await AdminService.getAccountStatus("user@example.com"); console.log(status); // Output: // { // email: "user@example.com", // isLocked: true, // failedAttempts: 5, // remainingTime: "5 minutes 30 seconds", // remainingSeconds: 330 // } ``` **Parameters:** - `email` (string, required): User's email address to check **Returns:** ```javascript { email: string, // User's email isLocked: boolean, // Whether account is locked failedAttempts: number, // Number of failed login attempts remainingTime: string, // Human-readable time until unlock remainingSeconds: number // Seconds until automatic unlock } ``` **Throws:** - "Email is required" - If email is missing - "Email cannot be empty" - If email is empty after trimming - "Invalid email format" - If email format is invalid - "Admin authentication required. Please log in with admin credentials." - Missing/invalid admin token (401) - "Access denied. Admin privileges required for this operation." - User is not admin (403) #### `unlockAccount(email)` Unlock a user account that has been locked due to failed login attempts. ```javascript const result = await AdminService.unlockAccount("user@example.com"); console.log(result); // Output: // { // success: true, // message: "Account unlocked successfully", // email: "user@example.com" // } ``` **⚠️ SECURITY EVENT**: This operation logs a security event (`ACCOUNT_UNLOCKED`) with the admin user ID who performed the unlock operation. This creates an audit trail for security compliance. **Parameters:** - `email` (string, required): User's email address to unlock **Returns:** ```javascript { success: boolean, message: string, email: string } ``` **Throws:** - "Email is required" - If email is missing - "Email cannot be empty" - If email is empty after trimming - "Invalid email format" - If email format is invalid - "Account is not currently locked." - If account is not locked - "Admin authentication required. Please log in with admin credentials." - Missing/invalid admin token (401) - "Access denied. Admin privileges required for this operation." - User is not admin (403) #### `needsUnlock(email)` Check if an account needs unlocking (is locked with remaining time). ```javascript const needs = await AdminService.needsUnlock("user@example.com"); console.log(needs); // true or false ``` **Parameters:** - `email` (string, required): User's email address to check **Returns:** `boolean` - True if account is locked and needs admin unlock **Use Case:** Check before showing "Unlock Account" button in admin UI. #### `validateEmail(email)` Validate email format. ```javascript const result = AdminService.validateEmail("user@example.com"); console.log(result); // { valid: true, error: null } const invalid = AdminService.validateEmail("invalid-email"); console.log(invalid); // { valid: false, error: "Invalid email format" } ``` **Returns:** `{ valid: boolean, error: string|null }` **Validation Rules:** - Required (non-empty) - Valid email format (`user@domain.com`) - Maximum 255 characters #### `formatRemainingTime(seconds)` Format remaining time for display. ```javascript const formatted = AdminService.formatRemainingTime(330); console.log(formatted); // "5 minutes 30 seconds" const oneHour = AdminService.formatRemainingTime(3661); console.log(oneHour); // "1 hour 1 minute 1 second" ``` **Returns:** `string` (e.g., "5 minutes 30 seconds", "1 hour", "30 seconds") ## Data Flow ### Check Account Status Flow ``` Admin provides email to check ↓ AdminService.getAccountStatus() ↓ Validate email format (client-side) ↓ ApiClient.get() with JWT token (admin role) ↓ Token automatically refreshed if needed ↓ GET /api/v1/admin/account-status?email=... ↓ Backend checks Redis for lock status ↓ Backend returns lock info and failed attempts ↓ AdminService transforms response ↓ Component displays lock status ``` ### Unlock Account Flow ``` Admin requests account unlock ↓ AdminService.unlockAccount() ↓ Validate email format (client-side) ↓ ApiClient.post() with JWT token (admin role) ↓ Token automatically refreshed if needed ↓ POST /api/v1/admin/unlock-account ↓ Backend checks if account is locked ↓ Backend clears lock status in Redis ↓ Backend logs security event (ACCOUNT_UNLOCKED) ↓ Backend returns success response ↓ AdminService transforms response ↓ Component displays unlock confirmation ``` ## Error Handling ### Error Types | Error Condition | Response | Frontend Behavior | |----------------|----------|-------------------| | Missing admin authentication | 401 Unauthorized | "Admin authentication required." | | Insufficient privileges | 403 Forbidden | "Access denied. Admin privileges required." | | Invalid email | 400 Bad Request | Specific validation error | | Account not locked (unlock) | 200 OK | "Account is not locked" (success) | | Server error | 500 Internal Server Error | Generic error message | ## Usage Examples ### Admin Panel - Check Account Status ```javascript import React, { useState } from 'react'; import AdminService from '../../services/API/AdminService'; function AccountStatusChecker() { const [email, setEmail] = useState(''); const [status, setStatus] = useState(null); const [error, setError] = useState(''); const [loading, setLoading] = useState(false); const handleCheck = async (e) => { e.preventDefault(); setError(''); setStatus(null); setLoading(true); // Validate before sending const validation = AdminService.validateEmail(email); if (!validation.valid) { setError(validation.error); setLoading(false); return; } try { const accountStatus = await AdminService.getAccountStatus(email); setStatus(accountStatus); } catch (err) { setError(err.message); } finally { setLoading(false); } }; return (
🔒 Account is LOCKED
Failed Attempts: {status.failedAttempts}
Automatic Unlock In: {status.remainingTime}
({status.remainingSeconds} seconds remaining)
✓ Account is NOT locked
Failed Attempts: {status.failedAttempts}
⚠️ Admin action: This operation will be logged for security audit.
{error}
} {status && (Status:{" "} {status.isLocked ? "🔒 LOCKED" : "✓ Not Locked"}
Failed Attempts: {status.failedAttempts}
{status.isLocked && ( <>Automatic Unlock In: {status.remainingTime}
({status.remainingSeconds} seconds)
> )}| Failed Attempts | Unlocks In | Action | |
|---|---|---|---|
| {user.email} | {user.failedAttempts} | {user.remainingTime} |