12 KiB
اتصال gold-bank به سامانه هویت متمرکز (Central IAM)
نحوه ارتباط (مسیر داده، اندپوینتها، رفتار در خطا):
iam-connection.mdمرجع تصویری API سامانه هویت:<آدرس IAM>/docs
این سند توضیح میدهد ورود ادمین چطور میتواند بهجای رمز محلی، از سامانه هویت سازمانی اعتبارسنجی شود — بدون هیچ تغییری در رابط کاربری.
۱. خلاصه تصمیم
| موضوع | تصمیم |
|---|---|
| رابط کاربری | هیچ تغییری نمیکند؛ نه دکمه SSO، نه ریدایرکت، نه صفحه جدید |
| جای اتصال | فقط همان یک نقطهای که رمز بررسی میشد (AuthService.login) |
| پیشفرض | خاموش (IAM_ENABLED=false) — رفتار سامانه دقیقاً مثل امروز |
| در دسترس نبودن IAM | بازگشت خودکار به بررسی رمز محلی ⇒ ورود ادمینها قطع نمیشود |
| تغییر دیتابیس | دو جدول جدید (AdminSession، AppSession) برای ابطال نشست؛ هیچ جدول موجودی تغییر نکرده |
| ورود مشتری (OTP پیامکی) | کد همچنان اینجا تولید و بررسی میشود؛ نتیجه به IAM اعلام میگردد تا نشست و خروج سراسری مرکزی شود |
جریان ورود ادمین پس از فعال شدن:
پنل ادمین ──POST /api/admin/auth/login──▶ API (NestJS)
(همان فرم و همان بدنه) │
├─ IAM روشن؟ ──خیر──▶ bcrypt محلی (رفتار فعلی)
│
└─بله─▶ POST {IAM}/api/v1/auth/login
│
┌─────────────────┴─────────────────┐
تأیید شد رد/خطا/در دسترس نبود
│ │
صدور همان JWT محلی fallback ⇒ bcrypt محلی
exclusive ⇒ رد ورود
پاسخ اندپوینت در هر دو حالت دقیقاً یکسان است (api_token, roles, …)،
بنابراین پنل ادمین هیچ تفاوتی نمیبیند.
۲. فایلها
| مسیر | نقش |
|---|---|
apps/api/src/modules/iam/iam.service.ts |
ارتباط با IAM و نگاشت خطاها |
apps/api/src/modules/iam/iam-logout.service.ts |
تأیید توکن خروج سراسری با JWKS (بدون وابستگی جدید) |
apps/api/src/modules/iam/iam.controller.ts |
دریافت اعلان خروج سراسری |
apps/api/src/modules/iam/iam.module.ts |
ماژول Global (تا زمان روشن شدن، بیاثر) |
apps/api/src/modules/sessions/admin-session.service.ts |
نشستهای قابل ابطال (jti) |
apps/api/src/modules/sessions/admin-session.module.ts |
ماژول Global نشستها |
prisma/migrations/20260925120000_admin_session/ |
جدول AdminSession |
apps/api/src/modules/**/*.spec.ts |
تست هر چهار سرویس بالا |
تغییر روی کد موجود، حداقلی است:
auth.service.ts— یکbcrypt.compareبه متدverifyPasswordمنتقل شد و توکن صادرشده (در صورت روشن بودن ردیابی نشست) ثبت میشودauth.controller.ts— افزودن@Ip()وuser-agentjwt.strategy.ts— یک بررسی «نشست باطل نشده باشد» (در حالت خاموش، بدون کوئری)admin-users.service.ts— غیرفعال کردن حساب یا تغییر رمز، نشستها را میبنددapp.module.ts— ثبتIamModuleوAdminSessionModuleprisma/schema.prisma— فقط مدل جدیدAdminSession(+ رابطه رویAdminUser)
۳. فعالسازی
گام ۱ — ثبت این سامانه بهعنوان کلاینت در IAM
باید کلاینت از نوع confidential و is_trusted: true باشد؛ فقط چنین
کلاینتی اجازه «ورود واگذارشده» دارد.
curl -X POST https://auth.example.com/api/v1/admin/clients \
-H "Authorization: Bearer $IAM_ADMIN_TOKEN" \
-H 'content-type: application/json' \
-d '{
"client_id": "goldbank_api",
"client_name": "سامانه بانک طلا",
"client_type": "confidential",
"redirect_uris": ["https://goldbank.example.com/unused"],
"allowed_scopes": ["openid", "profile", "email"],
"is_trusted": true
}'
redirect_urisدر این حالت استفاده نمیشود (جریان مرورگری نداریم) ولی IAM آن را اجباری میداند؛ یک آدرس معتبر بگذارید.
گام ۲ — تنظیم .env
IAM_ENABLED=true
IAM_ISSUER=https://auth.example.com
IAM_CLIENT_ID=goldbank_api
IAM_CLIENT_SECRET=<راز دریافتی>
IAM_TENANT_SLUG=montzar
IAM_LOGIN_DELEGATION=fallback
همین. سرویس را ریاستارت کنید؛ هیچ مایگریشنی لازم نیست.
گام ۳ — بازگشت به عقب
IAM_ENABLED=false و ریاستارت. تمام.
۴. حالتهای IAM_LOGIN_DELEGATION
| مقدار | رفتار |
|---|---|
off |
IAM اصلاً در ورود دخالت نمیکند |
fallback (پیشفرض) |
اول IAM؛ اگر کاربر آنجا نبود یا سرویس در دسترس نبود، رمز محلی بررسی میشود — مناسب مهاجرت تدریجی |
exclusive |
فقط IAM؛ رمز محلی پذیرفته نمیشود و خرابی IAM یعنی توقف ورود |
پیشنهاد مهاجرت: با fallback شروع کنید، بعد از انتقال همه ادمینها به IAM
و اطمینان از پایداری، به exclusive بروید.
۵. نکات عملیاتی
محدودیت نرخ
IAM روی «IP» و «هویت» محدودیت نرخ دارد. چون درخواستها از سرور API میآیند،
IP کاربر نهایی با فیلد client_ip فرستاده میشود؛ در غیر این صورت پس از چند
ورود، کل پنل ادمین ۴۲۹ میگرفت.
اگر API پشت پراکسی است، مطمئن شوید Nest trust proxy دارد وگرنه @Ip()
همیشه IP پراکسی را میدهد و همان مشکل برمیگردد.
احراز دو مرحلهای
فرم ورود این پنل فقط «موبایل + رمز» است و هیچ مرحلهی ورود کدی ندارد؛ پس جایی برای گرفتن کد عامل دوم وجود ندارد و این مسیر نمیتواند آن را تکمیل کند. ورود چنین حسابی با پیام روشن رد میشود (نه خطای مرموز). سه گزینه:
- برای حسابهایی که از این پنل وارد میشوند 2FA را در IAM فعال نکنید (سادهترین)؛
- مرحلهی «ورود کد» را به همین فرم اضافه کنید؛ آنوقت میتوان مثل سامانه «همکار»
کد را واسطه کرد یا با کانال خودِ سامانه فرستاد
(
/api/v1/auth/mfa/verifyو/api/v1/auth/mfa/delegateدر IAM آمادهاند)؛ - یا جریان مرورگری SSO را برای این پنل به کار بگیرید که خودش صفحه کد را میدهد.
در سامانه «همکار» این محدودیت وجود ندارد، چون ورود کاربر عادی آنجا از قبل مرحلهی «ورود کد» (پیامک) دارد و همان مرحله برای عامل دوم استفاده میشود.
ابطال نشست
توکن ادمین خودبسنده و ۱۲ ساعته است، پس تا پیش از این هیچچیز نمیتوانست نشست را
زودتر تمام کند: نه غیرفعال کردن حساب، نه خروج سراسری در IAM. حالا هر توکن صادرشده
یک jti دارد که در جدول AdminSession ثبت میشود و درخواست بعدیِ نشستِ باطلشده
۴۰۱ میگیرد.
IAM: ابطال نشست / خروج سراسری
│ logout_token (RS256، امضاشده)
▼
POST /api/iam/backchannel-logout ← این آدرس را در IAM بهعنوان
│ backchannel_logout_uri کلاینت ثبت کنید
├─ تأیید امضا با JWKS، بررسی iss/aud/events و نبودن nonce
└─ ابطال نشستهای متناظر (sid، یا همه نشستهای sub)
│
▼
درخواست بعدی با آن توکن ⇒ ۴۰۱
کلید تنظیم: AUTH_SESSION_TRACKING
| مقدار | رفتار |
|---|---|
| خالی (پیشفرض) | همراه IAM_ENABLED روشن/خاموش میشود — تا ابطال مرکزی بیاثر نماند |
true |
روشن، حتی بدون IAM (ابطال محلی: غیرفعال کردن حساب، تغییر رمز) |
false |
خاموش؛ دقیقاً رفتار قبلی (بدون jti، بدون کوئری اضافه) و ابطال مرکزی بیاثر |
نکتههای عملی:
- توکنهایی که پیش از روشن کردن این قابلیت صادر شدهاند
jtiندارند و تا انقضا معتبر میمانند؛ یعنی روشن کردن این کلید کسی را یکباره بیرون نمیاندازد. - ردیف نشستِ گمشده «باطل» تلقی میشود، پس پاک کردن جدول یعنی خروج همه.
- اندپوینت خروج سراسری عمداً بدون احراز هویت است (اعتبارش از امضای توکن میآید) و همیشه ۲۰۰ برمیگرداند تا IdP بینهایت تلاش مجدد نکند؛ دلیل رد شدن در لاگ میآید.
- تنها تغییر شِما یک جدول جدید است؛ هیچ جدول موجودی ستون نگرفته. مایگریشن:
pnpm --filter @gold-bank/api exec prisma migrate deploy.
لاگ امنیتی
هر ورود واگذارشده در IAM با IP واقعی کاربر و client_id این سامانه ثبت
میشود، پس گزارشهای امنیتی متمرکز کامل باقی میمانند.
۶. تست
cd apps/api
pnpm test # ۱۱۵ تست، بدون دیتابیس و بدون شبکه
pnpm test -- iam-logout.service # تأیید توکن خروج سراسری با کلید RSA واقعی
pnpm test -- admin-session # نشستهای قابل ابطال
تستها بدون دیتابیس و بدون شبکه اجرا میشوند و مهمترین چیزی که تضمین میکنند این است: با خاموش بودن قابلیت، هیچ درخواستی به IAM نمیرود و ورود دقیقاً مثل قبل کار میکند؛ و با روشن بودن، خرابی IAM ورود ادمین را قطع نمیکند.