در معماریهای سنتی PHP معمولاً هر سایت Session خودش را مدیریت میکند.
مثلاً:
shop.example.com
↓
PHP Session
panel.example.com
↓
PHP Session
اما وقتی چند اپلیکیشن به یک Identity Provider مرکزی متصل میشوند، موضوع کمی پیچیدهتر میشود.
فرض کنید معماری ما اینگونه است:
Identity Provider
auth.example.com
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
Portal Shop Admin
کاربر در Portal وارد حساب خود شده است.
سپس در Shop نیز Login میکند و به دلیل SSO نیازی به وارد کردن مجدد رمز عبور ندارد.
اما یک سؤال مهم وجود دارد:
اگر کاربر در Identity Provider Logout کند، Shop و Portal از کجا بفهمند Session مرکزی دیگر معتبر نیست؟
اینجا مفهوم OIDC Session Management مطرح میشود.
# OIDC Session Management چیست؟
OIDC Session Management مجموعهای از مکانیزمها برای مدیریت و هماهنگسازی وضعیت Session بین:
- OpenID Provider یا OP
- Relying Party یا RP
- چند Application متصل به Identity Provider
است.
در این معماری:
OP
=
OpenID Provider
RP
=
Relying Party
مثلاً:
auth.example.com
│
│ OP
│
├──────── portal.example.com
│ RP
│
├──────── shop.example.com
│ RP
│
└──────── admin.example.com
RP
# Session در OIDC دقیقاً به چه معناست؟
برای درک Session Management باید بین چند Session مختلف تفاوت بگذاریم.
Session محلی Application
مثلاً:
shop.example.com
ممکن است Session PHP زیر را داشته باشد:
$_SESSION['user_id'] = 1001;
این Session متعلق به Shop است.
Session در Identity Provider
Identity Provider نیز Session خودش را دارد.
مثلاً:
auth.example.com
ممکن است بداند:
User 1001
Authenticated
این Session مرکزی باعث میشود کاربر در Application دیگری بدون وارد کردن دوباره رمز عبور احراز هویت شود.
# یک کاربر، چند Session
بنابراین ممکن است همزمان داشته باشیم:
Identity Provider Session
│
├── Portal Session
├── Shop Session
└── Admin Session
این Sessionها به هم مرتبط هستند، اما الزاماً یک Session مشترک PHP نیستند.
# چرا نمیتوانیم یک PHP Session مشترک داشته باشیم؟
ممکن است این سؤال مطرح شود:
> چرا Session را روی یک Database یا Redis مشترک قرار ندهیم؟
از نظر فنی ممکن است، اما این معماری دیگر همان مدل استاندارد SSO با OIDC نیست.
ضمن اینکه Applicationها ممکن است:
- روی سرورهای متفاوت باشند.
- با تکنولوژیهای متفاوت ساخته شده باشند.
- Session Cookieهای متفاوت داشته باشند.
- توسط تیمهای متفاوت توسعه داده شوند.
- حتی روی Domainهای متفاوت باشند.
OIDC اجازه میدهد این Applicationها بدون اشتراک مستقیم Session داخلی با یکدیگر کار کنند.
# OIDC Session Management چه مشکلی را حل میکند؟
فرض کنید کاربر در سه Application Login است:
Portal → Logged In
Shop → Logged In
Admin → Logged In
سپس در Identity Provider Logout میکند:
auth.example.com
↓
Logout
حالا وضعیت مرکزی:
Identity Provider Session
↓
Logged Out
اما Sessionهای محلی ممکن است همچنان باقی مانده باشند:
Portal → Logged In
Shop → Logged In
Admin → Logged In
این اختلاف وضعیت باید مدیریت شود.
# OP و RP چه هستند؟
دو اصطلاح مهم:
OP
OpenID Provider
سرویسی که:
- کاربر را Authenticate میکند.
- ID Token صادر میکند.
- Session مرکزی را مدیریت میکند.
- Endpointهای OIDC را ارائه میدهد.
مثلاً:
https://auth.example.com
RP
Relying Party
Applicationای که از Identity Provider برای Login استفاده میکند.
مثلاً:
https://shop.example.com
یا:
https://portal.example.com
# معماری Session Management
یک معماری ساده:
Browser
│
├───────────────┐
│ │
▼ ▼
RP Application OP
│ │
│ │
Local Session OP Session
RP باید بتواند متوجه شود وضعیت Session مرکزی تغییر کرده است.
# Session State چیست؟
در OIDC Session Management مفهومی به نام:
session_state
وجود دارد.
این مقدار برای کمک به RP جهت تشخیص تغییر وضعیت Session در OP استفاده میشود.
در فرآیند Authentication ممکن است چنین مقداری دریافت شود:
session_state=abc123...
RP این مقدار را نگهداری میکند.
# چرا Session State مهم است؟
فرض کنید:
session_state = STATE-A
کاربر وارد Application شده است.
بعداً وضعیت Session در OP تغییر میکند.
مثلاً:
Logout
در نتیجه وضعیت Session دیگر مانند قبل نیست.
RP میتواند با سازوکار Session Management متوجه این تغییر شود.
# Check Session Iframe چیست؟
در مدل OIDC Session Management، یکی از مکانیزمهای شناختهشده استفاده از یک iframe برای بررسی Session در OP است.
به صورت ساده:
RP Page
│
└── iframe
│
▼
OP Session
RP و OP میتوانند از طریق این مکانیزم وضعیت Session را بررسی کنند.
# معماری Check Session
فرض کنیم:
shop.example.com
به عنوان RP در مرورگر باز است.
صفحه RP یک iframe از OP دارد:
Shop
│
└── iframe
│
▼
auth.example.com
JavaScript سمت RP وضعیت Session را بررسی میکند.
# چرا iframe در این معماری استفاده میشود؟
هدف این است که RP بتواند متوجه تغییر Session در OP شود، بدون اینکه مجبور باشد برای هر بررسی یک درخواست مستقیم Server-to-Server ارسال کند.
اما باید توجه کرد که اجرای این مکانیزم به محدودیتهای مرورگر، Cookie Policy و معماری واقعی Application نیز وابسته است.
# جریان کلی OIDC Session Management
فرآیند را میتوان به صورت زیر تصور کرد:
1. User Login
↓
2. OP creates Session
↓
3. OP returns ID Token + session_state
↓
4. RP creates Local Session
↓
5. RP monitors Session State
↓
6. OP Session changes
↓
7. RP detects change
↓
8. RP re-checks authentication
↓
9. RP updates Local Session
# Login چه اتفاقی ایجاد میکند؟
فرض کنید کاربر به:
shop.example.com
مراجعه میکند.
Shop کاربر را به OP هدایت میکند:
shop
↓
auth.example.com
اگر کاربر قبلاً Login کرده باشد:
OP Session = Active
در نتیجه ممکن است بدون نمایش فرم Login مجدداً Authorization انجام شود.
# ایجاد Local Session
بعد از دریافت Authorization Code و انجام Token Exchange، RP اطلاعات لازم را اعتبارسنجی میکند.
سپس Session محلی ایجاد میشود:
session_start();
$_SESSION['user_id'] = $userId;
$_SESSION['authenticated'] = true;
در کنار آن میتوان اطلاعات مربوط به Session مرکزی را نیز نگهداری کرد:
$_SESSION['oidc_session_state'] = $sessionState;
# Session State را کجا نگهداری کنیم؟
اگر Application Server-Side PHP است، میتوان آن را در Session سمت سرور نگهداری کرد.
مثلاً:
$_SESSION['oidc_session_state'] = $sessionState;
نیازی نیست اطلاعات حساس Authentication را در JavaScript عمومی قرار دهیم.
# تشخیص تغییر Session
در مدل Session Management، RP به صورت دورهای وضعیت Session را بررسی میکند.
یک مفهوم ساده:
Current Session State
│
▼
Check OP Session
│
▼
Same?
┌──────┴──────┐
│ │
Yes No
│ │
▼ ▼
Continue Re-check Login
# اگر Session تغییر کرده باشد چه کنیم؟
تغییر Session State لزوماً به معنای Logout قطعی نیست.
ممکن است کاربر:
- Logout کرده باشد.
- Login کرده باشد.
- Session تغییر کرده باشد.
- Account دیگری را فعال کرده باشد.
بنابراین RP نباید صرفاً با دیدن تغییر State فوراً Session را حذف کند.
بهتر است فرآیند Authentication را مجدداً بررسی کند.
# چرا Re-check مهم است؟
فرض کنید:
session_state تغییر کرد
RP باید مشخص کند:
آیا کاربر هنوز Authenticated است؟
بنابراین میتواند کاربر را به Authorization Endpoint هدایت کند یا طبق معماری Client خودش وضعیت Session را مجدداً بررسی کند.
# Logout سناریوی اول: Logout در RP
کاربر در Shop روی Logout کلیک میکند.
Shop
↓
Destroy Local Session
↓
RP Logout
در این حالت ممکن است Session مرکزی OP هنوز فعال باشد.
بنابراین اگر کاربر دوباره به Shop یا Application دیگری برود، ممکن است بدون وارد کردن مجدد رمز عبور Login شود.
این موضوع با Global Logout متفاوت است.
# Logout سناریوی دوم: Logout در OP
کاربر مستقیماً از Identity Provider Logout میکند:
auth.example.com
↓
Logout
حالا Session مرکزی از بین میرود.
Applicationهای متصل باید در صورت استفاده از Session Management مناسب، تغییر وضعیت را تشخیص دهند.
# Logout سناریوی سوم: RP-Initiated Logout
در مقاله قبلی درباره:
RP-Initiated Logout
صحبت کردیم.
در این مدل:
User
↓
Shop Logout
↓
OP Logout Endpoint
↓
OP Session Destroyed
↓
Redirect
در نتیجه Logout از سمت RP میتواند باعث Logout مرکزی شود.
# تفاوت Session Management و RP-Initiated Logout
این دو را نباید یکی بدانیم.
RP-Initiated Logout
میگوید:
> RP از OP درخواست Logout میکند.
Session Management
میگوید:
> RP چگونه تغییر وضعیت Session در OP را متوجه شود؟
بنابراین میتوانند مکمل یکدیگر باشند.
# Session Management و Back-Channel Logout
در OIDC روشهای مختلفی برای هماهنگسازی Logout وجود دارد.
یکی از روشها:
Back-Channel Logout
است.
در این مدل OP میتواند از سمت Server به RP اطلاع دهد که Session باید Logout شود.
مثلاً:
OP
│
├── POST → Portal
│
├── POST → Shop
│
└── POST → Admin
هر Application Session مربوطه را مدیریت میکند.
# Front-Channel Logout
در Front-Channel Logout، هماهنگسازی Logout از طریق Browser انجام میشود.
به صورت مفهومی:
Browser
│
├── OP
├── Portal
├── Shop
└── Admin
این روش با Back-Channel متفاوت است.
# Session Management یا Back-Channel Logout؟
این دو جایگزین کامل یکدیگر نیستند.
Session Management بیشتر روی:
تشخیص تغییر Session
تمرکز دارد.
Back-Channel Logout روی:
ارسال Logout از OP به RP
تمرکز دارد.
در معماریهای مختلف ممکن است از یکی یا ترکیبی از این مکانیزمها استفاده شود.
# پیادهسازی Local Session در PHP
یک Session ساده:
session_start();
$_SESSION['user_id'] = $userId;
$_SESSION['oidc_session_state'] = $sessionState;
برای Logout محلی:
session_start();
$_SESSION = [];
if (ini_get('session.use_cookies')) {
$params = session_get_cookie_params();
setcookie(
session_name(),
'',
time() - 42000,
$params['path'],
$params['domain'],
$params['secure'],
$params['httponly']
);
}
session_destroy();
در پروژه واقعی بهتر است این منطق در یک Service اختصاصی مدیریت Session قرار بگیرد.
# Session Cookie را جدی بگیرید
حتی اگر OIDC را به شکل صحیح پیادهسازی کرده باشید، Session داخلی PHP همچنان یک Credential مهم است.
Cookie Session بهتر است تنظیمات امنیتی مناسب داشته باشد:
session_set_cookie_params([
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);
استفاده از HTTPS در Production نیز ضروری است.
# Session Fixation
هنگام Login بهتر است Session ID تغییر کند.
مثلاً:
session_regenerate_id(true);
این کار از Session Fixation جلوگیری میکند.
یک جریان مناسب:
Authentication Success
↓
session_regenerate_id(true)
↓
Create Authenticated Session
# Session Management و CSRF
Endpointهای مربوط به تغییر وضعیت Session مانند Logout و Account Management نیز باید در برابر درخواستهای ناخواسته طراحی مناسبی داشته باشند.
بهخصوص اگر Logout یا عملیات مرتبط با Session از طریق Browser انجام میشود، باید معماری Cookie و CSRF را بهدرستی بررسی کنیم.
# Session State با Access Token چه تفاوتی دارد؟
این دو مفهوم کاملاً متفاوت هستند.
Access Token
برای دسترسی به Resource Server است.
API Access
Session State
برای کمک به مدیریت وضعیت Session بین OP و RP استفاده میشود.
Session Synchronization
نباید Session State را به عنوان Credential دسترسی به API در نظر گرفت.
# Session State را Token امنیتی فرض نکنید
یک اشتباه رایج این است که توسعهدهنده تصور کند:
session_state
مثل Access Token است.
در حالی که کاربرد آن متفاوت است و نباید آن را جایگزین Access Token یا ID Token کنیم.
# معماری PHP + OIDC
یک پروژه میتواند ساختار زیر را داشته باشد:
app/
├── Controllers/
│ ├── LoginController.php
│ ├── CallbackController.php
│ └── LogoutController.php
│
├── Services/
│ ├── OidcClient.php
│ ├── SessionManager.php
│ └── TokenManager.php
│
├── Security/
│ ├── StateValidator.php
│ └── NonceValidator.php
│
└── Repositories/
└── UserRepository.php
Session Management نیز بهتر است منطق مستقل داشته باشد.
# یک SessionManager ساده
مثلاً:
class SessionManager
{
public function login(
int $userId,
string $sessionState
): void {
session_regenerate_id(true);
$_SESSION['user_id'] = $userId;
$_SESSION['authenticated'] = true;
$_SESSION['oidc_session_state'] = $sessionState;
}
public function logout(): void
{
$_SESSION = [];
session_destroy();
}
}
در پروژه واقعی میتوان کنترلهای بیشتری اضافه کرد.
# Session State را در Callback ذخیره کنیم
بعد از Authorization Callback، در صورت وجود Session State:
$sessionState = $oidcResponse['session_state'] ?? null;
و:
$sessionManager->login(
$userId,
$sessionState
);
# مشکل Domainهای مختلف
فرض کنید:
auth.example.com
shop.example.com
admin.example.com
هستند.
Session Cookie هر Application باید مستقل مدیریت شود.
نباید تصور کنیم:
PHP Session
به صورت خودکار بین این Domainها مشترک است.
OIDC دقیقاً برای حل چنین معماریهایی طراحی شده است.
# آیا میتوان Session Cookie را بین Subdomainها مشترک کرد؟
از نظر فنی در برخی معماریها میتوان Cookie Domain را روی:
.example.com
تنظیم کرد.
اما این کار باعث میشود سطح دسترسی Cookie بین Subdomainها گستردهتر شود.
در بسیاری از معماریهای OIDC بهتر است هر Application Session خودش را داشته باشد و برای SSO از Identity Provider استفاده شود.
# Session Management در چند Application
فرض کنید:
auth.example.com
│
├── portal.example.com
├── shop.example.com
├── crm.example.com
└── admin.example.com
هر Application:
Local Session
خودش را دارد.
اما:
Identity Provider Session
مرکز Authentication است.
# سناریوی کامل Login
User
│
▼
Shop
│
│ No Local Session
▼
Identity Provider
│
│ Existing OP Session
▼
Authorization
│
▼
Callback
│
▼
Token Validation
│
▼
Create Shop Session
# سناریوی Logout
User
│
▼
Shop Logout
│
├── Destroy Local Session
│
└── RP-Initiated Logout
│
▼
Identity Provider
│
▼
Destroy OP Session
بعد از آن Applicationهای دیگر نیز باید بر اساس مکانیزم Logout/Session Management انتخابشده، Session خود را بهروزرسانی کنند.
# چرا Logout در همه Applicationها ساده نیست؟
فرض کنید:
Portal
Shop
Admin
هرکدام Session جدا دارند.
اگر OP Session حذف شود، Sessionهای PHP قبلی هنوز ممکن است وجود داشته باشند.
بنابراین سیستم باید مشخص کند:
OP Logout
↓
How do RPs learn about it?
راهکار میتواند بر اساس نیاز شامل:
- Session Management
- Front-Channel Logout
- Back-Channel Logout
- RP-Initiated Logout
باشد.
# Performance و Session Management
اگر Browser دائماً وضعیت Session را بررسی کند، باید Polling Interval منطقی باشد.
مثلاً:
Check every 5 seconds
ممکن است برای یک پروژه بیش از نیاز باشد.
یا:
Check every 60 seconds
ممکن است برای یک سیستم حساس زیاد باشد.
بنابراین مقدار مناسب به نیاز واقعی سیستم بستگی دارد.
# امنیت JavaScript Session Check
اگر از مکانیزم Browser-based برای Session Check استفاده میکنیم، باید به مواردی مانند:
- Same-Origin Policy
- iframe restrictions
- Cookie policies
- CSP
- HTTPS
- Third-party Cookie restrictions
توجه داشته باشیم.
این بخش خصوصاً در مرورگرهای مدرن میتواند روی طراحی Session Management تأثیر بگذارد.
# چرا Cookie Policy مهم است؟
در معماریهای چند Domain، Browser تصمیم میگیرد Cookieها را در چه شرایطی ارسال کند.
بنابراین اگر معماری شما به Cookieهای Cross-Site وابسته باشد، باید رفتار مرورگرهای هدف را به دقت بررسی کنید.
نباید فرض کنیم:
> هر Cookieای در هر iframe همیشه ارسال میشود.
# Session Management در معماری مدرن
در پروژههای جدید بهتر است Session Management را به شکل یک سیستم مستقل ببینیم:
Authentication
│
▼
Identity Provider
│
├── Token Management
│
├── Session Management
│
└── Logout Management
│
▼
RPs
# ارتباط Session Management با Token Revocation
در مقاله قبلی دیدیم:
Logout
↓
Revoke Refresh Token
حالا میتوانیم این معماری را کاملتر کنیم:
Logout
│
├── Local Session Destroy
│
├── Refresh Token Revocation
│
├── OP Session Logout
│
└── Notify / Detect RPs
در نتیجه Session و Token Management به یکدیگر متصل میشوند.
# ارتباط با Account Linking
در مقاله Account Linking یاد گرفتیم که هویت خارجی کاربر بهتر است با ترکیبی مانند:
issuer + subject
شناسایی شود.
Session Management نیز باید به User Identity صحیح متصل باشد.
مثلاً:
OIDC Identity
↓
Local User
↓
Local Session
بنابراین نباید صرفاً بر اساس Email Session را مدیریت کنیم.
# اشتباهات رایج در OIDC Session Management
1. تصور Session مشترک بین همه سایتها
OIDC به معنای Shared PHP Session نیست.
2. حذف Session محلی و تصور Logout کامل
حذف Session PHP لزوماً OP Session را حذف نمیکند.
3. Logout کردن OP بدون توجه به Sessionهای RP
Sessionهای محلی Applicationها ممکن است همچنان وجود داشته باشند.
4. استفاده نادرست از Session State
session_state را نباید با Access Token یا ID Token اشتباه گرفت.
5. اعتماد به Session State بدون Re-check
تغییر Session State باید باعث بررسی وضعیت Authentication شود، نه اینکه بدون بررسی نتیجه خاصی فرض شود.
6. وابستگی کامل به Third-Party Cookie
معماریهای Browser-based Session Management باید محدودیتهای Cookie و iframe در مرورگرهای جدید را در نظر بگیرند.
7. Session Cookie ناامن
حتی با OIDC باید Session Cookie را با:
Secure
HttpOnly
SameSite
و تنظیمات مناسب محافظت کرد.
8. عدم Regenerate کردن Session ID
بعد از Login بهتر است:
session_regenerate_id(true);
انجام شود.
# چکلیست OIDC Session Management
قبل از Production بررسی کنید:
- [ ] OP Session و Local Session را از هم تفکیک کردهاید.
- [ ] RP Session مستقل دارد.
- [ ] Session ID بعد از Login Regenerate میشود.
- [ ] Session Cookie دارای Secure است.
- [ ] Session Cookie دارای HttpOnly است.
- [ ] SameSite متناسب با معماری انتخاب شده است.
- [ ] Logout محلی از Logout مرکزی تفکیک شده است.
- [ ] Refresh Token هنگام Logout طبق سیاست سیستم Revoke میشود.
- [ ] RP-Initiated Logout در صورت نیاز پیادهسازی شده است.
- [ ] Session State با Token اشتباه گرفته نمیشود.
- [ ] تغییر Session State باعث Re-check مناسب میشود.
- [ ] محدودیت iframe و Cookie مرورگرها در نظر گرفته شده است.
- [ ] Session Management باعث ایجاد Polling غیرضروری نمیشود.
- [ ] Back-Channel یا Front-Channel Logout در صورت نیاز بررسی شده است.
- [ ] Sessionهای چند دستگاه به شکل مستقل مدیریت میشوند.
# پرسشهای متداول
OIDC Session Management چیست؟
مجموعهای از مکانیزمها برای مدیریت و هماهنگسازی وضعیت Session بین OpenID Provider و Applicationهای متصل به آن است.
آیا OIDC Session Management همان SSO است؟
خیر.
SSO به کاربر اجازه میدهد با یک احراز هویت مرکزی وارد چند Application شود.
Session Management بیشتر به هماهنگسازی وضعیت Session و تشخیص تغییر آن مربوط میشود.
آیا هر Application باید PHP Session خودش را داشته باشد؟
در معماری معمول OIDC، بله، هر RP میتواند Session محلی خودش را داشته باشد و Identity Provider احراز هویت مرکزی را مدیریت کند.
اگر کاربر از Identity Provider Logout کند، آیا Session تمام سایتها خودکار حذف میشود؟
نه لزوماً.
Applicationها باید مکانیزم مناسب برای هماهنگسازی Logout داشته باشند؛ مانند Session Management، Front-Channel Logout یا Back-Channel Logout، بسته به معماری.
session_state چیست؟
مقداری است که در OIDC Session Management برای کمک به تشخیص تغییر وضعیت Session بین OP و RP استفاده میشود.
آیا Session State همان Access Token است؟
خیر.
Session State برای Session Management است و Access Token برای دسترسی به Resource Server.
آیا RP-Initiated Logout جای Session Management را میگیرد؟
خیر.
RP-Initiated Logout مکانیزم شروع Logout از سمت RP است، در حالی که Session Management برای تشخیص تغییر وضعیت Session استفاده میشود.
# جمعبندی
در معماریهای ساده ممکن است مدیریت Session فقط به یک Cookie و یک PHP Session محدود شود.
اما وقتی چند Application به یک Identity Provider متصل میشوند، معماری پیچیدهتر میشود:
Identity Provider
│
OP Session │
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
Portal Shop Admin
│ │ │
Session Session Session
در این معماری باید تفاوت بین موارد زیر را همیشه در نظر داشته باشیم:
OP Session
Local RP Session
Access Token
Refresh Token
ID Token
Session State
همچنین Logout دیگر فقط به معنی:
session_destroy();
نیست.
یک Logout کاملتر ممکن است شامل:
Destroy Local Session
+
Revoke Refresh Token
+
OP Logout
+
Synchronize RPs
باشد.
برای معماریهای چنداپلیکیشنی، انتخاب بین Session Management، RP-Initiated Logout، Front-Channel Logout و Back-Channel Logout باید بر اساس نیاز سیستم، نوع Applicationها و محدودیتهای Browser انجام شود.





