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

15 KiB

Hello API Implementation

This document describes the implementation of the Hello API endpoint for the MaplePress frontend, integrated with the MaplePress backend API.

Overview

The Hello API is a simple authenticated endpoint that returns a personalized greeting message. It demonstrates JWT authentication and can be used to verify that access tokens are working correctly. This is useful for testing authentication flows and ensuring the token refresh system is functioning properly.

Backend API Endpoint

Endpoint: POST /api/v1/hello Authentication: Required (JWT token) Documentation: /cloud/maplepress-backend/docs/API.md (lines 326-372)

Request Structure

{
  "name": "Alice"
}

Headers Required:

  • Content-Type: application/json
  • Authorization: JWT {access_token}

Response Structure

{
  "message": "Hello, Alice! Welcome to MaplePress Backend."
}

Validation Rules

Backend Validation:

  • Name is required (cannot be empty)
  • Name length: 1-100 characters
  • Must contain only printable characters
  • No HTML tags allowed (XSS prevention)
  • Input is sanitized and HTML-escaped

Security Features:

  • CWE-20: Comprehensive input validation
  • CWE-79: XSS prevention (HTML escaping)
  • CWE-117: Log injection prevention (name is hashed in logs)
  • CWE-436: Strict Content-Type validation

Frontend Implementation

HelloService (src/services/API/HelloService.js)

Handles direct communication with the backend Hello API.

Key Features:

  • Client-side validation before API call
  • Request body formatting
  • Authenticated requests (uses JWT token)
  • User-friendly error message mapping
  • XSS prevention (HTML tag validation)

Methods:

hello(name)

Main method to send a hello request.

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

const response = await HelloService.hello("Alice");
console.log(response.message);
// Output: "Hello, Alice! Welcome to MaplePress Backend."

Parameters:

  • name (string, required): Name to include in greeting (1-100 characters)

Returns:

{
  message: string  // Personalized greeting message
}

Throws:

  • "Name is required" - If name is missing or not a string
  • "Name cannot be empty" - If name is empty after trimming
  • "Name must be 100 characters or less" - If name exceeds limit
  • "Name cannot contain HTML tags" - If name contains <> tags
  • "Authentication required. Please log in to continue." - If JWT token is invalid/expired
  • "Invalid name provided. Please check your input." - If backend validation fails
  • Generic error message for other failures

validateName(name)

Client-side validation helper to check name before sending.

const validation = HelloService.validateName("Alice");
if (!validation.valid) {
  console.error(validation.error);
} else {
  // Proceed with API call
}

Returns:

{
  valid: boolean,      // true if name is valid
  error: string|null   // Error message if invalid, null if valid
}

Data Flow

User provides name
    ↓
HelloService.validateName() (optional client-side check)
    ↓
HelloService.hello(name)
    ↓
ApiClient.post() with JWT token
    ↓
Token automatically refreshed if needed (ApiClient feature)
    ↓
POST /api/v1/hello with Authorization header
    ↓
Backend validates JWT token
    ↓
Backend validates name (length, characters, no HTML)
    ↓
Backend sanitizes and HTML-escapes name
    ↓
Backend returns personalized greeting
    ↓
Frontend receives response

Validation Rules

Client-Side Validation

  1. Required: Name cannot be null, undefined, or empty string
  2. Type: Must be a string
  3. Length: 1-100 characters (after trimming)
  4. HTML Tags: Must not contain < or > characters
  5. Printable: No control characters (0x00-0x1F, 0x7F-0x9F)

Backend Validation

  1. Required: Name field must be present and non-empty
  2. Length: 1-100 characters
  3. Printable: Only printable characters allowed
  4. No HTML: Validated for HTML tags (XSS prevention)
  5. Sanitization: Input is sanitized and HTML-escaped
  6. Logging: Name is hashed in logs (PII protection)

Error Handling

Error Types

Error Condition Response Frontend Behavior
Name missing 400 Bad Request "Name is required"
Name empty 400 Bad Request "Name cannot be empty"
Name too long 400 Bad Request "Name must be 100 characters or less"
HTML tags in name 400 Bad Request "Name cannot contain HTML tags"
Invalid JWT token 401 Unauthorized "Authentication required. Please log in to continue."
JWT token expired 401 Unauthorized Token auto-refresh triggered, then retry

Error Message Mapping

// Backend error → Frontend error
"unauthorized"  "Authentication required. Please log in to continue."
"name"  "Invalid name provided. Please check your input."
Other errors  Original error message or generic fallback

Security Features

  1. Authentication Required: Endpoint requires valid JWT token
  2. XSS Prevention: HTML tags are rejected both client and server-side
  3. Input Sanitization: Backend sanitizes and HTML-escapes all input
  4. Length Limits: Prevents buffer overflow attacks
  5. Printable Characters Only: Prevents control character injection
  6. PII Protection: Name is hashed in backend logs
  7. Automatic Token Refresh: ApiClient ensures token is valid before request

Usage Examples

Basic Usage in React Component

import React, { useState } from 'react';
import { useAuth } from '../services/Services';
import HelloService from '../services/API/HelloService';

function HelloExample() {
  const { authManager } = useAuth();
  const [name, setName] = useState('');
  const [message, setMessage] = useState('');
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(false);

  const handleSubmit = async (e) => {
    e.preventDefault();
    setError('');
    setMessage('');
    setLoading(true);

    // Check authentication
    if (!authManager.isAuthenticated()) {
      setError('Please log in first');
      setLoading(false);
      return;
    }

    // Optional: Validate before sending
    const validation = HelloService.validateName(name);
    if (!validation.valid) {
      setError(validation.error);
      setLoading(false);
      return;
    }

    try {
      const response = await HelloService.hello(name);
      setMessage(response.message);
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <input
          type="text"
          value={name}
          onChange={(e) => setName(e.target.value)}
          placeholder="Enter your name"
          maxLength={100}
        />
        <button type="submit" disabled={loading}>
          {loading ? 'Sending...' : 'Say Hello'}
        </button>
      </form>

      {message && <p className="success">{message}</p>}
      {error && <p className="error">{error}</p>}
    </div>
  );
}

export default HelloExample;

Testing Authentication

// Test if authentication is working
import HelloService from './services/API/HelloService';

async function testAuthentication() {
  try {
    const response = await HelloService.hello("Test User");
    console.log("✅ Authentication working:", response.message);
    return true;
  } catch (error) {
    console.error("❌ Authentication failed:", error.message);
    return false;
  }
}

Validation Testing

// Test validation rules
const testCases = [
  { name: "", expected: "Name cannot be empty" },
  { name: "Alice", expected: null },
  { name: "<script>alert('xss')</script>", expected: "Name cannot contain HTML tags" },
  { name: "A".repeat(101), expected: "Name must be 100 characters or less" },
];

testCases.forEach(({ name, expected }) => {
  const result = HelloService.validateName(name);
  console.log(`Input: "${name}"`);
  console.log(`Expected: ${expected}`);
  console.log(`Got: ${result.error}`);
  console.log(`✓ Pass: ${result.error === expected}\n`);
});

Testing the Hello API

1. Prerequisites

  • Backend running at http://localhost:8000
  • Frontend running at http://localhost:5173
  • User logged in (valid JWT token)

2. Testing via Dashboard

You can add a simple test component to the dashboard:

// Add to Dashboard.jsx
import HelloService from '../../services/API/HelloService';

// Inside Dashboard component
const [helloMessage, setHelloMessage] = useState('');

const testHello = async () => {
  try {
    const response = await HelloService.hello(user.name);
    setHelloMessage(response.message);
  } catch (error) {
    console.error('Hello test failed:', error);
  }
};

// In JSX
<button onClick={testHello}>Test Hello API</button>
{helloMessage && <p>{helloMessage}</p>}

3. Testing via Browser Console

// After logging in, open browser console

// Test basic hello
const response = await window.HelloService.hello("Alice");
console.log(response.message);
// Expected: "Hello, Alice! Welcome to MaplePress Backend."

// Test validation
const validation = window.HelloService.validateName("<script>test</script>");
console.log(validation);
// Expected: { valid: false, error: "Name cannot contain HTML tags" }

// Test with empty name
try {
  await window.HelloService.hello("");
} catch (error) {
  console.log(error.message);
  // Expected: "Name cannot be empty"
}

4. Testing with curl

# 1. Login and get access token
ACCESS_TOKEN=$(curl -X POST http://localhost:8000/api/v1/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "test@example.com",
    "password": "SecurePass123!"
  }' | jq -r '.access_token')

# 2. Test hello endpoint
curl -X POST http://localhost:8000/api/v1/hello \
  -H "Content-Type: application/json" \
  -H "Authorization: JWT $ACCESS_TOKEN" \
  -d '{"name": "Alice"}' | jq

# Expected output:
# {
#   "message": "Hello, Alice! Welcome to MaplePress Backend."
# }

# 3. Test with empty name (should fail)
curl -X POST http://localhost:8000/api/v1/hello \
  -H "Content-Type: application/json" \
  -H "Authorization: JWT $ACCESS_TOKEN" \
  -d '{"name": ""}' | jq

# Expected: 400 Bad Request

# 4. Test without authentication (should fail)
curl -X POST http://localhost:8000/api/v1/hello \
  -H "Content-Type: application/json" \
  -d '{"name": "Alice"}' | jq

# Expected: 401 Unauthorized

5. Testing Token Refresh Integration

// Test that token refresh works automatically

// 1. Login
// 2. Manually set token expiry to 30 seconds from now
const newExpiry = new Date(Date.now() + 30000).toISOString();
localStorage.setItem('maplepress_access_expiry', newExpiry);

// 3. Wait 31 seconds, then call hello
await new Promise(resolve => setTimeout(resolve, 31000));
const response = await HelloService.hello("Test");

// 4. Check console logs - should see automatic token refresh
// 5. Response should be successful despite expired token
console.log(response.message);

Integration with Existing Services

The HelloService automatically integrates with existing infrastructure:

Authentication (AuthManager)

// HelloService uses ApiClient, which automatically:
// - Adds JWT token to Authorization header
// - Refreshes token if expired
// - Handles 401 errors

Token Refresh (RefreshTokenService)

// Automatic token refresh before hello request
// If token expires within 1 minute, it's refreshed proactively
// If 401 received, token is refreshed and request is retried

Error Handling

// All errors are caught and mapped to user-friendly messages
// Authentication errors trigger automatic login redirect (in components)

Use Cases

1. Authentication Testing

Use the Hello endpoint to verify that JWT authentication is working correctly after login or token refresh.

// After login
const testAuth = async () => {
  try {
    await HelloService.hello("Test");
    console.log("✅ Authentication successful");
  } catch (error) {
    console.error("❌ Authentication failed");
    // Redirect to login
  }
};

2. Token Validity Check

Check if the current access token is valid without making a critical API call.

// Before important operation
const isTokenValid = async () => {
  try {
    await HelloService.hello("TokenCheck");
    return true;
  } catch (error) {
    return false;
  }
};

3. User Greeting

Display a personalized welcome message on the dashboard.

// On dashboard load
useEffect(() => {
  const greetUser = async () => {
    try {
      const response = await HelloService.hello(user.name);
      setGreeting(response.message);
    } catch (error) {
      console.error("Failed to load greeting:", error);
    }
  };

  greetUser();
}, [user.name]);

Troubleshooting

"Authentication required" error

Possible causes:

  1. User not logged in
  2. Access token expired
  3. Refresh token expired
  4. Session invalidated

Solution:

  • Check authManager.isAuthenticated() before calling
  • Login again if session expired
  • Check browser console for token refresh logs

"Name cannot contain HTML tags" error

Possible causes:

  1. User input contains < or > characters
  2. Attempt to inject HTML/JavaScript

Solution:

  • Sanitize user input before calling HelloService
  • Use validateName() to check input first
  • Inform user about character restrictions

Request succeeds but token expired shortly after

Possible causes:

  1. Token was refreshed but expiry is still 15 minutes
  2. High request frequency without refresh

Solution:

  • Token automatically refreshes 1 minute before expiry
  • This is expected behavior (15-minute access token lifetime)
  • Refresh token handles session extension

401 error despite valid token

Possible causes:

  1. Clock skew between client and server
  2. Token format incorrect
  3. Backend session invalidated

Solution:

  • Check system clock synchronization
  • Verify token in localStorage is properly formatted
  • Clear session and login again

Created Files

src/services/API/HelloService.js
docs/HELLO_API.md

Backend Reference Files

cloud/maplepress-backend/docs/API.md (lines 326-372)
cloud/maplepress-backend/internal/interface/http/handler/gateway/hello_handler.go

Summary

The Hello API implementation provides:

  1. Simple Authentication Test: Verify JWT tokens are working
  2. Security Features: XSS prevention, input validation, sanitization
  3. User-Friendly: Personalized greeting messages
  4. Automatic Integration: Works seamlessly with token refresh
  5. Error Handling: Clear error messages and graceful failures
  6. Validation Helpers: Client-side validation before API calls

This endpoint is perfect for testing authentication flows and demonstrating the three-layer service architecture in action.


Last Updated: October 30, 2024 Frontend Version: 0.0.0 Documentation Version: 1.0.0