# Authentication & Profile API Documentation

Dành cho Frontend Team - Tài liệu API Profile, Change Password và Avatar Upload

---

## Table of Contents

- [Overview](#overview)
- [Authentication](#authentication)
- [Response Format](#response-format)
- [Profile API](#profile-api)
- [Change Password API](#change-password-api)
- [Avatar Upload Process](#avatar-upload-process)
- [Complete Workflows](#complete-workflows)
- [Error Handling](#error-handling)

---

## Overview

Các API này dùng để quản lý thông tin profile của user, thay đổi mật khẩu và upload avatar.

**Base URL**: `https://api.example.com/api/v1`

---

## Authentication

Tất cả API đều yêu cầu Bearer token trong header:

```http
Authorization: Bearer <access_token>
```

**Roles**:

- `admin`: Quản trị viên - full access
- `system_admin`: Quản trị viên hệ thống - full access
- User thường: Chỉ cập nhật thông tin của chính mình

---

## Response Format

### Success Response

```json
{
  "success": true,
  "trace_id": null,
  "timestamp": "2024-01-15T08:30:00.000Z",
  "errors": null,
  "data": { ... }
}
```

### Error Response

```json
{
	"error": {
		"code": "VALIDATION_ERROR",
		"message": "Validation failed",
		"details": [
			{
				"field": "old_password",
				"code": "INVALID_FORMAT",
				"messages": {
					"vi": "Mật khẩu cũ không đúng",
					"en": "Invalid old password"
				}
			}
		]
	}
}
```

---

## Profile API

### Get User Profile

Lấy thông tin profile của user đang đăng nhập, bao gồm thông tin resident (nếu có) và avatar URL.

```http
GET /api/v1/auth/profile
```

**Middleware**: `verify`, `attachPermissions`

**Security**: BearerAuth

**Success Response**:

```json
{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "email": "user@example.com",
    "username": "johndoe",
    "first_name": "John",
    "last_name": "Doe",
    "status": "active",
    "avatar_id": "550e8400-e29b-41d4-a716-446655440001",
    "avatar_url": "https://backend.com/api/v1/files/550e8400-e29b-41d4-a716-446655440001/content?mode=view",
    "roles": ["user"],
    "permissions": ["read:profile"],
    "last_login_at": "2024-01-15T08:00:00.000Z",
    "resident_id": "550e8400-e29b-41d4-a716-446655440002",
    "resident": {
      "id": "550e8400-e29b-41d4-a716-446655440002",
      "full_name": "Nguyễn Văn A",
      "email": "nguyenvana@example.com",
      "phone": "0912345678",
      "id_card": "001234567890",
      "date_of_birth": "1990-01-15",
      "emergency_contact_name": "Nguyễn Văn B",
      "emergency_contact_phone": "0912345679",
      "emergency_contact_relationship": "Brother",
      "nationality": "Vietnamese",
      "job": "Engineer",
      "status": "active"
    }
  }
}
```

**Field Descriptions**:

| Field | Type | Nullable | Description |
|-------|------|----------|-------------|
| id | string (UUID) | No | ID của user |
| email | string | No | Email của user |
| username | string | Yes | Username |
| first_name | string | Yes | Tên |
| last_name | string | Yes | Họ |
| status | string | No | Trạng thái (active, inactive, etc.) |
| avatar_id | string (UUID) | Yes | ID của file avatar |
| avatar_url | string | Yes | URL để hiển thị avatar (tự động sinh từ avatar_id) |
| roles | array of string | No | Danh sách roles |
| permissions | array of string | No | Danh sách permissions |
| last_login_at | string (datetime) | Yes | Thời gian đăng nhập gần nhất |
| resident_id | string (UUID) | Yes | ID của resident liên kết |
| resident | object | Yes | Thông tin chi tiết resident (nếu có) |

**Resident Fields**:

| Field | Type | Nullable | Description |
|-------|------|----------|-------------|
| id | string (UUID) | Yes | ID của resident |
| full_name | string | Yes | Họ tên đầy đủ |
| email | string | Yes | Email |
| phone | string | Yes | Số điện thoại |
| id_card | string | Yes | Số CCCD/CMND |
| date_of_birth | string | Yes | Ngày sinh |
| emergency_contact_name | string | Yes | Tên người liên hệ khẩn cấp |
| emergency_contact_phone | string | Yes | SĐT người liên hệ khẩn cấp |
| emergency_contact_relationship | string | Yes | Mối quan hệ người liên hệ |
| nationality | string | Yes | Quốc tịch |
| job | string | Yes | Nghề nghiệp |
| status | string | Yes | Trạng thái |

**Notes**:
- `avatar_url` được tự động sinh từ `avatar_id` nếu có:
  - Nếu storage là `local`: trả về fallback URL `/api/v1/files/{id}/content?mode=view`
  - Nếu storage là cloud (MinIO/S3): trả về signed URL có thời hạn 1 giờ
- Nếu user không có avatar hoặc file không tồn tại, `avatar_url` sẽ là `null`
- `resident` sẽ là `null` nếu user không được liên kết với resident nào

---

## Change Password API

### Change User Password

Thay đổi mật khẩu của user đang đăng nhập. Cần xác thực mật khẩu cũ.

```http
PUT /api/v1/auth/change-password
```

**Middleware**: `verify`

**Security**: BearerAuth

**Request Body**:

```json
{
  "old_password": "OldSecurePass123",
  "new_password": "NewSecurePass456"
}
```

**Field Validation**:

| Field | Type | Required | Validation |
|-------|------|----------|------------|
| old_password | string | Yes | min 1, max 128 ký tự |
| new_password | string | Yes | min 8, max 128 ký tự |

**Success Response**:

```json
{
  "success": true,
  "data": {
    "success": true,
    "message": "Password changed successfully"
  }
}
```

**Error Responses**:

**400 - Invalid old password**:
```json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed",
    "details": [
      {
        "field": "old_password",
        "code": "INVALID_CREDENTIALS",
        "messages": {
          "vi": "Mật khẩu cũ không đúng",
          "en": "Invalid old password"
        }
      }
    ]
  }
}
```

**400 - Password change too soon**:
```json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed",
    "details": [
      {
        "field": "password",
        "code": "VALIDATION_ERROR",
        "messages": {
          "vi": "Vui lòng đợi trước khi thay đổi mật khẩu lại",
          "en": "Password cannot be changed yet. Please wait before changing password again."
        }
      }
    ]
  }
}
```

**400 - Weak password**:
```json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed",
    "details": [
      {
        "field": "new_password",
        "code": "WEAK_PASSWORD",
        "messages": {
          "vi": "Mật khẩu không đủ mạnh. Vui lòng thêm số, ký tự đặc biệt hoặc tránh dùng thông tin cá nhân.",
          "en": "Password is too weak. Please add numbers, special characters, or avoid personal information."
        }
      }
    ]
  }
}
```

**Notes**:
- Mật khẩu mới phải tuân thủ NIST password strength validation
- Không thể đổi password quá thường xuyên (có giới hạn thời gian giữa các lần đổi)
- Password history được lưu để tránh dùng lại password cũ
- Sau khi đổi password thành công, tất cả các session khác có thể bị revoke (tùy config)

---

## Avatar Upload Process

Quy trình upload avatar bao gồm 3 bước:

### Bước 1: Upload File

Upload file ảnh avatar lên server.

```http
POST /api/v1/files
```

**Middleware**: `verify`, `uploadRateLimit`, `upload`, `validateZod`

**Security**: BearerAuth

**Request**: `multipart/form-data`

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| file | File | Yes | File ảnh (JPG, PNG, etc.) |
| title | string | No | Tiêu đề file (max 150 ký tự) |
| description | string | No | Mô tả (max 500 ký tự) |
| note | string | No | Ghi chú (max 255 ký tự) |


**Success Response**:

```json
{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440001",
    "original_name": "avatar.jpg",
    "provider": "local",
    "bucket_name": "",
    "object_key": "uploads/avatars/550e8400-e29b-41d4-a716-446655440001.jpg",
    "mime_type": "image/jpeg",
    "size_bytes": 102400,
    "title": "My Avatar",
    "description": null,
    "note": null,
    "visibility": "private",
    "category": "image",
    "checksum_sha_256": "abc123...",
    "owner_user_id": "550e8400-e29b-41d4-a716-446655440000",
    "created_at": "2024-01-15T08:30:00.000Z",
    "updated_at": "2024-01-15T08:30:00.000Z"
  }
}
```

**Lưu ý**: Lấy `id` từ response để dùng ở bước 2.

---

### Bước 2: Update User Avatar ID

Cập nhật `avatar_id` của user với ID của file vừa upload.

```http
PUT /api/v1/users/{id}
```

**Security**: BearerAuth

**Path Parameters**:

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| id | string (UUID) | Yes | ID của user (chỉ cập nhật chính mình hoặc admin) |

**Request Body**:

```json
{
  "avatar_id": "550e8400-e29b-41d4-a716-446655440001"
}
```

**Field Validation**:

| Field | Type | Required | Validation |
|-------|------|----------|------------|
| avatar_id | string (UUID) | No | UUID của file avatar (nullable để xóa avatar) |

**Success Response**:

```json
{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "email": "user@example.com",
    "username": "johndoe",
    "first_name": "John",
    "last_name": "Doe",
    "phone": "+84901234568",
    "status": "active",
    "avatar_id": "550e8400-e29b-41d4-a716-446655440001",
    "created_at": "2024-01-15T08:00:00.000Z",
    "updated_at": "2024-01-15T08:35:00.000Z"
  }
}
```

---

### Bước 3: Get Profile with Avatar URL

Gọi API profile để lấy `avatar_url` được sinh tự động.

```http
GET /api/v1/auth/profile
```

**Success Response**:

```json
{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "email": "user@example.com",
    "username": "johndoe",
    "first_name": "John",
    "last_name": "Doe",
    "status": "active",
    "avatar_id": "550e8400-e29b-41d4-a716-446655440001",
    "avatar_url": "https://backend.com/api/v1/files/550e8400-e29b-41d4-a716-446655440001/content?mode=view",
    "roles": ["user"],
    "permissions": ["read:profile"],
    "last_login_at": "2024-01-15T08:00:00.000Z",
    "resident_id": "550e8400-e29b-41d4-a716-446655440002",
    "resident": { ... }
  }
}
```

**Notes**:
- `avatar_url` được sinh tự động từ `avatar_id`
- Không cần gọi API file link riêng
- URL đã sẵn sàng để hiển thị trong `<img src="...">`

---

## Complete Workflows

### Workflow 1: Upload Avatar

1. User chọn file ảnh từ device
2. FE gọi `POST /api/v1/files` với file trong `multipart/form-data`
3. Backend lưu file và trả về file ID
4. FE gọi `PUT /api/v1/users/{id}` với `avatar_id` vừa nhận
5. FE gọi `GET /api/v1/auth/profile` để lấy `avatar_url`
6. FE hiển thị avatar với URL nhận được


---

### Workflow 2: Change Password

1. User nhập old_password và new_password
2. FE validate client-side (min 8 ký tự)
3. FE gọi `PUT /api/v1/auth/change-password`
4. Backend validate old_password và password strength
5. Nếu thành công, logout user và yêu cầu login lại
6. Nếu thất bại, hiển thị error message


---

### Workflow 3: Get User Profile

1. Khi user login thành công, FE gọi `GET /api/v1/auth/profile`
2. Backend trả về thông tin user, roles, permissions, resident info, avatar_url
3. FE lưu thông tin vào state/context
4. FE hiển thị thông tin profile trong UI


---

### Workflow 4: Remove Avatar

Để xóa avatar, FE cần:

1. Gọi `PUT /api/v1/users/{id}` với `avatar_id: null`
2. Gọi `GET /api/v1/auth/profile` để xác nhận avatar_url là null
3. Hiển thị avatar mặc định


---

## Error Handling

### Common Error Codes

| Code | Description | Solution |
|------|-------------|----------|
| VALIDATION_ERROR | Dữ liệu không hợp lệ | Kiểm tra validation rules |
| INVALID_CREDENTIALS | Mật khẩu cũ không đúng | Yêu cầu user nhập lại |
| WEAK_PASSWORD | Mật khẩu không đủ mạnh | Yêu cầu user dùng password mạnh hơn |
| UNAUTHORIZED | Không có quyền truy cập | Kiểm tra token và permissions |
| FORBIDDEN | Không có quyền thực hiện hành động | Chỉ admin hoặc user chính mình có thể cập nhật |
| NOT_FOUND | User hoặc file không tồn tại | Kiểm tra ID |
| FILE_TOO_LARGE | File quá lớn | Kiểm tra kích thước file (tùy config) |
| INVALID_FILE_TYPE | Loại file không hợp lệ | Chỉ upload ảnh (JPG, PNG, etc.) |

### Validation Error Example

```json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed",
    "details": [
      {
        "field": "new_password",
        "code": "TOO_SHORT",
        "messages": {
          "vi": "Mật khẩu phải có ít nhất 8 ký tự",
          "en": "Password must be at least 8 characters"
        }
      }
    ]
  }
}
```

## Changelog

### 2026-05-28
- Thêm resident information vào profile response (full_name, email, phone, id_card, date_of_birth, emergency_contact...)
- Thêm avatar_url tự động sinh từ avatar_id
- Thêm API change-password với validation password strength
- Thêm swagger documentation cho change-password endpoint
