--- description: اتصال ورود ادمین به سامانه هویت متمرکز (IAM) — مرزها و قواعدی که نباید شکسته شوند globs: alwaysApply: true --- # اتصال به سامانه هویت متمرکز (Central IAM) مستندها: - `docs/iam-connection.md` — **نحوه ارتباط**: سه کانال داده و رفتار در خطا - `docs/iam-sso.md` — پیکربندی، ابطال نشست، محدودیت‌ها - مرجع تصویری اندپوینت‌های IAM: `/docs` (Scalar) ## اصل غیرقابل‌مذاکره این قابلیت **افزایشی** است. با `IAM_ENABLED=false` (پیش‌فرض) سامانه باید دقیقاً مثل قبل رفتار کند: یک `bcrypt.compare` محلی و هیچ درخواست شبکه‌ای. - **هیچ تغییری در رابط کاربری نیست.** نه دکمه SSO، نه ریدایرکت، نه صفحه جدید. ادمین مثل همیشه موبایل و رمز را در همین پنل وارد می‌کند. - پاسخ `POST /api/admin/auth/login` در هر دو حالت یکسان است (`api_token`, `roles`, …) پس پنل ادمین تفاوتی نمی‌بیند. ## نقشه فایل‌ها | مسیر | نقش | | --- | --- | | `apps/api/src/modules/iam/iam.service.ts` | ارتباط با IAM و نگاشت خطاها | | `apps/api/src/modules/iam/iam.module.ts` | ماژول Global (بی‌اثر تا زمان روشن شدن) | | `apps/api/src/modules/auth/auth.service.ts` | متد `verifyPassword` — تنها نقطه اتصال ورود | | `apps/api/src/modules/sessions/admin-session.service.ts` | نشست‌های قابل ابطال (`jti`) | | `apps/api/src/modules/iam/iam-logout.service.ts` | تأیید توکن خروج سراسری (JWKS، RS256) | | `apps/api/src/modules/iam/iam.controller.ts` | `POST /api/iam/backchannel-logout` | ## قواعد سخت 1. **هر نتیجه مبهم باید به `bcrypt` محلی برگردد.** سرویس در دسترس نیست، کاربر آنجا نیست، یا ۴۲۹ گرفتیم ⇒ رمز محلی بررسی شود. فقط `exclusive` اجازه رد کردن دارد. خرابی IAM هرگز نباید ادمین‌ها را بیرون بیندازد. 2. **حساب قفل‌شده، «نتیجه قطعی» است نه خطا.** `account_locked` نباید به `bcrypt` محلی برگردد وگرنه قفلِ IAM بی‌اثر می‌شود. 3. **`client_ip` همیشه فرستاده می‌شود.** بدون آن، محدودیت نرخِ IAM روی IP همین سرور اعمال می‌شود و بعد از چند ورود کل پنل ادمین ۴۲۹ می‌گیرد. اگر API پشت پراکسی رفت، `trust proxy` را تنظیم کن وگرنه `@Ip()` همیشه IP پراکسی است. 4. **هیچ جدول موجودی تغییر نمی‌کند.** نگاشت کاربر با همان `mobile` انجام می‌شود و `iamSub`/`iamSessionId` روی جدول **جدید** `AdminSession` می‌نشینند، نه روی `AdminUser`. اگر روزی چیزی از IAM لازم شد، همین الگو را نگه دار. 5. **ابطال نشست دو شرط دارد و هر دو لازم است:** توکن باید `jti` داشته باشد و ردیف `AdminSession` باید موجود و باطل‌نشده باشد. با خاموش بودن ردیابی (`AUTH_SESSION_TRACKING=false` یا IAM خاموش) نه `jti` صادر می‌شود، نه کوئری اضافه‌ای در مسیر احراز هویت اجرا می‌شود — یعنی دقیقاً رفتار و هزینه قبلی. توکن بدون `jti` هرگز نباید رد شود، وگرنه روشن کردن این کلید همه را بیرون می‌اندازد. 6. **اندپوینت خروج سراسری بدون احراز هویت است و باید بماند.** اعتبارش از امضای `logout_token` می‌آید: `iss`، `aud`، `events`، نبودن `nonce` و `exp` همه بررسی می‌شوند. هیچ‌یک از این بررسی‌ها را برای «راحتی تست» حذف نکن. پاسخ همیشه ۲۰۰ است تا IdP بی‌نهایت تلاش مجدد نکند؛ دلیل رد شدن فقط لاگ می‌شود. 7. **فرم ورود این پنل مرحله «ورود کد» ندارد**، پس حساب دو مرحله‌ای در IAM از این مسیر قابل تکمیل نیست و با پیام روشن رد می‌شود. تا وقتی آن مرحله ساخته نشده، تلاش برای تکمیل 2FA در بک‌اند بی‌معناست. 8. **ورود اپ کاربر رمز ندارد**، پس از `verifyCredentials` استفاده نمی‌شود؛ بعد از بررسی موفق کد، `IamService.attestOtpLogin` فراخوانی می‌شود. قاعده خطا: «کاربر آنجا نیست» و «سرویس در دسترس نیست» ⇒ ورود ادامه می‌یابد؛ «قفل/غیرفعال/دو مرحله‌ای» ⇒ ورود رد می‌شود. کد اضطراری تحویل (`OTP_MASTER_CODE`) هرگز به IAM اعلام نمی‌شود. 9. پیکربندی ناقص = خاموش. `IamService.enabled` وجود `issuer`, `client_id` و `client_secret` را هم شرط می‌کند (با `envOr` که مقدار خالی را unset می‌شمارد). ## قبل از تحویل ```bash cd apps/api && npx jest src/modules/iam src/modules/auth src/modules/sessions # و در استقرار: prisma migrate deploy (جدول AdminSession) ```