# جریان کاربری — تسک ۰۲: ماژول احراز هویت ## جریان کامل ورود با OTP (جریان واقعی از Drupal) ``` کاربر موبایل را وارد می‌کند │ ▼ POST /api/v1/user/send-code { mobile: "09120671713", captcha_token: "" } │ ├─► اعتبارسنجی فرمت موبایل (/^(\+98|0)?9\d{9}$/) ├─► بررسی rate limit IP (max 50/hour) ├─► بررسی rate limit Mobile (max 30/hour) ├─► تولید UUID + کد OTP ├─► ذخیره در Redis: otp:{uuid} = {code, mobile} (TTL=1200s) └─► ارسال SMS │ ▼ Response: { uuid: "a1b2c3d4-...", message: "..." } │ ▼ کاربر کد را وارد می‌کند │ ▼ POST /api/v1/user/verify-code { uuid: "a1b2c3d4-...", code: "12345" } │ ├─► بررسی وجود uuid در Redis ├─► مقایسه code │ ├─► نادرست: خطا │ └─► درست: حذف از Redis └─► Response: { message: "کد با موفقیت تایید شد.", success: true } ⚠️ verify-code در Drupal JWT صادر نمی‌کند! JWT در مرحله بعد با /oauth/token صادر می‌شود. │ ▼ POST /oauth/token (MobileGrant) grant_type=mobile uuid=a1b2c3d4-... ← همان uuid code=12345 ← همان code client_id=clinic-pro client_secret=... registration=true ← اگر false باشد، فقط کاربر موجود می‌تواند وارد شود │ ├─► OtpService::verify(uuid, code) ├─► OtpService::getMobileByUuid(uuid) ├─► بررسی وجود کاربر با این موبایل │ ├─► وجود دارد: ادامه │ └─► جدید + registration=true: ایجاد user با status=pending └─► صدور JWT │ ▼ Response: { access_token, refresh_token, token_type: "Bearer", expires_in: 3600 } ``` ## جریان تمدید Token ``` POST /oauth/token grant_type=refresh_token client_id=clinic-pro client_secret=... refresh_token=eyJ... │ └─► بررسی refresh_token → صدور access_token جدید ``` ## جریان دریافت اطلاعات کاربر ``` GET /oauth/userinfo Authorization: Bearer {access_token} │ └─► decode JWT → بازگشت: sub, uuid, name, email, phone_number, scope ```