احراز هویت
ورود اصلی این سرویس با شمارهٔ موبایل است:
POST /api/auth/send-otpبا شمارهٔ کاربر فراخوانی میشود. این مرحله فقط شماره را ثبت میکند و اگر کاربر تازه باشد، ردیف کاربر را میسازد؛ هیچ متادیتای حسابی (نام، ایمیل، دستگاه و…) در این مرحله ذخیره نمیشود.POST /api/auth/verify-otpبا شماره و کد ۶ رقمی فراخوانی میشود. اولین تأییدِ موفق، تقویم اصلی حساب را میسازد. متادیتای حساب هم از همینجا، بعد از اثبات مالکیت شماره، قابل اعمال است.
مسیر POST /api/auth/login با email/password هم وجود دارد؛ فیلدهایش را در مرجع API ببینید.
دو نکتهٔ مرزی:
deviceIdنامعتبر، پیش از هر تغییری در پروفایل، FCM یا وضعیت تأیید، درخواست را ناموفق میکند.- کد توسعه (OTP آزمایشی): کد فقط وقتی
APP_ENVدقیقاًdevelopmentیاtestباشد در پاسخ برمیگردد؛ اگرAPP_ENVتنظیم نشده یا هر مقدار دیگری باشد، پنهان میماند. کد در console لاگ نمیشود و با RNG رمزنگاریشده ساخته میشود. در محیط واقعی debug را فعال نکنید وNODE_ENVرا جایگزالAPP_ENVنگیرید.
نشست دستگاه
سرور دو حالت نشست دارد و رفتارشان فرق میکند؛ این دو را با هم اشتباه نگیرید.
حالت ۱ — نشست دستگاهمحور (مسیر فعلی و توصیهشده): در verify-otp یا login یک شیء device بفرستید که uid (هویت پایدارِ آن نصب، نه شناسهٔ کاربر یا سختافزار) و platform (ANDROID/WEB/IOS) دارد؛ نام دستگاه اختیاری است. با این کار refresh token به آن دستگاه وصل میشود و از فهرست دستگاهها قابل ابطال است.
حالت ۲ — نشست قدیمی روی User: اگر device نفرستید، refresh token روی خود ردیف کاربر ذخیره میشود و هر ورود قدیمیِ دیگری میتواند آن را جایگزین کند.
در POST /api/auth/refresh:
refreshTokenهمیشه لازم است.deviceUidاختیاری است؛ اگر نفرستید، از خود توکنِ امضاشده و تأییدشده استخراج میشود.- مقدار ارسالی
deviceUidباید با اتصال واقعی توکن یکی باشد؛گرنه چرخش ناموفق است.
| مسیر | رفتار |
|---|---|
POST /api/auth/refresh | چرخش جفت توکن؛ هر دو مقدار تازه را ذخیره کنید |
POST /api/auth/logout | ابطال refresh دستگاهِ مشخص (یا در نبود آن، refresh قدیمی حساب) |
GET /api/auth/me | اطلاعات حساب با access token معتبر |
مشخصات توک نها:
- access token در سرصفحهٔ
Authorization: Bearerارسال میشود؛ عمر پیشفرض ۱۵ دقیقه است. - refresh token عمر پیشفرض ۹۰ روز دارد.
- هر دو عمر با
JWT_ACCESS_EXPIRES_INوJWT_REFRESH_EXPIRES_INقابل تنظیماند. - چرخشهای refresh همزمان را در کلاینت یکی کنید (یک پرواز در آن واحد): استفادهٔ دوباره از refresh توکنی که همین خانواده قبلاً چرخاندهاند، بهعنوان reuse تشخیص داده میشود و کل نشست دستگاه باطل میشود. ابطال دستگاه مرجع کامل است.
بازیابی بدون حذف داده
قاعدهٔ کلی: شکست احراز هویت یا شبکه، مجوز پاککردن دادهٔ محلی کاربر نیست.
- 401 روی درخواست عادی: یک refresh مشترک بزنید (تا دو درخواست همزمان نسازید) و همان درخواست را تکرار کنید.
- 401/403 روی خود refresh: نشست از بین رفته؛ کاربر باید دوباره وارد شود — بدون پاککردن خودکار دادهٔ محلی و outbox.
- شبکه، timeout، 429، 5xx یا پاسخ نامعتبر (خواندهنشدنی/ناقص): همه شکست گذرایند؛ توکن و outbox را نگه دارید و بعداً تلاش کنید.
- خروج وقتی تغییر ارسالنشده هست: تصمیم باید صریح باشد — ارسال کامل، خروجی گرفتن، یا دورریز با تأیید کاربر. پاسخ دیررسِ حساب قبلی نباید وارد حساب بعدی شود.
نکتهٔ امنیتی وب: توکنها در localStorage نگه داشته میشوند، نه storage رمزنگاریشده. جزئیات پیادهسازی: وب و اندروید.
مرجع سرور: apps/api/src/routes/auth.ts و apps/api/src/services/deviceService.ts؛ کلاینت وب: apps/calendar-web/src/lib/apiClient.ts.