# Me API Implementation (User Profile)

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

## Overview

The Me API endpoint returns the authenticated user's profile information extracted directly from the JWT token. This is a lightweight endpoint that requires no database queries, making it ideal for displaying user information in the dashboard and verifying the current user's identity.

## Backend API Endpoint

**Endpoint**: `GET /api/v1/me`
**Authentication**: Required (JWT token)
**Documentation**: `/cloud/maplepress-backend/docs/API.md` (lines 374-413)

### Request Structure

No request body required. Authentication is provided via the Authorization header:

**Headers Required:**
- `Authorization: JWT {access_token}`

### Response Structure

```json
{
  "user_id": "550e8400-e29b-41d4-a716-446655440000",
  "email": "john@example.com",
  "name": "John Doe",
  "role": "owner",
  "tenant_id": "650e8400-e29b-41d4-a716-446655440000"
}
```

### Key Features

- **No Database Query**: All data extracted from JWT token claims
- **Fast Response**: Minimal processing required
- **Current User Only**: Returns data for the authenticated user
- **Role Information**: Includes user's role for authorization checks
- **Tenant Context**: Includes tenant ID for multi-tenant operations

## Frontend Implementation

### MeService (`src/services/API/MeService.js`)

Handles direct communication with the backend Me API and provides utility methods for user profile operations.

**Key Features:**
- GET request with automatic JWT authentication
- Response transformation (snake_case to camelCase)
- User-friendly error message mapping
- Helper methods for common use cases
- Automatic token refresh integration

**Methods:**

#### `getMe()`
Main method to fetch the current user's profile.

```javascript
import MeService from './services/API/MeService';

const profile = await MeService.getMe();
console.log(profile);
// Output:
// {
//   userId: "550e8400-e29b-41d4-a716-446655440000",
//   email: "john@example.com",
//   name: "John Doe",
//   role: "owner",
//   tenantId: "650e8400-e29b-41d4-a716-446655440000"
// }
```

**Returns:**
```javascript
{
  userId: string,    // User's unique identifier (UUID)
  email: string,     // User's email address
  name: string,      // User's full name
  role: string,      // User's role (e.g., "owner", "admin", "user")
  tenantId: string   // User's tenant/organization ID (UUID)
}
```

**Throws:**
- "Authentication required. Please log in to continue." - If JWT token is missing/invalid
- "Invalid or expired authentication token. Please log in again." - If token has expired
- Generic error message for other failures

#### `hasRole(requiredRole)`
Check if the current user has a specific role.

```javascript
const isOwner = await MeService.hasRole("owner");
if (isOwner) {
  console.log("User is an owner");
}

const isAdmin = await MeService.hasRole("admin");
```

**Parameters:**
- `requiredRole` (string): Role to check for (e.g., "owner", "admin", "user")

**Returns:** `boolean` - True if user has the required role

#### `getTenantId()`
Get the current user's tenant ID.

```javascript
const tenantId = await MeService.getTenantId();
console.log("User's tenant:", tenantId);
```

**Returns:** `string|null` - Tenant ID or null if not available

#### `isCurrentUser(userId)`
Verify that the current user matches a specific user ID.

```javascript
const isSameUser = await MeService.isCurrentUser("550e8400-...");
if (isSameUser) {
  console.log("This is the current user");
}
```

**Parameters:**
- `userId` (string): User ID to verify against

**Returns:** `boolean` - True if the current user matches the provided ID

## Data Flow

```
User is authenticated (has JWT token)
    ↓
Component calls MeService.getMe()
    ↓
ApiClient.get("/api/v1/me")
    ↓
Token automatically refreshed if needed (ApiClient feature)
    ↓
GET /api/v1/me with Authorization header
    ↓
Backend JWT middleware extracts user from token
    ↓
Backend returns user profile from JWT claims
    ↓
MeService transforms response (snake_case → camelCase)
    ↓
Component receives user profile
```

## Error Handling

### Error Types

| Error Condition | Response | Frontend Behavior |
|----------------|----------|-------------------|
| Missing JWT token | 401 Unauthorized | "Authentication required. Please log in to continue." |
| Invalid JWT token | 401 Unauthorized | "Invalid or expired authentication token." |
| Expired JWT token | 401 Unauthorized | Token auto-refresh triggered, then retry |
| Server error | 500 Internal Server Error | Generic error message |

### Error Message Mapping

```javascript
// Backend error → Frontend error
"unauthorized" → "Authentication required. Please log in to continue."
"token" → "Invalid or expired authentication token. Please log in again."
Other errors → Original error message or generic fallback
```

## Security Features

1. **JWT Authentication Required**: Endpoint requires valid JWT token
2. **Token Validation**: Backend validates token signature and expiration
3. **No PII in Logs**: Backend uses hashed/redacted email in logs (CWE-532)
4. **Automatic Token Refresh**: ApiClient ensures token is valid before request
5. **Context Extraction**: User data extracted from secure JWT context

## Usage Examples

### Display User Profile in Dashboard

```javascript
import React, { useEffect, useState } from 'react';
import MeService from '../../services/API/MeService';

function UserProfile() {
  const [profile, setProfile] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState('');

  useEffect(() => {
    const fetchProfile = async () => {
      try {
        const data = await MeService.getMe();
        setProfile(data);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchProfile();
  }, []);

  if (loading) return <div>Loading profile...</div>;
  if (error) return <div>Error: {error}</div>;
  if (!profile) return null;

  return (
    <div className="user-profile">
      <h2>Welcome, {profile.name}!</h2>
      <p>Email: {profile.email}</p>
      <p>Role: {profile.role}</p>
      <p>User ID: {profile.userId}</p>
    </div>
  );
}

export default UserProfile;
```

### Role-Based Access Control

```javascript
import React, { useEffect, useState } from 'react';
import MeService from '../../services/API/MeService';

function AdminPanel() {
  const [isAdmin, setIsAdmin] = useState(false);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const checkAdmin = async () => {
      const hasAdminRole = await MeService.hasRole("owner");
      setIsAdmin(hasAdminRole);
      setLoading(false);
    };

    checkAdmin();
  }, []);

  if (loading) return <div>Checking permissions...</div>;

  if (!isAdmin) {
    return <div>Access denied. Admin rights required.</div>;
  }

  return (
    <div className="admin-panel">
      <h2>Admin Panel</h2>
      {/* Admin content */}
    </div>
  );
}

export default AdminPanel;
```

### Sync Profile with AuthManager

```javascript
import { useAuth } from '../../services/Services';
import MeService from '../../services/API/MeService';

function Dashboard() {
  const { authManager } = useAuth();

  useEffect(() => {
    const syncProfile = async () => {
      try {
        // Get fresh profile from backend
        const profile = await MeService.getMe();

        // Get stored profile from AuthManager
        const storedUser = authManager.getUser();

        // Check if data is in sync
        if (storedUser.email !== profile.email) {
          console.warn("Profile data mismatch detected");
          // Could update AuthManager or prompt user
        }

        console.log("Profile synced successfully");
      } catch (error) {
        console.error("Failed to sync profile:", error);
      }
    };

    syncProfile();
  }, [authManager]);

  return <div>Dashboard</div>;
}
```

### Check Tenant Context

```javascript
import MeService from '../../services/API/MeService';

async function verifyTenantAccess(requiredTenantId) {
  const userTenantId = await MeService.getTenantId();

  if (userTenantId !== requiredTenantId) {
    throw new Error("Access denied. Wrong tenant context.");
  }

  console.log("Tenant access verified");
  return true;
}

// Usage
try {
  await verifyTenantAccess("650e8400-e29b-41d4-a716-446655440000");
  // Proceed with tenant-specific operations
} catch (error) {
  console.error(error.message);
  // Redirect or show error
}
```

### Verify Current User

```javascript
import MeService from '../../services/API/MeService';

function EditProfileButton({ profileOwnerId }) {
  const [canEdit, setCanEdit] = useState(false);

  useEffect(() => {
    const checkOwnership = async () => {
      const isOwner = await MeService.isCurrentUser(profileOwnerId);
      setCanEdit(isOwner);
    };

    checkOwnership();
  }, [profileOwnerId]);

  if (!canEdit) return null;

  return <button>Edit Profile</button>;
}
```

## Testing the Me 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

Add a profile component to the dashboard:

```javascript
// In Dashboard.jsx
import MeService from '../../services/API/MeService';

const [userProfile, setUserProfile] = useState(null);

useEffect(() => {
  const loadProfile = async () => {
    try {
      const profile = await MeService.getMe();
      setUserProfile(profile);
      console.log("Profile loaded:", profile);
    } catch (error) {
      console.error("Failed to load profile:", error);
    }
  };

  loadProfile();
}, []);

// In JSX
{userProfile && (
  <div>
    <h3>{userProfile.name}</h3>
    <p>{userProfile.email}</p>
    <p>Role: {userProfile.role}</p>
  </div>
)}
```

### 3. Testing via Browser Console

```javascript
// After logging in, open browser console

// Test basic profile fetch
const profile = await MeService.getMe();
console.log(profile);
// Expected: User profile object

// Test role check
const isOwner = await MeService.hasRole("owner");
console.log("Is owner:", isOwner);

// Test tenant ID
const tenantId = await MeService.getTenantId();
console.log("Tenant ID:", tenantId);

// Test user verification
const isCurrent = await MeService.isCurrentUser(profile.userId);
console.log("Is current user:", isCurrent); // Should be true
```

### 4. Testing with curl

```bash
# 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 me endpoint
curl -X GET http://localhost:8000/api/v1/me \
  -H "Authorization: JWT $ACCESS_TOKEN" | jq

# Expected output:
# {
#   "user_id": "550e8400-...",
#   "email": "test@example.com",
#   "name": "Test User",
#   "role": "owner",
#   "tenant_id": "650e8400-..."
# }

# 3. Test without authentication (should fail)
curl -X GET http://localhost:8000/api/v1/me | jq

# Expected: 401 Unauthorized

# 4. Test with invalid token (should fail)
curl -X GET http://localhost:8000/api/v1/me \
  -H "Authorization: JWT invalid_token" | jq

# Expected: 401 Unauthorized
```

### 5. Testing Token Refresh Integration

```javascript
// 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 getMe
await new Promise(resolve => setTimeout(resolve, 31000));
const profile = await MeService.getMe();

// 4. Check console logs - should see automatic token refresh
// 5. Response should be successful despite expired token
console.log("Profile after token refresh:", profile);
```

### 6. Testing Against Stored Data

```javascript
// Compare Me API response with stored AuthManager data

import { useAuth } from './services/Services';
import MeService from './services/API/MeService';

const { authManager } = useAuth();

// Get stored user
const storedUser = authManager.getUser();
console.log("Stored user:", storedUser);

// Get fresh profile from API
const profile = await MeService.getMe();
console.log("API profile:", profile);

// Compare
console.log("Email match:", storedUser.email === profile.email);
console.log("Name match:", storedUser.name === profile.name);
console.log("Role match:", storedUser.role === profile.role);
```

## Integration with Existing Services

The MeService automatically integrates with existing infrastructure:

### Authentication (AuthManager)
```javascript
// MeService uses ApiClient, which automatically:
// - Adds JWT token to Authorization header
// - Refreshes token if expired
// - Handles 401 errors
```

### Token Refresh (RefreshTokenService)
```javascript
// Automatic token refresh before getMe request
// If token expires within 1 minute, it's refreshed proactively
// If 401 received, token is refreshed and request is retried
```

### Data Consistency
```javascript
// Me API returns data from JWT token
// AuthManager stores data from login/register/refresh
// Both should be in sync, but Me API is source of truth
```

## Use Cases

### 1. Dashboard User Display
Display the current user's name and email in the dashboard header.

```javascript
const profile = await MeService.getMe();
setDashboardHeader(profile.name, profile.email);
```

### 2. Role-Based UI
Show or hide UI elements based on user role.

```javascript
const isAdmin = await MeService.hasRole("owner");
setShowAdminPanel(isAdmin);
```

### 3. Tenant Context
Ensure user is operating in the correct tenant context.

```javascript
const tenantId = await MeService.getTenantId();
loadTenantSpecificData(tenantId);
```

### 4. Profile Verification
Verify that the current user matches a resource owner.

```javascript
const canEdit = await MeService.isCurrentUser(resourceOwnerId);
```

### 5. Session Validation
Periodically verify that the session is still valid.

```javascript
setInterval(async () => {
  try {
    await MeService.getMe();
    console.log("Session still valid");
  } catch (error) {
    console.log("Session expired, logging out");
    authManager.logout();
  }
}, 5 * 60 * 1000); // Every 5 minutes
```

## Data Comparison: JWT vs Stored

### JWT Token Data (from Me API)
- **Source**: JWT token claims
- **Updated**: On login, registration, or token refresh
- **Authoritative**: Yes (always current)
- **Requires Network**: Yes

### AuthManager Stored Data
- **Source**: localStorage (from login/register/refresh responses)
- **Updated**: On login, registration, or token refresh
- **Authoritative**: No (could be stale)
- **Requires Network**: No

**Recommendation**: Use Me API when you need authoritative current data, use AuthManager for quick access without network calls.

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

### Profile data doesn't match stored data

**Possible causes:**
1. Token was refreshed but AuthManager not updated
2. Profile was updated on another device
3. Data corruption in localStorage

**Solution:**
- Me API is source of truth - use its data
- Update AuthManager with fresh data if needed
- Clear localStorage and login again if corrupted

### Request succeeds but returns empty fields

**Possible causes:**
1. JWT token missing required claims
2. Backend not setting context values correctly
3. Token format incorrect

**Solution:**
- Check token structure (decode JWT at jwt.io)
- Verify backend is setting all required claims
- Re-login to get fresh token

### 401 error despite valid token

**Possible causes:**
1. Clock skew between client and server
2. Token signature invalid
3. Token expired but not detected

**Solution:**
- Check system clock synchronization
- Clear tokens and login again
- Verify token expiry dates in localStorage

## Related Files

### Created Files
```
src/services/API/MeService.js
docs/ME_API.md
```

### Backend Reference Files
```
cloud/maplepress-backend/docs/API.md (lines 374-413)
cloud/maplepress-backend/internal/interface/http/handler/gateway/me_handler.go
```

## Related Documentation

- [LOGIN_API.md](./LOGIN_API.md) - Login and JWT token acquisition
- [REFRESH_TOKEN_API.md](./REFRESH_TOKEN_API.md) - Automatic token refresh
- [HELLO_API.md](./HELLO_API.md) - Simple authenticated endpoint testing
- [FRONTEND_ARCHITECTURE.md](./FRONTEND_ARCHITECTURE.md) - Architecture overview
- [README.md](./README.md) - Documentation index
- [Backend API Documentation](../../../cloud/maplepress-backend/docs/API.md) - Complete API reference

## Summary

The Me API implementation provides:

1. **User Profile Access**: Get current user's profile from JWT token
2. **Fast & Efficient**: No database queries, data from token claims
3. **Helper Methods**: Role checking, tenant ID, user verification
4. **Automatic Integration**: Works seamlessly with token refresh
5. **Error Handling**: Clear error messages and graceful failures
6. **Use Case Focused**: Methods designed for common scenarios

This endpoint is essential for displaying user information in the dashboard and implementing role-based access control.

---

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