Feat add iam auth
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
# نحوه ارتباط gold-bank با سامانه هویت متمرکز (IAM)
|
||||
|
||||
این سند **مسیر داده** را توضیح میدهد: چه کسی، چه زمانی، چه چیزی را به کجا
|
||||
میفرستد و اگر آن طرف جواب نداد چه اتفاقی میافتد.
|
||||
|
||||
برای پیکربندی و فعالسازی: [`iam-sso.md`](iam-sso.md) ·
|
||||
مرجع تصویری اندپوینتهای IAM: `http://<آدرس IAM>/docs`
|
||||
|
||||
---
|
||||
|
||||
## ۱. خلاصه در یک تصویر
|
||||
|
||||
```
|
||||
پنل ادمین (Vue) apps/api (NestJS) Central IAM
|
||||
─────────────── ───────────────── ───────────
|
||||
فرم ورودِ همیشگی
|
||||
(موبایل + رمز)
|
||||
│ POST /api/admin/auth/login
|
||||
├──────────────────────────────▶
|
||||
│ AuthService.verifyPassword
|
||||
│ │ POST /api/v1/auth/login
|
||||
│ │ + client_id/secret + client_ip
|
||||
│ ├────────────────────────────────▶
|
||||
│ │◀────────────────────────────────┤
|
||||
│ │ SUCCESS (sub، sid) │ 401 │ خطا
|
||||
│ │
|
||||
│ AdminSession: ثبت jti + sid + sub
|
||||
│◀─────────────────────────────┤ همان پاسخ همیشگی (api_token, roles)
|
||||
```
|
||||
|
||||
سه نکته که کل طراحی را توضیح میدهد:
|
||||
|
||||
1. **مرورگر ادمین هرگز به IAM نمیرود.** نه ریدایرکت، نه دکمه SSO، نه صفحه جدید.
|
||||
2. **قالب پاسخ ورود تغییر نکرده است** (`api_token`, `roles`, …)، پس پنل تفاوتی نمیبیند.
|
||||
3. **نگاشت کاربر با `mobile` انجام میشود.** هیچ ستونی به `AdminUser` اضافه نشده؛
|
||||
شناسههای IAM روی جدول **جدید** `AdminSession` مینشینند.
|
||||
|
||||
> **اپ کاربر (`bnk.montzar.ir`) هم متصل است**، ولی از مسیر دیگری: ورودش فقط با کد
|
||||
> یکبارمصرف است و رمزی ندارد که به IAM فرستاده شود. کد را همین سامانه تولید،
|
||||
> ارسال و بررسی میکند و سپس نتیجه را به IAM اعلام میکند
|
||||
> (`/api/v1/auth/login/delegated-otp`) تا نشست مرکزی، لاگ امنیتی و خروج سراسری
|
||||
> شامل آن ورود هم بشود. توکن اپ هم مثل توکن پنل `jti` میگیرد و در جدول
|
||||
> `AppSession` ثبت میشود، پس قابل ابطال است.
|
||||
|
||||
---
|
||||
|
||||
## ۲. کانالهای ارتباطی (همه چیزی که رد و بدل میشود)
|
||||
|
||||
| # | جهت | مسیر | چه زمانی | چه چیزی میرود | اگر خطا بدهد |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| ۱ | ما → IAM | `POST /api/v1/auth/login` | هر ورود ادمین | موبایل، رمز، `client_id`، `client_secret`، `client_ip` | طبق `IAM_LOGIN_DELEGATION` (پیشفرض: `bcrypt` محلی) |
|
||||
| ۱.۵ | ما → IAM | `POST /api/v1/auth/login/delegated-otp` | **ورود اپ کاربر** (`/auth/verify-otp`) — این ورود رمز ندارد | موبایل، اعتبارنامه کلاینت، `client_ip`، شماره ماسکشده | کاربر آنجا نیست یا سرویس down ⇒ ورود محلی ادامه؛ قفل/غیرفعال/2FA ⇒ ورود رد میشود |
|
||||
| ۲ | ما → IAM | `GET /.well-known/jwks.json` | تأیید توکن خروج سراسری (کش ۲۴ ساعته، `kid` ناشناس ⇒ یکبار refetch) | — | اعلان خروج پذیرفته نمیشود (۲۰۰ + لاگ) |
|
||||
| ۳ | **IAM → ما** | `POST /api/iam/backchannel-logout` | خروج سراسری یا ابطال نشست توسط ادمینِ IAM | `logout_token` امضاشده (RS256) | همیشه ۲۰۰؛ دلیل رد فقط در لاگ |
|
||||
|
||||
فقط همین سه کانال. جریان مرورگری OAuth در این سامانه استفاده نمیشود، چون قرار
|
||||
است رابط کاربری ورود دستنخورده بماند.
|
||||
|
||||
---
|
||||
|
||||
## ۳. جریان ۱ — ورود ادمین
|
||||
|
||||
```
|
||||
POST /api/admin/auth/login { phone, password }
|
||||
│
|
||||
├─ AdminUser با این موبایل هست و فعال است؟ (نه ⇒ ۴۰۱ مبهم، مثل قبل)
|
||||
│
|
||||
├─ AuthService.verifyPassword
|
||||
│ ├─ IAM خاموش ⇒ یک bcrypt.compare (دقیقاً رفتار قبلی، بدون هیچ درخواست شبکه)
|
||||
│ ├─ IAM تأیید کرد ⇒ { ok: true, identity: { sub, sessionId } }
|
||||
│ ├─ IAM رد کرد (کاربر آنجا نیست/رمز فرق دارد)
|
||||
│ │ ├─ fallback ⇒ bcrypt محلی
|
||||
│ │ └─ exclusive ⇒ ۴۰۱
|
||||
│ ├─ حساب در IAM قفل است ⇒ **رد قطعی** (قفل IAM دور زده نمیشود)
|
||||
│ ├─ IAM در دسترس نیست / ۴۲۹
|
||||
│ │ ├─ fallback ⇒ bcrypt محلی
|
||||
│ │ └─ exclusive ⇒ ۴۰۱ با پیام روشن
|
||||
│ └─ حساب دو مرحلهای است ⇒ پیام روشن (این فرم مرحله کد ندارد)
|
||||
│
|
||||
├─ jti تازه (فقط وقتی ردیابی نشست روشن است)
|
||||
├─ امضای همان JWT همیشگی { sub, mobile, jti? }
|
||||
├─ AdminSession: { jti, adminUserId, iamSessionId, iamSub, ip, userAgent }
|
||||
│
|
||||
└─ پاسخ: همان قالب قبلی
|
||||
```
|
||||
|
||||
**چرا `client_ip` حیاتی است:** محدودیت نرخ IAM روی IP اعمال میشود. بدون IP کاربر
|
||||
نهایی، همه ورودها از IP همین سرور دیده میشوند و بعد از چند تلاش ناموفق، **کل**
|
||||
پنل ادمین ۴۲۹ میگیرد. اگر API پشت پراکسی است، `trust proxy` را تنظیم کنید وگرنه
|
||||
`@Ip()` همیشه IP پراکسی را میدهد و همان مشکل برمیگردد.
|
||||
|
||||
---
|
||||
|
||||
## ۴. جریان ۲ — اعتبارسنجی هر درخواست (نشست قابل ابطال)
|
||||
|
||||
```
|
||||
هر درخواست با Bearer توکن
|
||||
│
|
||||
├─ JwtStrategy: امضا و انقضا (مثل قبل)
|
||||
├─ توکن از نوع اپ مشتری نباشد (typ)
|
||||
│
|
||||
├─ AdminSessionService.isActive(jti)
|
||||
│ ├─ ردیابی خاموش ⇒ true، بدون هیچ کوئری (هزینه دقیقاً مثل قبل)
|
||||
│ ├─ توکن بدون jti (صادرشده پیش از فعالسازی) ⇒ true تا انقضا
|
||||
│ ├─ ردیف AdminSession باطل شده یا وجود ندارد ⇒ ۴۰۱
|
||||
│ └─ سالم ⇒ ادامه
|
||||
│
|
||||
└─ AdminUser + مجوزها از دیتابیس (مثل قبل، تا تغییر دسترسی فوری اثر کند)
|
||||
```
|
||||
|
||||
`AUTH_SESSION_TRACKING`: خالی = همراه `IAM_ENABLED` · `true` = روشن بدون IAM ·
|
||||
`false` = خاموش (ابطال مرکزی دیگر به اینجا نمیرسد).
|
||||
|
||||
---
|
||||
|
||||
## ۵. جریان ۳ — خروج سراسری (تنها مسیر ورودی از IAM)
|
||||
|
||||
```
|
||||
IAM: خروج کاربر یا «بستن همه نشستها» توسط ادمین
|
||||
│ logout_token امضاشده با RS256 (application/x-www-form-urlencoded)
|
||||
▼
|
||||
POST /api/iam/backchannel-logout ← بدون احراز هویت، عمداً
|
||||
│
|
||||
├─ IamLogoutService.verify
|
||||
│ ├─ alg باید RS256 باشد (alg=none رد میشود)
|
||||
│ ├─ امضا با کلید عمومی JWKS
|
||||
│ ├─ iss برابر issuer پیکربندیشده
|
||||
│ ├─ aud شامل client_id ما
|
||||
│ ├─ events شامل رخداد back-channel logout (تا ID Token جای آن نرود)
|
||||
│ ├─ nonce نباید وجود داشته باشد
|
||||
│ └─ exp/iat با تحمل ۶۰ ثانیه انحراف ساعت
|
||||
│
|
||||
└─ AdminSessionService
|
||||
├─ sid دارد ⇒ همان نشست باطل میشود
|
||||
└─ فقط sub دارد ⇒ همه نشستهای آن هویت
|
||||
⇒ درخواست بعدی با آن توکن ۴۰۱ میگیرد
|
||||
```
|
||||
|
||||
پاسخ **همیشه ۲۰۰** است (حتی برای توکن نامعتبر) تا IdP بینهایت تلاش مجدد نکند؛
|
||||
دلیل رد شدن در لاگ سرویس میآید.
|
||||
|
||||
آدرسی که باید در IAM برای این کلاینت ثبت شود:
|
||||
|
||||
```
|
||||
backchannel_logout_uri = https://<آدرس این API>/api/iam/backchannel-logout
|
||||
```
|
||||
|
||||
ابطال محلی هم به همین مسیر وصل است: غیرفعال کردن حساب یا تغییر رمز یک ادمین
|
||||
(`AdminUsersService.update`) نشستهای او را میبندد.
|
||||
|
||||
---
|
||||
|
||||
## ۶. نگاشت هویتها
|
||||
|
||||
| در IAM | در gold-bank | قاعده |
|
||||
| --- | --- | --- |
|
||||
| موبایل | `AdminUser.mobile` | تنها معیار نگاشت کاربر — هیچ ستون جدیدی روی این جدول نیست |
|
||||
| `sub` | `AdminSession.iamSub` | برای خروج سراسریِ «همه نشستهای این هویت» |
|
||||
| `sid` | `AdminSession.iamSessionId` | برای ابطال دقیقاً همان نشست |
|
||||
| نقشها | `UserType` / `Permission` محلی | از IAM **خوانده نمیشود**؛ دسترسیها مال همین سامانه است |
|
||||
|
||||
**چرا شناسههای IAM روی جدول جدید نشستند و نه روی `AdminUser`:** تا فعال کردن این
|
||||
قابلیت هیچ تغییری در جدولهای موجود لازم نداشته باشد و برگرداندنش هم به همان
|
||||
سادگی باشد. اگر روزی فیلد دیگری از IAM لازم شد، همین الگو ادامه یابد.
|
||||
|
||||
---
|
||||
|
||||
## ۷. رفتار در خطا (خلاصه تصمیمها)
|
||||
|
||||
| وضعیت | `fallback` (پیشفرض) | `exclusive` |
|
||||
| --- | --- | --- |
|
||||
| IAM خاموش/ناقص | یک bcrypt، بدون شبکه — رفتار کاملاً قبلی | همان |
|
||||
| IAM در دسترس نیست | bcrypt محلی؛ ورود ادمینها قطع نمیشود | ۴۰۱ «سرویس هویت در دسترس نیست» |
|
||||
| کاربر در IAM نیست | bcrypt محلی (مناسب مهاجرت تدریجی) | ۴۰۱ |
|
||||
| رمز در IAM غلط است | bcrypt محلی | ۴۰۱ |
|
||||
| حساب در IAM قفل است | **۴۰۱** — قفل IAM دور زده نمیشود | ۴۰۱ |
|
||||
| حساب دو مرحلهای است | ۴۰۱ با پیام روشن (این فرم مرحله کد ندارد) | همان |
|
||||
| ثبت `AdminSession` ناموفق | ورود انجام میشود (فقط هشدار لاگ) — بدترین حالت: نشستی که زودتر قابل ابطال نیست | همان |
|
||||
| توکن خروج نامعتبر | ۲۰۰ + هشدار لاگ؛ هیچ نشستی بسته نمیشود | همان |
|
||||
|
||||
---
|
||||
|
||||
## ۸. بررسی سریع اتصال
|
||||
|
||||
```bash
|
||||
cd apps/api
|
||||
npx jest src/modules/iam src/modules/auth src/modules/sessions # ۶۱ تست
|
||||
npx prisma migrate deploy # جدول AdminSession
|
||||
```
|
||||
|
||||
مشکلات پرتکرار:
|
||||
|
||||
| نشانه | علت |
|
||||
| --- | --- |
|
||||
| بعد از چند ورود همه ۴۲۹ میگیرند | `client_ip` نمیرسد؛ `trust proxy` تنظیم نشده |
|
||||
| خروج سراسری بیاثر است | `backchannel_logout_uri` ثبت نشده، یا `AUTH_SESSION_TRACKING=false` |
|
||||
| همه ادمینها ۴۰۱ میگیرند | جدول `AdminSession` پاک شده — ردیف نبود یعنی «باطل» |
|
||||
| `unsupported logout token algorithm` در لاگ | توکن با RS256 امضا نشده (تلاش جعل یا پیکربندی اشتباه IdP) |
|
||||
| حساب دو مرحلهای وارد نمیشود | این پنل مرحله «ورود کد» ندارد — بخش ۷ سند `iam-sso.md` |
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
# اتصال gold-bank به سامانه هویت متمرکز (Central IAM)
|
||||
|
||||
> **نحوه ارتباط (مسیر داده، اندپوینتها، رفتار در خطا):** [`iam-connection.md`](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-agent`
|
||||
- `jwt.strategy.ts` — یک بررسی «نشست باطل نشده باشد» (در حالت خاموش، بدون کوئری)
|
||||
- `admin-users.service.ts` — غیرفعال کردن حساب یا تغییر رمز، نشستها را میبندد
|
||||
- `app.module.ts` — ثبت `IamModule` و `AdminSessionModule`
|
||||
- `prisma/schema.prisma` — فقط مدل جدید `AdminSession` (+ رابطه روی `AdminUser`)
|
||||
|
||||
---
|
||||
|
||||
## ۳. فعالسازی
|
||||
|
||||
### گام ۱ — ثبت این سامانه بهعنوان کلاینت در IAM
|
||||
|
||||
باید کلاینت از نوع `confidential` و **`is_trusted: true`** باشد؛ فقط چنین
|
||||
کلاینتی اجازه «ورود واگذارشده» دارد.
|
||||
|
||||
```bash
|
||||
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`
|
||||
|
||||
```dotenv
|
||||
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 پراکسی را میدهد و همان مشکل برمیگردد.
|
||||
|
||||
### احراز دو مرحلهای
|
||||
|
||||
فرم ورود این پنل فقط «موبایل + رمز» است و **هیچ مرحلهی ورود کدی ندارد**؛ پس جایی
|
||||
برای گرفتن کد عامل دوم وجود ندارد و این مسیر نمیتواند آن را تکمیل کند. ورود چنین
|
||||
حسابی با پیام روشن رد میشود (نه خطای مرموز). سه گزینه:
|
||||
|
||||
1. برای حسابهایی که از این پنل وارد میشوند 2FA را در IAM فعال نکنید (سادهترین)؛
|
||||
2. مرحلهی «ورود کد» را به همین فرم اضافه کنید؛ آنوقت میتوان مثل سامانه «همکار»
|
||||
کد را واسطه کرد یا با کانال خودِ سامانه فرستاد
|
||||
(`/api/v1/auth/mfa/verify` و `/api/v1/auth/mfa/delegate` در IAM آمادهاند)؛
|
||||
3. یا جریان مرورگری 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` این سامانه ثبت
|
||||
میشود، پس گزارشهای امنیتی متمرکز کامل باقی میمانند.
|
||||
|
||||
---
|
||||
|
||||
## ۶. تست
|
||||
|
||||
```bash
|
||||
cd apps/api
|
||||
pnpm test # ۱۱۵ تست، بدون دیتابیس و بدون شبکه
|
||||
pnpm test -- iam-logout.service # تأیید توکن خروج سراسری با کلید RSA واقعی
|
||||
pnpm test -- admin-session # نشستهای قابل ابطال
|
||||
```
|
||||
|
||||
تستها بدون دیتابیس و بدون شبکه اجرا میشوند و مهمترین چیزی که تضمین میکنند
|
||||
این است: **با خاموش بودن قابلیت، هیچ درخواستی به IAM نمیرود و ورود دقیقاً مثل
|
||||
قبل کار میکند**؛ و با روشن بودن، خرابی IAM ورود ادمین را قطع نمیکند.
|
||||
Reference in New Issue
Block a user