arc/docs/SESSION_MANAGEMENT_SCENARIO.md
2026-04-14 19:34:55 +03:30

14 KiB
Executable file
Raw Permalink Blame History

سناریو مدیریت سشن‌های ورود در تنظیمات حساب کاربری

بررسی وضعیت فعلی

ساختار دیتابیس

  • جدول api_keys با فیلدهای زیر برای session keys:
    • id: شناسه یکتا
    • user_id: شناسه کاربر
    • key_type: نوع کلید ("session" یا "personal")
    • device_id: شناسه دستگاه (اختیاری)
    • user_agent: User Agent مرورگر/اپلیکیشن (اختیاری)
    • ip: آدرس IP آخرین استفاده (اختیاری)
    • last_used_at: تاریخ و زمان آخرین استفاده
    • created_at: تاریخ و زمان ایجاد
    • revoked_at: تاریخ و زمان لغو (NULL = فعال)

وضعیت فعلی

  • ✅ Session keys در زمان ورود ایجاد می‌شوند (login_user و verify_login_otp)
  • ✅ اطلاعات device_id، user_agent و ip در زمان ورود ذخیره می‌شوند
  • ✅ last_used_at در هر درخواست API به‌روزرسانی می‌شود (هر 60 ثانیه)
  • ❌ هیچ endpoint برای لیست کردن session keys وجود ندارد
  • ❌ هیچ endpoint برای حذف session keys وجود ندارد
  • ❌ صفحه تنظیمات حساب کاربری کارتی برای مدیریت sessions ندارد
  • ❌ هیچ utility برای تشخیص نام دستگاه از user_agent وجود ندارد

سناریو پیشنهادی

1. Backend - API Endpoints

1.1. لیست سشن‌های ورود

Endpoint: GET /api/v1/auth/sessions

توضیحات:

  • دریافت لیست تمام session keys فعال کاربر (key_type="session" و revoked_at=NULL)
  • مرتب‌سازی بر اساس last_used_at (جدیدترین اول)
  • نمایش اطلاعات کامل هر session

Response Schema:

{
  "success": true,
  "data": [
    {
      "id": 123,
      "device_name": "Chrome on Windows",
      "device_id": "device-uuid-123",
      "user_agent": "Mozilla/5.0...",
      "ip": "192.168.1.100",
      "location": "تهران، ایران",  // اختیاری - از IP geolocation
      "is_current": true,  // آیا این session فعلی است؟
      "created_at": "2024-01-15T10:30:00Z",
      "last_used_at": "2024-01-20T14:25:00Z",
      "last_used_relative": "2 ساعت پیش"  // فرمت نسبی برای نمایش
    }
  ]
}

ویژگی‌ها:

  • تشخیص نام دستگاه از user_agent (مثلاً "Chrome on Windows", "Safari on iPhone", "Flutter App")
  • تشخیص اینکه آیا session فعلی است (مقایسه key_hash با API key فعلی)
  • فرمت نسبی تاریخ (مثلاً "2 ساعت پیش"، "3 روز پیش")
  • امکان افزودن geolocation از IP (اختیاری - نیاز به سرویس خارجی)

1.2. حذف سشن

Endpoint: DELETE /api/v1/auth/sessions/{session_id}

توضیحات:

  • حذف (revoke) یک session خاص
  • کاربر نمی‌تواند session فعلی خود را حذف کند (برای جلوگیری از logout ناخواسته)
  • اگر session فعلی حذف شود، باید خطا برگرداند

Response Schema:

{
  "success": true,
  "message": "سشن با موفقیت حذف شد",
  "data": {
    "ok": true
  }
}

خطاها:

  • 404: Session یافت نشد
  • 400: نمی‌توانید session فعلی را حذف کنید
  • 403: Session متعلق به کاربر دیگری است

1.3. حذف همه سشن‌های دیگر

Endpoint: DELETE /api/v1/auth/sessions/others

توضیحات:

  • حذف تمام session های کاربر به جز session فعلی
  • مفید برای logout از همه دستگاه‌ها

Response Schema:

{
  "success": true,
  "message": "تمام سشن‌های دیگر حذف شدند",
  "data": {
    "deleted_count": 5
  }
}

2. Backend - Service Layer

2.1. Session Service

فایل: hesabixAPI/app/services/session_service.py

توابع:

def list_user_sessions(db: Session, user_id: int, current_api_key_hash: str) -> list[dict]:
    """
    لیست تمام session keys کاربر
    
    Args:
        db: Session دیتابیس
        user_id: شناسه کاربر
        current_api_key_hash: hash کلید API فعلی برای تشخیص session فعلی
    
    Returns:
        لیست session ها با اطلاعات کامل
    """
    pass

def revoke_session(db: Session, user_id: int, session_id: int, current_api_key_hash: str) -> None:
    """
    حذف یک session
    
    Args:
        db: Session دیتابیس
        user_id: شناسه کاربر
        session_id: شناسه session
        current_api_key_hash: hash کلید API فعلی
    
    Raises:
        ApiError: اگر session فعلی باشد یا یافت نشود
    """
    pass

def revoke_other_sessions(db: Session, user_id: int, current_api_key_hash: str) -> int:
    """
    حذف تمام session های دیگر (به جز فعلی)
    
    Returns:
        تعداد session های حذف شده
    """
    pass

2.2. Device Detection Utility

فایل: hesabixAPI/app/utils/device_detection.py

توابع:

def parse_user_agent(user_agent: str | None) -> dict:
    """
    تجزیه user agent و استخراج اطلاعات دستگاه
    
    Returns:
        {
            "device_name": "Chrome on Windows",
            "browser": "Chrome",
            "browser_version": "120.0",
            "os": "Windows",
            "os_version": "10",
            "device_type": "desktop"  # desktop, mobile, tablet
        }
    """
    pass

def format_device_name(user_agent: str | None, device_id: str | None) -> str:
    """
    تولید نام خوانا برای دستگاه
    
    مثال:
        - "Chrome on Windows"
        - "Safari on iPhone 13"
        - "Flutter App (Android)"
        - "دستگاه نامشخص" (اگر اطلاعات کافی نباشد)
    """
    pass

نکته: می‌توان از کتابخانه‌های Python مثل user-agents استفاده کرد.

2.3. Repository Extension

فایل: hesabixAPI/adapters/db/repositories/api_key_repo.py

متدهای جدید:

def get_user_sessions(self, user_id: int) -> list[ApiKey]:
    """دریافت تمام session keys فعال کاربر"""
    pass

def revoke_session(self, session_id: int, user_id: int) -> bool:
    """حذف یک session"""
    pass

def revoke_other_sessions(self, user_id: int, exclude_key_hash: str) -> int:
    """حذف تمام session های دیگر"""
    pass

3. Frontend - UI Components

3.1. صفحه مدیریت سشن‌ها

فایل: hesabixUI/hesabix_ui/lib/pages/profile/sessions_page.dart

ویژگی‌ها:

  • لیست تمام session های فعال
  • نمایش اطلاعات هر session:
    • نام دستگاه (با آیکون)
    • آخرین استفاده (فرمت نسبی)
    • آخرین IP
    • تاریخ ایجاد
    • نشانگر "این دستگاه" برای session فعلی
  • دکمه حذف برای هر session (غیرفعال برای session فعلی)
  • دکمه "خروج از همه دستگاه‌ها" (حذف همه به جز فعلی)
  • Pull-to-refresh
  • Empty state (اگر session ای وجود نداشته باشد)

Layout:

┌─────────────────────────────────────┐
│  مدیریت سشن‌های ورود                │
├─────────────────────────────────────┤
│  🔒 Chrome on Windows               │
│  این دستگاه                         │
│  آخرین استفاده: 2 ساعت پیش         │
│  IP: 192.168.1.100                  │
│  ایجاد شده: 15 دی 1402              │
│                          [حذف ❌]   │
├─────────────────────────────────────┤
│  📱 Safari on iPhone                │
│  آخرین استفاده: 3 روز پیش           │
│  IP: 185.123.45.67                  │
│  ایجاد شده: 10 دی 1402              │
│                          [حذف ❌]   │
├─────────────────────────────────────┤
│  💻 Flutter App (Android)           │
│  آخرین استفاده: 1 هفته پیش         │
│  IP: 192.168.1.50                   │
│  ایجاد شده: 5 دی 1402               │
│                          [حذف ❌]   │
├─────────────────────────────────────┤
│  [خروج از همه دستگاه‌ها]           │
└─────────────────────────────────────┘

3.2. کارت در صفحه تنظیمات

فایل: hesabixUI/hesabix_ui/lib/pages/profile/account_settings_page.dart

تغییرات:

  • افزودن کارت جدید برای "سشن‌های ورود" یا "دستگاه‌های متصل"
  • آیکون: Icons.devices یا Icons.security
  • رنگ: Colors.indigo یا Colors.cyan
  • مسیر: /user/profile/sessions

کد:

_SettingsCard(
  title: 'سشن‌های ورود',
  description: 'مشاهده و مدیریت دستگاه‌های متصل به حساب کاربری',
  icon: Icons.devices,
  color: Colors.indigo,
  onTap: () => context.go('/user/profile/sessions'),
),

3.3. Service Layer

فایل: hesabixUI/hesabix_ui/lib/services/session_service.dart

کلاس:

class SessionService {
  final ApiClient _api;
  
  Future<List<SessionInfo>> listSessions();
  Future<void> revokeSession(int sessionId);
  Future<int> revokeOtherSessions();
}

class SessionInfo {
  final int id;
  final String deviceName;
  final String? deviceId;
  final String? userAgent;
  final String? ip;
  final bool isCurrent;
  final DateTime createdAt;
  final DateTime? lastUsedAt;
  final String lastUsedRelative;  // "2 ساعت پیش"
}

4. Route Configuration

4.1. Backend Route

فایل: hesabixAPI/adapters/api/v1/auth.py

افزودن routes:

@router.get("/sessions", ...)
def list_sessions(...):
    pass

@router.delete("/sessions/{session_id}", ...)
def revoke_session(...):
    pass

@router.delete("/sessions/others", ...)
def revoke_other_sessions(...):
    pass

4.2. Frontend Route

فایل: hesabixUI/hesabix_ui/lib/main.dart

افزودن route:

GoRoute(
  path: '/user/profile/sessions',
  name: 'profile_sessions',
  builder: (context, state) => SessionsPage(
    authStore: _authStore!,
  ),
),

5. امنیت و محدودیت‌ها

5.1. محدودیت‌های امنیتی

  • ✅ کاربر فقط می‌تواند session های خود را ببیند
  • ✅ کاربر نمی‌تواند session فعلی را حذف کند
  • ✅ حذف session باید با تأیید انجام شود (confirmation dialog)
  • ✅ حذف همه session ها باید با تأیید قوی انجام شود

5.2. Rate Limiting

  • لیست sessions: بدون محدودیت (فقط احراز هویت)
  • حذف session: حداکثر 10 بار در دقیقه

5.3. Logging

  • تمام عملیات حذف session باید در activity log ثبت شود
  • لاگ شامل: user_id, session_id, ip, user_agent

6. بهبودهای آینده (اختیاری)

6.1. Geolocation

  • استفاده از سرویس IP geolocation برای نمایش موقعیت جغرافیایی
  • نمایش شهر و کشور برای هر IP

6.2. اعلان‌ها

  • ارسال اعلان (email/SMS) هنگام ورود از دستگاه جدید
  • ارسال اعلان هنگام حذف session از دستگاه دیگر

6.3. محدودیت تعداد Sessions

  • محدود کردن تعداد session های همزمان (مثلاً 5 session)
  • حذف خودکار قدیمی‌ترین session هنگام ایجاد session جدید

6.4. نام‌گذاری Sessions

  • امکان نام‌گذاری دستی session ها توسط کاربر
  • ذخیره نام در فیلد name جدول api_keys

7. مراحل پیاده‌سازی

مرحله 1: Backend Core

  1. ✅ ایجاد device_detection.py utility
  2. ✅ افزودن متدهای repository
  3. ✅ ایجاد session_service.py
  4. ✅ افزودن API endpoints

مرحله 2: Backend Testing

  1. ✅ تست unit برای device detection
  2. ✅ تست integration برای API endpoints
  3. ✅ تست امنیت (دسترسی غیرمجاز)

مرحله 3: Frontend Core

  1. ✅ ایجاد session_service.dart
  2. ✅ ایجاد sessions_page.dart
  3. ✅ افزودن route
  4. ✅ افزودن کارت در تنظیمات

مرحله 4: Frontend Testing

  1. ✅ تست UI/UX
  2. ✅ تست responsive design
  3. ✅ تست error handling

مرحله 5: Integration & Polish

  1. ✅ تست end-to-end
  2. ✅ بهبود UI/UX
  3. ✅ افزودن loading states
  4. ✅ افزودن error messages

خلاصه

این سناریو شامل:

  • ✅ لیست کامل session های ورود
  • ✅ نمایش اطلاعات دستگاه (نام، IP، آخرین استفاده)
  • ✅ قابلیت حذف session های فردی
  • ✅ قابلیت حذف همه session های دیگر
  • ✅ تشخیص هوشمند نام دستگاه از user_agent
  • ✅ UI/UX مناسب در صفحه تنظیمات
  • ✅ امنیت و محدودیت‌های مناسب

نکته مهم: این سناریو فقط برای بررسی و برنامه‌ریزی است. هیچ تغییری در کد ایجاد نشده است.