راهنمای جامع یکپارچه سازی با SSO پلاس

راهنمای پیاده‌سازی SSO پلاس در سازمان‌ها

فهرست مطالب

راهنمای جامع یکپارچه‌سازی SSO پلاس

نسخه ۴.۱ بهار ۱۴۰۴

1- معرفی SSOپلاس

در این سند به راهکار های یکپارچه سازی سامانه های اطلاعاتی/تخصصی با سامانه SSOپلاس پرداخته شده است، مخاطبان این سند افراد فنی و توسعه دهندگان نرم افزار هستند که قصد یکپارچه سازی سامانه های اطلاعات با SSOپلاس را دارند.

SSOپلاس یا سامانه پنجره واحد سامانه ها و مدیریت کاربران و دسترسی ها (Single Sign On) به اختصار SSO با متمرکزسازی احراز هویت و مجاز شماری دسترسی ها به کاربران، یک پنجره واحد برای سامانه های اطلاعاتی فراهم می سازد.

اسامی دیگری که SSO با آن ها نیز شناخته می شود :

  • Identity and Access Management
  • Central Authentication Server
  • Authorization Server
  • Authentication and Authorization and Account
  • Credential Management
  • User Management
  • Federated Identity

در واقع SSO یک قابلیت از IAM است که وظیفه دارد تا با یکبار دریافت اطلاعات هویتی کاربر به کاربر اجازه ورود به سامانه های اطلاعاتی مختلف را فراهم نماید به این صورت که هر بار رمز عبور وارد نکند این قابلیت مبتنی بر پروتکل HTTP است. به جهت عمومیت نام SSO برای عموم کاربران، نام گذاری این سامانه بر اساس نام SSO صورت گرفته است.

جدول ۱ – تعاریف و موجودیت ها

نام فارسی نام گذاری طبق OAuth نام گذاری طبق OIDC توضیحات
کاربر Resource Owner End-User کاربر یک سامانه اطلاعاتی
کلاینت Client Relying Party (RP) منظور سامانه اطلاعاتی / تخصصی یا همان برنامه‌های کاربردی که می‌خواهند با SSO یکپارچه‌سازی کنند.
سرور منبع Resource Server جایی که منابع اطلاعاتی وجود دارد… برای سادگی در این سند صرفاً کلاینت خواهیم داشت.
اس اس او Authorization Server OpenID Provider (OP) سرور احراز هویت و مجازشماری است که در این سند با نام SSO شناخته شده است.
محدوده Scope Scope محدوده مجوز در پروتکل OAuth مانند profile، email یا openid
کد Auth Code Auth Code یک رشته تصادفی که توسط SSO صادر می‌شود و باید به توکن تبدیل شود.
ادعا / ویژگی‌ها Claim ویژگی‌های اطلاعاتی درباره کاربر که در توکن ارسال می‌شوند.
توکن Token Token شامل:
  • Access Token: توکن دسترسی
  • ID Token: توکن هویت
  • Refresh Token: توکن تازه‌سازی
  • Userinfo Endpoint: پایانه اطلاعات کاربر به‌صورت JSON
  • Introspection Token: بررسی وضعیت توکن

جدول ۲ – آدرس پایانه‌ها

نام پایانه آدرس پایانه
آدرس پایه سامانه SSO پلاس را از راهبر دریافت کنید
Authorization Endpoint URL https://<SSO_URL>/auth/realms/sso/protocol/openid-connect/auth
Token Endpoint URL https://<SSO_URL>/auth/realms/sso/protocol/openid-connect/token
User Info Endpoint https://<SSO_URL>/auth/realms/sso/protocol/openid-connect/userinfo
Logout Endpoint https://<SSO_URL>/auth/realms/sso/protocol/openid-connect/logout
یا
https://<SSO_URL>/sso/logout
Introspection Endpoint https://<SSO_URL>/auth/realms/sso/protocol/openid-connect/token/introspect
Public Keys (JWKS) https://<SSO_URL>/auth/realms/sso/protocol/openid-connect/certs
.well-known Endpoint https://<SSO_URL>/auth/realms/sso/.well-known/openid-configuration

2- استانداردهای SSOپلاس

سامانه SSOپلاس با استفاده از پروتکل OAuth و OpenID Connect مکانیزم احراز هویت و دسترسی به سامانه های کاربردی و برنامه‌های مختلف را فراهم می‌کند. استانداردهای مرجع مورد استفاده در سامانه SSO و جهت مطالعه بیشتر:

  • OAuth 2.0 Framework (RFC 6749)
    • OAuth 2.0 یک استاندارد صنعت برای تفویض دسترسی یا Authorization است که به سامانه‌های تخصصی (کلاینت‌ها) امکان می‌دهد با کسب توکن‌های موقت به منابع کاربر در یک سرویس وب دسترسی محدوده‌دار پیدا کنند، بدون اینکه نیاز باشد نام‌کاربری و گذرواژه کاربر را مستقیماً در اختیار داشته باشند.
    • این RFC شرح جامعی از پروتکل OAuth را ارائه می‌دهد. این استاندارد توضیح دقیقی از مراحل احراز هویت، صدور توکن‌های دسترسی، انواع مجوزها و تعاملات بین موجودیت‌ها (کلاینت، سرویس‌دهنده احراز هویت و سرویس‌دهنده منابع) را ارائه می‌دهد.
  • OpenID Connect 1.0
    • یک لایهٔ احراز هویت Authentication مبتنی بر OAuth 2.0 است. OIDC با استفاده از جریان‌های OAuth (به‌ویژه Authorization Code Flow) امکان تأیید هویت کاربران را به صورت یکپارچه فراهم می‌کند. این پروتکل مفهومی به‌نام ID Token (توکن هویتی) را معرفی می‌کند که یک JWT JSON Web Token حاوی ادعاهای هویتی (claims) درباره کاربر است. به کمک ID Token و همچنین یک Endpoint اطلاعات کاربر (UserInfo)، کلاینت می‌تواند مشخصات پایه کاربر (مانند نام، ایمیل و …) را به شکل استاندارد دریافت کرده و هویت کاربر را احراز کند. به بیان دیگر، OAuth 2.0 مجوز دسترسی صادر می‌کند و OpenID Connect روی همان بستر، تأیید هویت کاربر را اضافه می‌کند.
  • OAuth 2.0 Bearer Token Usage (RFC 6750)
    • این RFC به جزئیات استفاده از توکن‌های حامل Bearer Tokens در پروتکل OAuth پرداخته و نحوه ارتباط بین کلاینت و سرویس‌دهنده منابع با استفاده از این توکن‌ها را توضیح می‌دهد.
  • The OAuth 2.0 Authorization Framework: JWT Profile (RFC 7523)
    • این RFC بر روی استفاده از JWT (JSON Web Tokens) در پروتکل OAuth تمرکز دارد. JWT‌ها به عنوان توکن‌های دسترسی مورد استفاده قرار می‌گیرند.
  • JSON Web Token (JWT) (RFC 7519)
    • این RFC به جزئیات استفاده از JWT به عنوان یک فرمت استاندارد برای نشانه‌گذاری Claims و اشتراک اطلاعات پرداخته و مشخصات و نحوه ایجاد و تجزیه‌تحلیل JWT را توضیح می‌دهد.
  • OAuth 2.0 for Native App (RFC 8252)
  • OAuth 2.0 Token Introspection (RFC 7662)
  • Proof Key of Code Exchange (RFC 7636)

3- مراحل شروع یکپارچه‌سازی و اطلاعات مورد نیاز

برای آغاز فرآیند یکپارچه‌سازی سامانهٔ تخصصی شما با SSO پلاس، ابتدا باید برخی اطلاعات اولیه توسط توسعه‌دهنده به راهبر SSO پلاس ارائه شود تا یک کلاینت جدید در سامانه SSO پلاس ثبت و تنظیم گردد. این اطلاعات عبارت‌اند از:

  1. نام انگلیسی کلاینت (Client ID): یک شناسه یکتای انگلیسی برای سرویس کلاینت شما که به عنوان client_id ثبت می‌شود. این نام معمولاً کوتاه و معرف سامانه شما است (مثلاً mycompany-portal).
  2. نام فارسی کلاینت: عنوان یا نام سامانه به زبان فارسی جهت مستندسازی و نمایش در پنل مدیریتی SSO پلاس (مثلاً “پرتال سازمان من”).
  3. توضیحات: شرح مختصری درباره کاربرد و ویژگی‌های سامانه‌ی شما یا دلیلی که نیاز به اتصال SSO دارد. این توضیح به راهبر کمک می‌کند تنظیمات را بهتر انجام دهد.
  4. آدرس شروع فرآیند احراز هویت (Base URL یا oidc-start): نشانی که با فراخوانی آن، فرآیند احراز هویت (Authentication) و دریافت مجوز ورود (Authorization Code Flow) برای سامانه‌ی شما آغاز می‌شود. این آدرس معمولاً به‌صورت یک صفحه یا مسیر مشخص در دامنه‌ی سرویس شما است (مثلاً: https://portal.mycompany.ir/oidc-start). برای آشنایی بیشتر به سرفصل مربوط به آدرس پایه مراجعه کنید.
  5. نشانی بازگشت (redirect_uri): گاها oidc-callback هم گفته می‌شود و یک یا چند آدرس URL در سامانه شما که قرار است پس از احراز هویت کاربر، مرورگر به آن‌ها هدایت شود. این آدرس‌ها باید دقیقاً به راهبر اعلام شوند تا در تنظیمات کلاینت به عنوان آدرس‌های مجاز بازگشت ثبت شوند. هر URLای که قصد دارید کاربر پس از ورود موفق یا خروج به آن بازگردد باید از پیش در SSO پلاس ثبت شده باشد (به عنوان مثال: https://portal.mycompany.ir/auth/callback).
  6. اعلام نوع سامانه عمومی یا محرمانه: کلاینت عمومی (Public Client) کلاینتی است که قادر به محافظت امن از اطلاعات محرمانه مثل client secret نیست (مثل اپ موبایل یا وب)، ولی کلاینت محرمانه (Confidential Client)  کلاینتی است که می‌تواند اطلاعات محرمانه مثل client secret را به‌صورت امن نگهداری کند مثل بک‌اند سرور.
  7. اعلام آدرس خروج Back-Channel در صورت وجود.

پس از دریافت اطلاعات فوق، راهبر SSO پلاس اقدامات زیر را انجام می‌دهد:

  1. ایجاد کلاینت جدید: راهبر در کنسول مدیریت SSO پلاس یک Client جدید می‌سازد و نام انگلیسی کلاینت (Client ID) را مطابق اطلاعات ارائه‌شده تنظیم می‌کند. همچنین نام فارسی و توضیحات درج می‌گردد تا در آینده قابل شناسایی باشد.
  2. تنظیم آدرس‌های بازگشت: راهبر تمامی مقادیر Redirect URI اعلام‌شده را به عنوان آدرس‌های مجاز بازگشت برای کلاینت ثبت می‌کند. SSO پلاس در حین عملیات ورود، تنها به این آدرس‌های ثبت‌شده اجازه بازگشت (redirect) خواهد داد و در صورت عدم تطابق، درخواست را با خطا رد می‌کند.
  3. تعیین نوع کلاینت و تنظیمات امنیتی: بسته به نوع سامانه شما، راهبر نوع کلاینت را “عمومی (Public)” یا “محرمانه (Confidential)” مشخص می‌کند. برای کلاینت‌های سمت سرور که قادر به حفظ محرمانگی هستند (مانند وب‌سرورهای بک‌اند)، نوع Confidential تنظیم شده و یک Client Secret (رمز کلاینت) تصادفی تولید می‌شود. این Client Secret به عنوان گذرواژه کلاینت عمل کرده و باید به طور امن نزد سرویس شما نگه‌داری شود. برای کلاینت‌های عمومی مانند برنامه‌های تک‌صفحه‌ای (SPA) یا موبایل که امکان مخفی‌سازی Secret را ندارند، نوع Public انتخاب می‌شود (در این حالت Client Secret صادر نمی‌شود و به جای آن از مکانیزم‌های امن‌سازی مانند PKCE استفاده خواهد شد).

اطلاع‌رسانی اطلاعات اتصال: پس از پیکربندی، راهبر اطلاعات ضروری را در اختیار توسعه‌دهنده قرار می‌دهد. این اطلاعات معمولاً شامل Client ID ثبت‌شده، Client Secret  در صورت وجود، آدرس‌های سرویس‌های SSO پلاس شامل آدرس  Endpoint های مهم مانند Authorization Endpoint, Token Endpoint, UserInfo و … یا آدرس فایل تنظیمات .well-known و هر تنظیم خاص دیگر است. تأکید می‌شود: در کد یا تنظیمات خود از آدرس‌هایی استفاده کنید که راهبر SSO پلاس به شما اعلام می‌کند.

4- یکپارچه سازی سامانه تخصصی با SSOپلاس

جریان کد مجوز Authorization Code Flow : رایج‌ترین و امن‌ترین جریان OAuth/OIDC برای برنامه‌های وب و سرور است. در این روش، احراز هویت و اعطای مجوز در دو گام انجام می‌شود: ابتدا کاربر به SSO هدایت شده و یک Authorization Code کد مجوز موقت دریافت می‌شود؛ سپس سرویس سمت سرور این کد را به توکن‌ها تبدیل می‌کند. در ادامه جزئیات این جریان و پارامترهای لازم شرح داده شده است.
پس تمام فرآیند از دو مرحله تشکیل می شود: ۱. گرفتن کد از طریق مرورگر ۲. تبدیل کد به توکن با فراخوانی سرویس

4-1- گرفتن کد (Auth Code)

ابتدا کاربر باید به آدرس زیر هدایت شود. این کار معمولا با مرورگر انجام می‌شود:

https://<SSO_URL>/auth/realms/sso/protocol/openid-connect/auth?
client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&response_type=code&scope=openid

برای مثال با جایگذاری مقادیر:

https://sso.ssoplus.ir/auth/realms/sso/protocol/openid-connect/auth?
client_id=mycompany-portal&redirect_uri=https://portal.mycompany.ir/auth/callback&response_type=code&scope=openid

کاربر پس از لاگین موفق، به آدرس redirect_uri هدایت می شود و در URL یک پارامتر code به شکل زیر می‌بینید، یک دقیقه فرصت دارید این کد را به توکن تبدیل کنید.

https://portal.mycompany.ir/auth/callback?code=SplxlOBeZQ

4-2- تبدیل کد به توکن

در این مرحله کلاینت باید Auth Code دریافت شده از مرحله قبل را به توکن تبدیل کند برای این کار باید به پایانه token یک درخواست POST ارسال شود.

curl -X POST 'https://<SSO_URL>/auth/realms/sso/protocol/openid-connect/token'
-H 'Content-Type: application/x-www-form-urlencoded'
-d 'grant_type=authorization_code'
-d 'client_id=YOUR_CLIENT_ID'
-d 'client_secret=YOUR_CLIENT_SECRET_PROVIDED_BY_ADMIN'
-d 'code=GIVEN_AUTH_CODE_FROM_PREVIOUS_STEP'
-d 'redirect_uri=YOUR_REDIRECT_URI'
  

برای مثال با جایگذاری مقادیر:

curl -X POST 'https://sso.ssoplus.ir/auth/realms/sso/protocol/openid-connect/token'
-H 'Content-Type: application/x-www-form-urlencoded'
-d 'grant_type=authorization_code'
-d 'client_id=mycompany-portal'
-d 'client_secret=ABC123SECRETXYZ'
-d 'code=SplxlOBeZQ'
-d 'redirect_uri=https://portal.mycompany.ir/auth/callback'
  

می توانید از این لینک نمونه دستورات curl را مشاهده نمایید:

https://www.sso-plus.com/curl

5- آدرس شروع/پایه فرآیند احراز هویت (Base URL or oidc-start)

آدرس شروع فرآیند احراز هویت (Base URL یا oidc-start): نشانی که با فراخوانی آن، فرآیند احراز هویت (Authentication) و دریافت مجوز ورود (Authorization Code Flow) برای سامانه‌ی شما آغاز می‌شود. این آدرس معمولاً به‌صورت یک صفحه یا مسیر مشخص در دامنه‌ی سرویس شما است (مثلاً: https://portal.mycompany.ir/oidc-start).

آدرسی بازگشتی یا Redirect URI باید URL encode باشد(برای راهنمایی بیشتر کلیک کنید).

آدرس پایه همان URLی است که در تنظیمات کلاینت در SSO پلاس توسط راهبر ثبت می‌شود تا هنگام کلیک کاربر روی کارت سامانه، مرورگر کاربر به آن هدایت شود. این آدرس باید به‌دقت توسط توسعه‌دهنده سامانه تخصصی به راهبر SSO پلاس اعلام گردد. مطابق تصویر زیر:

تعریف آدرس پایه در تنظیمات کلاینت در SSO پلاس define Base URL on SSO PLUS

توصیه برای توسعه‌دهنده:

اگر مایل به کنترل دقیق‌تر بر امنیت احراز هویت، ردیابی نشست، و تولید مقادیر تصادفی (مانند state) هستید، حالت اول مناسب‌تر است. در غیر این صورت، با هماهنگی راهبر SSO پلاس می‌توانید از حالت دوم استفاده کنید و جریان ورود را به صورت ساده‌تر پیاده‌سازی نمایید.

توصیه برای راهبر SSO پلاس:

در زمان ثبت کلاینت، حتماً نوع آدرس پایه را با توسعه‌دهنده هماهنگ کنید.

5-1- حالت ۱: حرفه ای تر: هدایت به یک مسیر اختصاصی در خود سامانه تخصصی (oidc-start)

در این حالت، خود سامانه تخصصی وظیفه آغاز جریان Authorization Code Flow را بر عهده دارد. یعنی آدرس پایه به صفحه‌ای در سامانه اشاره می‌کند (مثلاً oidc-start) که در آن، سامانه به اختیار خود می‌تواند:

  • یک مقدار state یا nonce ایجاد کند.
  • مکانیزم PKCE را فعال کند.
  • سپس کاربر را به Authorization Endpoint هدایت کند.

این حالت برای سامانه‌هایی مفید است که می‌خواهند کنترل کامل جریان احراز هویت را در دست داشته باشند.

مثال آدرس پایه مدل ۱:

Base URL: https://myapp.com/oidc-start

5-2- حالت ۲: ساده‌تر — هدایت مستقیم از SSO پلاس به oidc-callback سامانه تخصصی

این حالت مناسب برای ساده‌سازی فرایند ورود و حذف نیاز به پیاده‌سازی مرحله oidc-start در سامانه تخصصی است.

در این حالت، راهبر SSO پلاس، آدرس پایه را طوری تنظیم می‌کند که مستقیماً به Authorization Endpoint از SSOپلاس اشاره کند و سپس کاربر به oidc-callback سامانه تخصصی هدایت ‌می‌شود:

مثال آدرس پایه مدل ۲ (که توسط راهبر تنظیم شده):

https://<SSO_URL>/auth/realms/sso/protocol/openid-connect/auth?
client_id=myapp-client&
redirect_uri=https://myapp.com/oidc-callback&
response_type=code&
scope=openid

توضیح:

  • کاربر پس از کلیک روی کارت سامانه با آدرس تنظیم‌شده فوق:
    • مستقیماً به آدرس redirect_uri (یا همان oidc-callback) سامانه تخصصی برگشت داده می‌شود، به همراه پارامتر code (کد مجوز).
  • یعنی پس از لاگین موفق، کاربر هدایت می‌شود به پایانه oidc-callback سامانه تخصصی با پارامتر کد مجوز:
    https://myapp.com/oidc-callback?code=abc123
  • سامانه تخصصی کد مجوز را دریافت کرده و با فراخوانی Token Endpoint سامانه SSOپلاس می‌تواند توکن‌ها را دریافت کند.

6- دریافت اطلاعات کاربر از پایانه Userinfo Endpoint

UserInfo Endpoint یک سرویس استاندارد OpenID Connect است که اطلاعات پروفایل کاربر احراز هویت‌شده را در قالب JSON برمی‌گرداند. این Endpoint برای دریافت اطلاعات تکمیلی کاربر به کار می‌رود (خصوصاً هنگامی که نمی‌خواهیم همه اطلاعات در ID Token جاگذاری شود یا برای به‌روز بودن داده‌ها).

برای فراخوانی UserInfo، باید یک Access Token معتبر (صادرشده در جریان OIDC) را به همراه درخواست ارسال کنید. این توکن باید شامل scopeهای لازم (مثل openid و profile) باشد تا SSO پلاس اجازه دسترسی به اطلاعات کاربر را بدهد.

فراخوانی به صورت یک درخواست HTTP (GET یا POST) به آدرس userinfo انجام می‌شود. متداول‌ترین روش استفاده از هدر Authorization است. در زیر یک نمونه درخواست با curl آمده است:

curl --request GET 
     --url "https://<SSO-BASE-URL>/auth/realms/sso/protocol/openid-connect/userinfo" 
     --header "Authorization: Bearer <ACCESS_TOKEN>"
  

در درخواست فوق، <ACCESS_TOKEN> باید با توکن دسترسی واقعی جایگزین شود. توجه کنید که نوع token_type در هدر ذکر شده (Bearer). اگر توکن معتبر و دارای مجوز باشد، SSO پلاس پاسخ را با کد 200 و یک JSON از claims کاربر برمی‌گرداند.

نمونه پاسخ:

{
  "sub": "248289761001", 
  "name": "علی احمدی",
  "given_name": "علی",
  "family_name": "احمدی",
  "preferred_username": "a.ahmadi",
  "email": "ali.ahmadi@example.com",
  "email_verified": true,
  "user_type": "PERSON"
}
  

اطلاعات دقیق برگردانده‌شده به scopeهای درخواست‌شده بستگی دارد. برای مثال اگر profile در scope بوده، فیلدهای نام و نام خانوادگی و … می‌آید؛ اگر email درخواست شده باشد، ایمیل و وضعیتش می‌آید. برخی اطلاعات ممکن است حتی بدون درخواست scope اضافی برگردند، چرا که حضور openid در scope معمولاً حداقل شامل شناسه کاربر (sub) است.

خطاهای محتمل در UserInfo:

  • اگر Access Token منقضی یا نامعتبر باشد، SSO پلاس معمولاً پاسخ 401 Unauthorized برمی‌گرداند. ممکن است یک JSON خطا نیز شامل error و error_description دریافت کنید (مثلاً error: “invalid_token”).
  • اگر توکن ارائه‌شده scope لازم را نداشته باشد (مثلاً profile و email را درخواست نکرده‌اید)، SSO پلاس ممکن است برخی فیلدها را خالی یا اصلاً برنگرداند. (در بیشتر موارد، SSO پلاس در صورت نبود مجوز، پاسخ 403 Forbidden نمی‌دهد بلکه صرفاً داده را حذف می‌کند).
  • اطمینان حاصل کنید که در هدر Authorization املای Bearer و فاصله پس از آن به‌درستی درج شود. اشتباه تایپی در این قسمت باعث 401 خواهد شد.

7- خروج کاربر از SSO (Logout)

7-1- خروج کاربر با مدل RP Initiated Logout (خروج توسط سامانه)

در این روش، سامانه‌ی شما به‌صورت استاندارد کاربر را از SSOپلاس خارج می‌کند.

7-1-1- خروج ساده

برای خروج ساده و بازگشت به صفحه ورود SSOپلاس، کافی است کاربر را به آدرس زیر هدایت کنید:

https://<SSO_URL>/auth/realms/sso/protocol/openid-connect/logout

یا نسخه‌ی کوتاه‌شده:

https://<SSO_URL>/sso/logout

7-1-2- خروج با هدایت کاربر به آدرس اختصاصی سامانه (Post-Logout Redirect)

7-1-2-1- خروج با تایید نهایی توسط کاربر

اگر می‌خواهید بعد از خروج، کاربر به آدرس مورد نظر سامانه شما منتقل شود و SSOپلاس از کاربر تأیید نهایی خروج را بگیرد، کاربر را به این آدرس هدایت کنید:

https://<SSO_URL>/auth/realms/sso/protocol/openid-connect/logout?
post_logout_redirect_uri=YOUR_VALID_REDIRECT_URI&
client_id=YOUR_CLIENT_ID

نکته: آدرس YOUR_VALID_REDIRECT_URI باید قبلاً توسط راهبر سامانه به‌عنوان آدرس بازگشتی معتبر (Valid Redirect URI) ثبت شده باشد، در غیر این صورت با خطا مواجه می‌شوید.

7-1-2-2- خروج مستقیم بدون تایید کاربر

برای خروج بدون تأیید کاربر و بازگشت مستقیم به آدرس مورد نظر سامانه (با امنیت بیشتر)، باید علاوه بر پارامترهای فوق، مقدار id_token_hint را نیز ارسال کنید:

https://<SSO_URL>/auth/realms/sso/protocol/openid-connect/logout?
post_logout_redirect_uri=YOUR_VALID_REDIRECT_URI&
id_token_hint=USER_ID_TOKEN

توجه:

  • id_token_hint همان توکن هویت کاربر است که در جریان ورود (login) توسط SSOپلاس به سامانه شما داده شده است. (این توکن با توکن دسترسی فرق دارد)
  • در این حالت، SSOپلاس بدون نمایش تأیید نهایی به کاربر، خروج را انجام می‌دهد و به آدرس بازگشتی منتقل می‌کند.
  • در صورتی که post_logout_redirect_uri نامعتبر باشد، با پیام خطا مواجه خواهید شد.
نمونه خطای معتبر نبودن آدرس بازگشت در درخواست Logout در سامانه SSO پلاس

8 – خروج یکپارچه  SLO – Single Logout 

برای سامانه‌هایی که خروج یکپارچه و هماهنگ با نشست‌های فعال نیاز دارند، SSOپلاس از Backchannel Logout پشتیبانی می‌کند. نبود امکان پیاده‌سازی خروج یکپارچه در سامانه‌های مقصد به معنی کاهش سطح امنیت نیست. با راهکارهای جایگزین می‌توانید همچنان نشست‌های کاربری را ایمن نگه دارید. برای جزئیات بیشتر پیشنهاد می‌کنیم این مقاله را مطالعه کنید: بیشتر بخوانید.

در این روش، زمانی که کاربر از SSOپلاس خارج می‌شود، SSOپلاس به تمام سامانه‌هایی که قبلاً به آن‌ها نشست (session) داده شده و آدرس Logout معتبر (Backchannel Logout URL) ثبت کرده‌اند، یک درخواست POST ارسال می‌کند که شامل یک Logout Token است.

محتوای Logout Token
Logout Token یک JWT است که اطلاعات زیر را شامل می‌شود:

  • sub : شناسه کاربر
  • aud : شناسه سامانه مقصد
  • iss : آدرس SSOپلاس
  • iat : زمان صدور توکن
  • events : حاوی رخداد logout
  • sid : شناسه نشست کاربر (Session ID)

نمونه‌ای از بدنه توکن:

{
  "iss": "https://<SSO_URL>/auth/realms/sso",
  "sub": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "aud": "your-client-id",
  "iat": 1621516400,
  "jti": "random-uuid",
  "events": {
    "http://schemas.openid.net/event/backchannel-logout": {}
  },
  "sid": "SESSION_STATE_VALUE"
}
  

نکته:

  • مقدار sid همان مقدار session_state است که در زمان احراز هویت موفق (زمان دریافت auth code) توسط SSOپلاس به سامانه داده می‌شود.
  • سامانه‌ها باید هنگام دریافت این توکن، نشست کاربر با این sid را یافته و آن را ابطال (invalidate) کنند.

مراحل عملی و مثال از ابتدا تا انتها

۱. دریافت session_state هنگام ورود

هنگام احراز هویت کاربر از طریق SSOپلاس (با پروتکل OIDC)، وقتی سامانه شما کاربر را برای ورود هدایت می‌کند و پس از تایید موفقیت‌آمیز، یک پاسخ مانند زیر دریافت می‌کند:

GET /your/callback?code=AUTH_CODE&state=xyz&session_state=6a9e2494-...-6f03f7e3
  
  • code: کد احراز هویت (برای دریافت توکن‌ها)
  • session_state: شناسه نشست کاربر بین سامانه و SSOپلاس

۲. تبادل code با توکن‌ها

سامانه شما با ارسال درخواست زیر به SSOپلاس، توکن‌ها (access_token و id_token و refresh_token) را دریافت می‌کند:

POST https://<SSO_URL>/auth/realms/sso/protocol/openid-connect/token

Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=AUTH_CODE
&client_id=your-client-id
&client_secret=your-client-secret (درصورت نیاز)
&redirect_uri=https://your-app/callback
  

پاسخ:

{
  "access_token": "...",
  "id_token": "...",
  "refresh_token": "...",
  "expires_in": 300,
  "session_state": "6a9e2494-...-6f03f7e3"
}
  

حتماً مقدار session_state را برای نگهداری نشست کاربر ذخیره کنید، زیرا هنگام Backchannel Logout همین مقدار برای شما ارسال خواهد شد.

۳. ثبت Backchannel Logout URL در SSOپلاس

راهبر سامانه باید برای هر کلاینت (client) آدرس خروج بک‌چنل (Backchannel Logout URL) را تعریف کند تا در صورت خروج کاربر، SSOپلاس یک POST به آن ارسال کند.

۴. رخداد خروج (Logout) و ارسال Logout Token

هر زمان که کاربر از SSOپلاس خارج شود، SSOپلاس برای همه کلاینت‌هایی که نشست فعال دارند، یک درخواست POST با محتوای Logout Token به آدرس Backchannel Logout ارسال می‌کند.

نمونه درخواست ارسالی از SSOپلاس به سامانه:

POST https://your-app.com/backchannel-logout

Content-Type: application/x-www-form-urlencoded

logout_token=eyJhbGciOiJSUzI1NiIsInR5cCI6...
  

۵. بررسی و پردازش Logout Token در سامانه

Logout Token یک JWT است که ساختار آن معمولاً به صورت زیر است (دیکود شده):

{
  "iss": "https://<SSO_URL>/auth/realms/sso",
  "sub": "07e9f944-6a84-476f-9e7f-3a22620f846b",
  "aud": "your-client-id",
  "iat": 1717632098,
  "jti": "55c23007-1b88-409d-9e9d-d7a08f485fa7",
  "events": {
    "http://schemas.openid.net/event/backchannel-logout": {}
  },
  "sid": "6a9e2494-...-6f03f7e3"
}
  

نکته مهم درباره Frontchannel Logout

در SSOپلاس به دلیل مشکلات امنیتی مرتبط با کوکی‌ها، سیاست‌های سختگیرانه‌ی مرورگرها و محدودیت‌های CSP  (Content Security Policy)، خروج Frontchannel (خروج با فراخوانی iframe یا ریدایرکت سمت مرورگر) پشتیبانی نمی‌شود و فقط از روش امن‌تر Backchannel Logout استفاده می‌شود.

9- خطاهای رایج

الف) خطاهای مرحله دریافت Authorization Code (مرحله لاگین)

خطا توضیح مثال پاسخ/رفتار
آدرس پایانه اشتباه اگر مسیر /auth/realms/sso/protocol/openid-connect/auth اشتباه باشد پاسخ HTTP 404 (صفحه پیدا نشد)
client_id اشتباه مقدار client_id صحیح نباشد یا ثبت نشده باشد پاسخ HTTP 400 + پیام خطا:
پارامتر نامعتبر است: client_id
redirect_uri نامعتبر آدرس بازگشت دقیقا مطابق با ثبت کلاینت نباشد پاسخ HTTP 400 + پیام خطا:
پارامتر نامعتبر است: redirect_uri
پارامتر اشتباه/ناقص مثلاً response_type اشتباه یا پارامتر اجباری ارسال نشود پاسخ HTTP 400 + پیام خطا:
پارامتر نامعتبر است: [نام پارامتر]
درخواست ناقص یا با مقادیر غلط پارامتر scope خالی یا اشتباه باشد، یا مقدار غیرمجاز پیام خطا:
پارامتر نامعتبر است: scope یا error=invalid_scope
کاربر لاگین نمی‌کند/رد می‌کند کاربر ورود را لغو کند یا دسترسی ندهد بازگشت با پارامتر error=access_denied به redirect_uri

نمونه پیام خطا در URL:

http://YOUR_REDIRECT_URI/?error=invalid_request&error_description=پارامتر+نامعتبر+است%3A+redirect_uri
  
نمونه خطای رایج دریافت کد در SSO کلاینت یافت نشد client id error

ب) خطاهای مرحله تبادل کد با توکن

خطا توضیح نمونه پاسخ
کد یک‌بارمصرف منقضی از همان code دوبار استفاده شود یا مدت‌زمان اعتبار تمام شده باشد HTTP Status 400
{
  "error": "invalid_grant",
  "error_description": "Code not valid"
}
          
client_secret اشتباه مقدار client_secret نادرست HTTP Status 400
{
  "error": "invalid_client",
  "error_description": "Invalid client credentials"
}
          
کد نامعتبر کد اشتباه یا دستکاری شده HTTP Status 400
{
  "error": "invalid_grant",
  "error_description": "Code not valid"
}
          
redirect_uri مغایر مقدار اعلامی با ثبت‌شده تفاوت داشته باشد HTTP Status 400
{
  "error": "invalid_grant",
  "error_description": "Incorrect redirect_uri"
}
          

10- تمدید نشست با refresh_token

Refresh Token (توکن به‌روزرسانی) به کلاینت این امکان را می‌دهد که بدون دخالت کاربر، نشست ورود را حفظ کرده و access tokenهای جدید دریافت کند. این برای زمانی کاربرد دارد که access token منقضی شده ولی کاربر هنوز در حال تعامل با سیستم است و نباید مجبور به ورود مجدد شود.

پس از ورود اولیه و دریافت refresh token، کلاینت می‌تواند هر زمان قبل یا بعد از انقضای access token، یک درخواست Refresh به SSO پلاس ارسال کند. این درخواست نیز به Token Endpoint فرستاده می‌شود ولی با پارامترهای متفاوت:

  • grant_type: برابر با refresh_token قرار می‌گیرد تا نشان دهد قصد استفاده از refresh token را داریم.
  • refresh_token: همان رشته Refresh Token که در پاسخ قبلی دریافت شد.
  • client_id و client_secret: مطابق قبل برای احراز هویت کلاینت (در صورت Confidential بودن) ارسال می‌گردد.

در ادامه، یک نمونه درخواست و پاسخ برای به‌روزرسانی توکن آمده است:

curl --request POST 
     --url "https://<SSO-BASE-URL>/auth/realms/sso/protocol/openid-connect/token " 
     --header "Content-Type: application/x-www-form-urlencoded" 
     --data "grant_type=refresh_token"  
     --data "client_id=YOUR_CLIENT_ID"  
     --data "client_secret=YOUR_CLIENT_SECRET"  
     --data "refresh_token=USER_REFRESH_TOKEN"
  

نمونه پاسخ موفق:

{
  "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9... جدید ...",
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9... جدید ...",
  "refresh_token": "f197de5a-...-7207df0341b6",
  "token_type": "Bearer",
  "expires_in": 300,
  "refresh_expires_in": 1800
}
  
  • بدون چرخش (Reuse same Refresh Token): در این حالت refresh token اولیه قابل استفاده مجدد است تا زمان انقضا. در نتیجه، پاسخ ممکن است همان refresh token قبلی را برگرداند و یا اصلاً این فیلد را نیاورد. (در مثال بالا یک مقدار refresh_token جدید داده شده که سناریوی بعدی را نشان می‌دهد)
  • با چرخش (Refresh Token Rotation): در این حالت به دلایل امنیتی SSO پلاس با هر بار استفاده از refresh token، یک refresh token تازه صادر می‌کند و قبلی را باطل می‌نماید. اگر SSO پلاس شما اینگونه تنظیم شده باشد، حتماً refresh_token جدید (در پاسخ) را ذخیره کنید و قبلی را کنار بگذارید. در نسخه‌های جدیدمان چنین مکانیزمی را پیشنهاد می‌کنیم.

کلاینت در هر صورت باید همواره آخرین refresh token معتبر را نگهداری کند و برای دفعات بعد استفاده نماید.

زمان انقضای Refresh Token: این توکن‌ها معمولاً عمر طولانی‌تری نسبت به access token دارند اما دائمی نیستند. مدت اعتبار آن را راهبر می‌تواند تنظیم کند (از چند دقیقه تا چند روز یا حتی ماه، بسته به سیاست امنیتی). مقدار refresh_expires_in اگر از سوی SSO پلاس ارائه شود (در اولین پاسخ token یا از طریق تنظیمات) نشان می‌دهد که refresh token چه زمانی منقضی می‌شود. اگر Refresh Token منقضی شود، دیگر امکان تمدید نشست نخواهد بود و کاربر باید دوباره از اول وارد شود.

امنیت Refresh Token: از آنجا که possession این توکن معادل امکان دسترسی بلندمدت بدون ورود کاربر است، نگهداری امن آن بسیار حیاتی است. این توکن نباید در مرورگر یا سمت کاربر ذخیره شود (برای SPAها معمولاً refresh token را به طور امن در حافظه نگه نمی‌دارند و از silent re-authn یا راهکارهای دیگری استفاده می‌کنند). برای برنامه‌های سرور، refresh token را در پایگاه‌داده یا مکانی امن که فقط سرور به آن دسترسی دارد، ذخیره کنید. همچنین توصیه می‌شود هنگام logout کاربر (محلی یا سراسری) این refresh token را از سیستم خود پاک کرده و یا بی‌اعتبار کنید (به بخش revoke مراجعه کنید).

خطاهای محتمل در Refresh:

  • اگر refresh token ارائه‌شده منقضی یا قبلاً ابطال شده باشد، SSO پلاس خطایی با error: “invalid_grant” برمی‌گرداند (معمولاً شرحی نظیر “Refresh token expired” یا “Session not active” همراه آن است). در این حالت، کلاینت باید کاربر را مجبور به ورود مجدد کند چون نشست قابل تمدید نیست.
  • اگر کلاینت سعی کند از یک refresh token استفاده کند که قبلاً با یک refresh token جدید جایگزین شده (در حالت Rotation)، ممکن است خطای invalid_grant دریافت کند چرا که توکن قبلی دیگر معتبر نیست.
  • هر نوع خطای invalid_client یا invalid_request نیز ممکن است ظاهر شود (نظیر بخش قبل) در صورت اشتباه بودن پارامترها یا credentialها.
  • اگر SSO پلاس سیاستی داشته باشد که اجازه ندهد برخی کلاینت‌ها از refresh استفاده کنند خطای unauthorized_client خواهد داد.

با مکانیزم refresh token، سامانه شما می‌تواند کاربر را برای مدت طولانی بدون نیاز به ورودِ دوباره، لاگین نگه دارد و تجربه کاربری بهتری فراهم کند. البته تصمیم در مورد مدت اعتبار نشست به ملاحظات امنیتی سازمان بستگی دارد.

11- توضیح جامع Authorization Code Flow

Authorization Code Flow (جریان کد مجوز) رایج‌ترین و امن‌ترین جریان OAuth/OIDC برای برنامه‌های وب و سرور است. در این روش، احراز هویت و اعطای مجوز در دو گام انجام می‌شود: ابتدا کاربر به SSO هدایت شده (از طریق مرورگر) و یک Authorization Code (کد مجوز موقت) دریافت می‌شود؛ سپس سرویس سمت سرور این کد را به توکن‌ها تبدیل می‌کند. در ادامه جزئیات این جریان و پارامترهای لازم شرح داده شده است.

11-1- هدایت کاربر به Authorization Endpoint

در گام نخست، برنامه‌ی کلاینت (سرویس‌گیرنده) کاربر را جهت ورود به سامانه‌ی SSO پلاس و دریافت کد مجوز به آدرس Authorization Endpoint  SSOپلاس هدایت می‌کند. این عمل معمولاً با یک تغییر مسیر (redirect) به یک URL خاص انجام می‌شود. درخواست احراز هویت که به SSO پلاس ارسال می‌شود باید شامل پارامترهای زیر باشد:

  • client_id: شناسه کلاینت که در مرحله ثبت به شما تخصیص داده شده است. (مثال: client_id=mycompany-portal)
  • redirect_uri: یکی از URLهای بازگشتی مجاز که برای این کلاینت ثبت شده است. پس از تکمیل احراز هویت، SSO پلاس کاربر را به این آدرس هدایت خواهد کرد. (مثال: redirect_uri=https://portal.mycompany.ir/auth/callback)
  • response_type: تعیین‌کننده نوع پاسخ مورد انتظار از SSO پلاس. در جریان Authorization Code مقدار این پارامتر همواره باید code باشد (یعنی درخواست یک کد مجوز). (مثال: response_type=code)
  • scope: محدوده‌های درخواست‌شده توسط کلاینت. این پارامتر یک رشته است که چندین مقدار را با فاصله جدا می‌کند و باید به صورت URL-Encode شده ارسال شود. برای تمام درخواست‌های OpenID Connect حتماً باید مقدار openid در scope وجود داشته باشد. این مقدار، صدور یک ID Token و دسترسی به پروفایل کاربر را امکان‌پذیر می‌کند. علاوه بر آن می‌توانید scopeهای دیگری را بر اساس نیاز اضافه کنید:
    • برای دریافت اطلاعات پایه کاربر (نام، نام خانوادگی، ایمیل و غیره) از UserInfo Endpoint می‌توانید scope استاندارد profile (و در صورت نیاز email) را اضافه کنید تا SSO پلاس مجوز ارائه این اطلاعات را بدهد.
    • اگر SSO پلاس برای مدیریت سطوح دسترسی یا نقش‌های کاربر استفاده می‌شود، ممکن است scopeهای دیگری تعریف شده باشد (مثلاً scopeهای سفارشی سازمان شما) که می‌توانید آن‌ها را درخواست کنید. این مورد بستگی به پیکربندی راهبر دارد.
    • در صورت عدم ارسال هیچ scope اضافه (جز openid)، ممکن است SSO پلاس به طور پیش‌فرض همهٔ دسترسی‌های پایه کاربر را در توکن بگنجاند، اما توصیه می‌شود صراحتاً scopeهای مورد نیاز را درخواست کنید.
(مثال: scope=openid profile email)
  • state (اختیاری): رشته‌ای تصادفی که توسط کلاینت تولید می‌شود و در ادامه عیناً توسط SSO پلاس بازگردانده خواهد شد. از پارامتر state برای ارتباط درخواست جاری با پاسخ دریافتی و جلوگیری از حملات CSRF استفاده می‌شود. کلاینت باید این مقدار را پیش از ارسال نگهداری کرده و پس از بازگشت کاربر از SSO، تطابق آن را بررسی کند. (مثال: state=XyZ123 )
  • response_mode (اختیاری): نحوه‌ی دریافت پاسخ از Authorization Endpoint را مشخص می‌کند. مقدار پیش‌فرض (در صورت عدم ارسال) معمولاً query است که به معنی الصاق پارامترها به صورت query string به آدرس بازگشت است.

اگر شما فقط این درخواست را ارسال کنید:

GET /auth/realms/sso/protocol/openid-connect/auth?client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&response_type=code&scope=openid
  
  • رفتار: در حالت پیش‌فرض، اگر response_mode ارسال نشود و response_type=code باشد، معمولاً query به کار می‌رود.
  • پاسخ: پس از احراز هویت موفق، کاربر به redirect_uri با پارامترهای مورد نظر در query string هدایت می‌شود:
REDIRECT TO:
https://YOUR_REDIRECT_URI?code=xyz123&session_state=abcdef
  
  • مقادیر قابل قبول دیگر response_mode عبارت‌اند از:
    • fragment (پاسخ به صورت fragment URI در آدرس بازگشت ارسال می‌شود – مناسب برای کلاینت‌های تک‌صفحه‌ای SPI). در این حالت به جای ? از # در URL بازگشت استفاده خواهد شد.
    • form_post (پاسخ به صورت فرم HTTP POST به آدرس بازگشت فرستاده می‌شود – مناسب برای افزایش امنیت و جلوگیری از ثبت در history مرورگر).
 پاسخ به صورت یک فرم HTML با متد POST به سمت redirect_uri ارسال می‌شود و پارامترها در body فرم قرار می‌گیرند.

نمونه فرم HTML با روش form_post:

<html>
  <body onload="document.forms[0].submit()">
    <form method="post" action="https://YOUR_REDIRECT_URI">
      <input type="hidden" name="code" value="xyz123"/>
      <input type="hidden" name="session_state" value="abcdef"/>
    </form>
  </body>
</html>
  
  • nonce (اختیاری ولی توصیه‌شده در OIDC): یک مقدار تک‌مصرف تصادفی دیگر که توسط کلاینت تولید می‌شود تا در ID Token بازگردانده شود. این مقدار جهت جلوگیری از حملات تکرار پاسخ (Replay) به خصوص در سناریوهای Implicit/Hybrid Flow استفاده می‌شود. در جریان کد استاندارد، استفاده از nonce الزامی نیست، اما اگر کلاینت شما ID Token را مستقیماً در frontend استفاده می‌کند، می‌توانید nonce را نیز ارسال کرده و پس از دریافت ID Token صحت آن را بررسی کنید.
  • code_challenge و code_challenge_method: این دو پارامتر مربوط به مکانیزم PKCE هستند که در ادامه توضیح داده می‌شود. اگر کلاینت شما Public باشد (یا حتی Confidential برای افزایش امنیت)، باید از PKCE استفاده کند:
    • code_challenge: چالشی است که از روی یک مقدار تصادفی تولید می‌شود.
    • code_challenge_method: روش تولید چالش را مشخص می‌کند (SSO پلاس از S256 یعنی SHA-256 پشتیبانی می‌کند که باید انتخاب شود)
  • grant_type در OAuth 2.0 و OpenID Connect تعیین می‌کند که کلاینت (مثلاً سامانه یا اپلیکیشن شما) با چه روشی و بر اساس چه مجوزی می‌خواهد توکن دریافت کند.
grant_type کاربرد نیاز به کاربر نیاز به secret مناسب برای
authorization_code وب، موبایل (امنیت بالا) بله بستگی به نوع همه
client_credentials سرور به سرور خیر بله بک‌اند/ماشین
password قدیمی، خاص بله معمولاً بله (پیشنهاد نمی‌شود)
refresh_token تمدید توکن بله (غیرمستقیم) بستگی به نوع همه
implicit کلاینت مرورگر (SPA، قدیمی) بله خیر (پیشنهاد نمی‌شود)

11-2- فرایند تبدیل code به token و ساختار پاسخ

در این مرحله، سرویس کلاینت شما که اکنون یک Authorization Code دارد، باید آن را به SSO پلاس ارسال کند تا توکن‌های مربوطه را دریافت نماید. این تبادل از طریق فراخوانی Token Endpoint به صورت مستقیم (Server to Server) انجام می‌شود. به علت حساسیت این مرحله، ارتباط باید حتماً از طریق یک کانال امن (HTTPS) برقرار شود.

برای دریافت توکن، باید یک درخواست HTTP POST با بدنه‌ی x-www-form-urlencoded به Token Endpoint SSO پلاس ارسال شود. پارامترهای اصلی این درخواست عبارت‌اند از:

  • grant_type: نوع مجوز درخواستی که برای مبادله‌ی کد باید مقدار آن را authorization_code قرار دهید.
  • code: همان Authorization Code که در مرحله قبل دریافت کردید.
  • redirect_uri: همان نشانی بازگشتی که در مرحله قبل استفاده شد. طبق استاندارد OAuth2، درج همان redirect_uri در این مرحله الزامی است تا مطابقت درخواست با کد انجام شود. (SSO پلاس کد را فقط در صورتی قبول می‌کند که redirect_uri این درخواست با مقدار ثبت‌شده روی کد مطابقت داشته باشد.)
  • client_id: شناسه کلاینت شما.
  • client_secret: (الزامی برای کلاینت‌های Confidential) راز کلاینت که در هنگام ثبت کلاینت دریافت کرده‌اید. این مقدار باید جهت احراز هویت کلاینت در این درخواست ارسال شود. (نحوه ارسال: می‌توانید client_id و secret را به صورت Basic Auth در هدر Authorization بفرستید یا در بدنه به عنوان فیلدهای جداگانه قرار دهید. در مثال ساده ما در بدنه می‌آوریم.)
  • code_verifier: (الزامی برای کلاینت‌های Public که از PKCE استفاده کرده‌اند) رشتهٔ اولیه تصادفی که در مرحله قبل برای تولید code_challenge به‌کار رفت. SSO پلاس از این مقدار برای اعتبارسنجی PKCE استفاده می‌کند. اگر کلاینت شما Public باشد و code_verifier را ارسال نکند یا مقدار آن اشتباه باشد، مبادله کد با خطا مواجه خواهد شد.

یک نمونه درخواست تبادل کد با توکن به صورت زیر است:

نمونه درخواست دریافت توکن با PKCE:

curl --request POST 
     --url "https://<SSO-URL>/token" 
     --header "Content-Type: application/x-www-form-urlencoded" 
     --data "grant_type=authorization_code" 
     --data "code=SplxlOBeZQQYbYS6WxSbIA" 
     --data "redirect_uri=https://portal.mycompany.ir/auth/callback" 
     --data "client_id=my-app" 
     --data "client_secret=ABC123SECRETXYZ" 
     --data "code_verifier=dXJhbGxhZGlhbjEyMzQ1Njc4OTAhQCMkJV4="
  

در درخواست بالا، Authorization Code دریافتی (SplxlOBeZQQYbYS6WxSbIA در نمونه) همراه با سایر پارامترها ارسال شده است. SSO پلاس پس از دریافت این درخواست و اعتبارسنجی تمام اجزا (درست بودن کد، منقضی نشدن آن، تطبیق داشتن با client_id و redirect_uri مربوطه، اعتبار کلاینت و در صورت وجود درستی code_verifier)، پاسخ را به صورت یک شیء JSON شامل توکن‌ها برمی‌گرداند.

ساختار پاسخ Token Endpoint:
پاسخ موفق یک نمونه شبیه زیر خواهد داشت:

نمونه پاسخ موفق:

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI... (بسیار طولانی)...",
  "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1... (بسیار طولانی)...",
  "refresh_token": "ODAzNTQ3YmQtYzIxZS00Y2Q5LTk... (رشته طولانی)...",
  "token_type": "Bearer",
  "expires_in": 300,
  "refresh_expires_in": 1800,
  "scope": "openid profile"
}
  

اجزای مهم این پاسخ عبارت‌اند از:

  • access_token (توکن دسترسی): یک رشته JWT که نشان‌دهنده مجوز دسترسی کلاینت به منابع محافظت‌شده است. کلاینت برای فراخوانی APIهای مورد نظر باید این توکن را به همراه درخواست‌ها (معمولاً در هدر Authorization به صورت Bearer) ارسال کند. اعتبار این توکن محدود است (در مثال بالا expires_in برابر 300 ثانیه یا 5 دقیقه است). پس از این زمان، access_token منقضی می‌شود و باید از refresh_token برای دریافت توکن جدید استفاده کرد. محتوای access token می‌تواند شامل اطلاعاتی در مورد کاربر و سطح دسترسی باشد، اما کلاینت نباید بر اساس تفسیر این محتوا، عمل حساس انجام دهد (بلکه صرفاً برای ارائه به Resource Server است).
  • id_token (توکن هویت): یک JWT حاوی اطلاعات احراز هویت کاربر است که توسط SSO پلاس امضا شده است. کلاینت می‌تواند از ID Token برای شناسایی هویت کاربر استفاده کند (مثلاً استخراج sub که شناسه یکتای کاربر در SSO پلاس است، name، email و سایر claims مربوط به پروفایل کاربر). توجه: ID Token در سمت کلاینت کاربرد احراز هویتی دارد و نباید برای دسترسی به منابع به کار رود. همچنین ID Token ممکن است زمان اعتبار کوتاه‌تری (هم‌تراز با access token) داشته باشد.
  • refresh_token (توکن به‌روزرسانی): یک توکن بلندمدت که به کلاینت امکان می‌دهد بدون نیاز به ورود مجدد کاربر، توکن دسترسی جدید دریافت کند. Refresh Token معمولاً به صورت یک رشته طولانی غیر JWT (یا در بعضی پیاده‌سازی‌ها JWT خاص) صادر می‌شود و زمان اعتبار طولانی‌تری دارد (در مثال بالا refresh_expires_in برابر 1800 ثانیه یا 30 دقیقه است). این توکن، باید محرمانه نزد کلاینت نگهداری شود و هرگز در سمت کاربر (مرورگر) یا به سایر سرویس‌ها افشا نگردد. مکانیزم استفاده از آن در بخش‌های بعدی توضیح داده خواهد شد.
  • token_type: نوع توکن صادرشده برای دسترسی، که معمولاً Bearer است. این به معنای آن است که دارنده token می‌تواند به منابع دسترسی داشته باشد و Resource Server صرفاً وجود توکن را در درخواست بررسی می‌کند.
  • expires_in: مدت زمان اعتبار access token به ثانیه.
  • refresh_expires_in: مدت زمان اعتبار refresh token به ثانیه (اگر ارائه شود).
  • scope: محدوده‌هایی که این توکن‌ها برایشان اعتبار دارند. معمولاً همان scopeهای درخواست‌شده یا محدودتر را نشان می‌دهد.

کلاینت پس از دریافت این پاسخ باید توکن‌ها را ذخیره و مدیریت کند. معمولاً access_token و id_token در حافظه کوتاه‌مدت (و در صورت نیاز ID Token رمزگشایی و بررسی می‌شود) نگه‌داری می‌شوند و refresh_token در مکانی امن (مثلاً دیتابیس یا حافظه امن) ذخیره می‌شود تا برای تمدید نشست استفاده گردد.

12- بکارگیری State و Nonce برای امنیت بیشتر

کلاینت باید هنگام ارسال درخواست احراز هویت، دو مقدار تصادفی و یکتا برای پارامترهای state و nonce تولید کند و در درخواست قرار دهد.

نمونه درخواست:

GET https://<SSO_URL>/auth/realms/sso/protocol/openid-connect/auth?
    client_id=YOUR_CLIENT_ID
    &redirect_uri=YOUR_REDIRECT_URI
    &response_type=code
    &scope=openid
    &state=abcd1234
    &nonce=efgh5678
  
  • توضیح پارامترها:

    • state: مقدار تصادفی تولید شده توسط کلاینت (برای جلوگیری از CSRF)
    • nonce: مقدار تصادفی تولید شده توسط کلاینت (برای جلوگیری از حملات بازپخش روی توکن id_token)

    12-1- هدایت کاربر و دریافت code

    پس از ورود موفق کاربر، سامانه SSO پلاس کاربر را به آدرس بازگشت (redirect_uri) هدایت می‌کند. پارامترهای code و state به URL افزوده می‌شوند:

نمونه خروجی (query string):

https://YOUR_REDIRECT_URI/?code=xyz9999&state=abcd1234
  

12-2- تبادل code با توکن‌ها (Token Request)

کلاینت با ارسال code به endpoint توکن، توکن‌های access_token و id_token را دریافت می‌کند:

درخواست:

POST https://SSO_URL/auth/realms/sso/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded

client_id=YOUR_CLIENT_ID
&grant_type=authorization_code
&code=xyz9999
&redirect_uri=YOUR_REDIRECT_URI
  

12-3- دریافت id_token و بررسی nonce

نمونه خروجی موفق:

{ "access_token": "....",
  "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJsub...w5cCI6IkpXVCJ9.eyJsub..." }
  

داخل توکن id_token (JWT)، claim زیر وجود دارد:

{ "iss": "https://SSO_URL/auth/realms/sso",
  "aud": "YOUR_CLIENT_ID",
  "nonce": "efgh5678",
  ... }
  

بررسی مهم: کلاینت باید مقدار nonce داخل id_token را با مقدار اولیه ارسال شده (efgh5678) مقایسه کند تا مطمئن شود پاسخ مربوط به همان نشست (session) است و حمله‌ای رخ نداده است.

12-4- جدول خلاصه فرآیند

گام کلاینت ارسال می‌کند سرور پاسخ می‌دهد بررسی امنیتی
1 state, nonce
2 code, state state باید یکسان باشد
3 code access_token, id_token
4 id_token (حاوی nonce) nonce باید یکسان باشد
  • 12-5- جمع‌بندی state و nonce

    • مقدار state تضمین می‌کند که درخواست احراز هویت توسط کلاینت ارسال شده و کسی وسط کار پارامترها را تغییر نداده است (ضد CSRF).
    • مقدار nonce تضمین می‌کند که توکن id_token واقعاً برای همین نشست و درخواست ساخته شده و قابل بازپخش نیست (ضد replay attack).

13– استفاده از PKCE برای امنیت بیشتر و اجباری برای کلاینت های Public

ثبات کلید برای تبادل کد یا PKCE (Proof Key for Code Exchange) یک لایه امنیتی اضافه بر روی Authorization Code Flow است که عمدتاً برای کلاینت‌های Public (مانند اپ‌های موبایل و جاوااسکریپتی که Secret ندارند) طراحی شده است. PKCE جلوی حملات سرقت کد (Code Interception) را می‌گیرد. نحوهٔ کار به این صورت است که کلاینت قبل از هدایت کاربر به SSO یک رشتهٔ تصادفی به نام code_verifier تولید می‌کند. سپس code_challenge را با اعمال تابع درهم‌ساز SHA-256 و انکود Base64URL از روی آن تولید می‌کند و این مقدار خلاصه‌شده (چالش) را به همراه درخواست احراز هویت می‌فرستد. مثال مراحل PKCE:

13-1- تولید مقادیر PKCE سمت کلاینت

تولید code_verifier

یک رشته تصادفی (حداقل 43 کاراکتر، حاوی حروف، عدد، -._~):

code_verifier = "nI89FzvN6rQ4lAqTDKy2kU4jM-VkTcPD6hyf_XZGAXL~sh4Fcmvif1FStL56Qv4c"
  

تولید code_challenge

ابتدا code_verifier را با SHA-256 هش کرده و سپس نتیجه را Base64URL encode کنید:

SHA256(code_verifier) → [binary]
Base64URL(SHA256(code_verifier)) → code_challenge
  

مثال خروجی:

code_challenge = "Up_UV16_EjTUr6IBgXiPnhBmsJBlbMsyfJbKHmiy8hI"
  

13-2- ارسال درخواست احراز هویت (Authorization Request)

درخواست باید شامل پارامترهای زیر باشد:

پارامتر مقدار (مثال) توضیح
response_type code دریافت Authorization Code
client_id my-app شناسه کلاینت شما
redirect_uri https://app.com/callback آدرس بازگشت
scope openid profile email محدوده دسترسی
state random-state-123 رشته تصادفی جهت امنیت
code_challenge Up_UV16_EjTUr6IBgXiPnhBmsJBlbMsyfJbKHmiy8hI خلاصه‌شده code_verifier
code_challenge_method S256 روش مورد استفاده (S256)

نمونه درخواست کامل:

GET https://SSO_URL/auth/realms/sso/protocol/openid-connect/auth
    ?response_type=code
    &client_id=my-app
    &redirect_uri=https%3A%2F%2Fapp.com%2Fcallback
    &scope=openid%20profile%20email
    &state=random-state-123
    &code_challenge=Up_UV16_EjTUr6IBgXiPnhBmsJBlbMsyfJbKHmiy8hI
    &code_challenge_method=S256
  

13-3- بازگشت کاربر به کلاینت (Redirect)

پس از ورود موفق کاربر و تایید دسترسی‌ها، SSO پلاس کاربر را به آدرس redirect_uri با code و state بازمی‌گرداند:

نمونه خروجی بازگشت:

https://app.com/callback?code=abcXYZ9876&state=random-state-123
  
  • code: کد یک‌بار مصرف برای تبادل توکن
  • state: همان مقدار ارسالی برای تایید صحت جریان

13-4- درخواست توکن (Token Request)

کلاینت حالا باید درخواست تبادل code با توکن را ارسال کند. این درخواست باید code_verifier اصلی را ارسال کند تا SSO پلاس بتواند آن را اعتبارسنجی کند.

نمونه درخواست POST:

POST https://SSO_URL/auth/realms/sso/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded

client_id=my-app
&grant_type=authorization_code
&code=abcXYZ9876
&redirect_uri=https%3A%2F%2Fapp.com%2Fcallback
&code_verifier=nI89FzvN6rQ4lAqTDKy2kU4jM-VkTcPD6hyf_XZGAXL~sh4Fcmvif1FStL56Qv4c
  

13-5- دریافت توکن‌ها و اعتبارسنجی

در صورت صحیح بودن code_verifier (مطابقت با code_challenge)، پاسخ شامل access_token و id_token خواهد بود:

نمونه خروجی موفق:

{  
  "access_token": "eyJhbGciOi...",
  "id_token": "eyJhbGciOi...",
  "token_type": "bearer",
  "expires_in": 300,
  "refresh_token": "...."
}
  

اگر code_verifier اشتباه یا نامعتبر باشد، خطایی مشابه دریافت خواهید کرد:

{  
  "error": "invalid_grant",
  "error_description": "PKCE verification failed, code_verifier does not match code_challenge"
}
  

13-6- جدول خلاصه مراحل PKCE

مرحله کلاینت ارسال می‌کند سرور چه بررسی انجام می‌دهد نکته امنیتی
۱. احراز هویت code_challenge, code_challenge_method code_challenge را ذخیره می‌کند جلوگیری از سرقت کد
۲. دریافت code state state را بازمی‌گرداند ضد CSRF
۳. تبادل توکن code_verifier با code_challenge مقایسه می‌کند فقط صاحب اصلی می‌تواند توکن بگیرد
۴. دریافت توکن در صورت صحت همه چیز توکن را برمی‌گرداند

13-7- نکات کلیدی PKCE

  • همیشه از code_challenge_method=S256 استفاده کنید.
  • مقادیر code_verifier را هرگز فاش یا لاگ نکنید.
  • برای کلاینت‌های Public، ارسال PKCE اجباری است.

اگر کلاینت PKCE را ارسال نکند، SSO پلاس خطای زیر را برمی‌گرداند:

{
  "error": "invalid_request",
  "error_description": "PKCE parameters required for public client"
}
  

14- اعتبارسنجی Token با کلید عمومی SSO پلاس (JWKS)

توکن‌های JWT صادرشده توسط SSO پلاس (از جمله ID Token و معمولاً Access Token) توسط کلید خصوصی SSO پلاس امضا شده‌اند. برای اطمینان از صحت و اعتبار یک JWT، کلاینت یا سرویس دریافت‌کننده توکن باید امضای آن را با استفاده از کلید عمومی متناظر بررسی کند. SSO پلاس برای این منظور یک Endpoint حاوی کلید عمومی فراهم می‌کند که غالباً به عنوان JWKS (JSON Web Key Set) Endpoint شناخته می‌شود.

JWKS Endpoint یک آدرس URL در SSO پلاس است که لیستی از کلیدهای عمومی معتبر فعلی را در قالب JSON ارائه می‌دهد. هر کلید شامل جزئیاتی مثل alg (الگوریتم، مثلاً RSA), kid (شناسه‌ی کلید) و مقادیر کلید عمومی (مانند n و e برای RSA) است. کلیدها ممکن است دوره‌ای چرخش (rotate) شوند، لذا بهتر است این لیست را به صورت دوره‌ای یا در مواجهه با یک شناسه کلید جدید بازیابی کنید.

برای اعتبارسنجی یک JWT (مثلاً ID Token):

  • رشته JWT را به اجزای خود (header, payload, signature) تفکیک کنید. در قسمت header توکن، یک فیلد به نام kid وجود دارد که شناسه کلید امضاکننده این توکن است.
  • کلاینت به پایانه JWKS از SSOپلاس درخواست GET می‌زند تا لیست کلیدهای عمومی را دریافت کند (این آدرس معمولاً در well-known configuration موجود است یا می توانید از پایانه های جدول ۲ استفاده کنید)
  • در پاسخ JWKS، به دنبال شیئی بگردید که “kid” آن برابر با kid موجود در JWT شما باشد. سپس مقادیر کلید (مانند n و e برای RSA) را استخراج کنید.
  • با استفاده از کتابخانه‌های استاندارد JWT، امضای JWT را با کلید عمومی به‌دست‌آمده بررسی کنید. اگر امضا تأیید شد، یعنی توکن توسط SSO پلاس صادر شده و دستکاری نشده است.
  • پس از تأیید امضا، باید سایر اعتبارها مانند exp و aud و … (که در بخش مربوطه توضیح داده شده) بررسی شوند تا از معتبر بودن محتوا مطمئن شوید.

توجه: اگر JWKS endpoint چندین کلید را برگرداند، بدان معناست که SSO پلاس ممکن است چند کلید فعال برای امضا داشته باشد (مثلاً در دوره‌ی تعویض کلید). بنابراین همیشه از kid برای انتخاب کلید صحیح استفاده کنید. همچنین بهتر است پاسخ JWKS را برای مدت معقولی (مثلاً چند ساعت) کش کنید و در صورت برخورد با توکنی با kid ناشناخته، مجدداً JWKS را بازیابی نمایید.

نمونه‌ای از پاسخ JWKS (برای نمایش ساده‌شده)

پاسخ به شکل زیر خواهد بود:

{
  "keys": [
    {
      "kty": "RSA",
      "alg": "RS2048",
      "kid": "abcdef123456",
      "use": "sig",
      "n": "AL6c7H......(متن طولانی)...ssz8",
      "e": "AQAB"
    },
    {
      "kty": "RSA",
      "alg": "RS2048",
      "kid": "ghijkl789012",
      "use": "sig",
      "n": "AMZKl... (کلید دیگر) ...n0w",
      "e": "AQAB"
    }
  ]
}
  

در مثال فوق، دو کلید عمومی ارائه شده که از روی kid قابل انتخاب هستند. پارامترهای n و e همان اجزای کلید RSA هستند که برای ساختن کلید عمومی در کد استفاده می‌شوند. کتابخانه‌های JWT معمولاً می‌توانند از این قالب JWKS مستقیماً استفاده کرده و فرآیند تطبیق و بررسی امضا را انجام دهند.

چرا اعتبارسنجی توکن مهم است؟ انجام این کار در سمت کلاینت/سامانه تضمین می‌کند که:

  • توکن واقعاً توسط SSO پلاس صادر شده و یک کلاینت مخرب یا مهاجم آن را جعل نکرده است.
  • محتوای توکن در حین انتقال تغییر داده نشده است (امضای معتبر دلیلی بر عدم تغییر است).
  • کلاینت می‌تواند با اتکا به اطلاعات داخل توکن (پس از تأیید)، تصمیمات امنیتی بگیرد (مثلاً شناسایی هویت کاربر از روی sub).

در صورتی که کلاینت امکان یا تمایل به اعتبارسنجی محلی توکن را نداشته باشد، روش جایگزین اعتبارسنجی از SSO برای وضعیت توکن است که از طریق Introspection انجام می‌شود.

15- اعتبارسنجی Token با پایانه Introspection

Token Introspection یک قابلیت تعریف‌شده در استاندارد OAuth2  (RFC 7662) است که به کلاینت‌ها یا سرویس‌های Confidential اجازه می‌دهد وضعیت و جزئیات یک توکن را با اعتبارسنجی از سرور مجوز (در اینجا یعنی SSO پلاس) به‌دست آورند. به عبارت دیگر، اگر سرویس شما یک توکن (Access Token یا Refresh Token) در اختیار دارد و می‌خواهد بداند آیا این توکن هنوز فعال (active) است، چه زمانی منقضی می‌شود و مربوط به کدام کاربر/کلاینت است، می‌تواند از Introspection Endpoint استفاده کند.

موارد استفاده معمول از introspection:

  • وقتی یک Resource Server (مثلاً API) توکن دریافتی از کلاینت را نمی‌تواند خودش اعتبارسنجی کند (مثلاً توکن به جای JWT، یک توکن مرجع یا opaque است)، با introspect کردن آن می‌تواند بفهمد معتبر است یا نه.
  • حتی اگر توکن JWT باشد، برای اطمینان از لغو نشدن یا معتبر بودن می‌توان introspection را به عنوان راه مطمئن استفاده کرد (JWT صرفاً با امضا اعتبارسنجی می‌شود و لغو شدن زودتر از exp را تشخیص نمی‌دهد، مگر با مکانیزم لیست سیاه).
  • بررسی یک Refresh Token قبل از استفاده یا حین logout جهت اطمینان از وضعیت آن.

این Endpoint نیاز به احراز هویت کلاینت دارد، یعنی تنها کلاینت‌های Confidential یا سرویس‌های مورد اعتماد می‌توانند درخواست introspect بفرستند. (Public clientها عموماً به این endpoint دسترسی ندارند مگر راهبر تنظیم خاصی انجام دهد).

برای استفاده از introspection، یک درخواست POST با Content-Type فرم-url-encode شده به این آدرس ارسال کنید که شامل:

  • token: مقداری از توکن (access یا refresh) که می‌خواهید بررسی شود.
  • token_type_hint (اختیاری): می‌توانید نوع توکن را مشخص کنید (access_token یا refresh_token) تا سرور سریع‌تر آن را پردازش کند. اگر ارسال نکنید، سرور به صورت پیش‌فرض بررسی می‌کند.
  • احراز هویت کلاینت: بهترین روش، ارسال client_id و client_secret به صورت Basic Auth در هدر Authorization است. یا می‌توانید آنها را در بدنه با پارامترهای client_id و client_secret قرار دهید.

نمونه درخواست introspection با curl

روش اول (با Basic Auth):

curl --request POST 
     --url "https://<SSO-URL>/auth/realms/sso/protocol/openid-connect/introspect" 
     --header "Content-Type: application/x-www-form-urlencoded" 
     --user "my-app:ABC123SECRETXYZ" 
     --data "token=eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIxMjM0NSIs...<Access Token>..."
  

در بالا، از -u my-app:ABC123SECRETXYZ برای ارسال client_id و secret استفاده شده است (این روش Basic Auth، header لازم را خودکار می‌سازد).

روش دوم (ارسال client_id و secret در بدنه درخواست):

curl --request POST 
     --url "https://<SSO-URL>/auth/realms/sso/protocol/openid-connect/introspect" 
     --header "Content-Type: application/x-www-form-urlencoded" 
     --data "client_id=my-app" 
     --data "client_secret=ABC123SECRETXYZ" 
     --data "token=eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIxMjM0NSIs...<Access Token>..."
  

پاسخ Introspection:
اگر احراز هویت کلاینت موفق باشد، SSO پلاس یک شیء JSON برمی‌گرداند. مهم‌ترین فیلد آن active است که نشان می‌دهد توکن ارائه‌شده در حال حاضر معتبر و فعال است یا خیر (true/false). اگر active=false باشد، معمولاً هیچ اطلاعات اضافه دیگری نمی‌آید یا فقط active:false می‌آید. اگر active=true باشد، ممکن است چندین claim درباره توکن ارائه شود. یک نمونه پاسخ در حالت active:

نمونه پاسخ introspection

در صورت معتبر بودن توکن:

{
  "active": true,
  "exp": 1694103000,
  "iat": 1694101200,
  "scope": "openid profile email",
  "sub": "248289761001",
  "client_id": "my-app",
  "username": "a.ahmadi",
  "token_type": "Access token",
  "aud": "my-api",
  "iss": "https://sso.ssoplus.ir/auth/realms/sso",
  "jti": "123e4567-e89b-12d3-a456-426614174000"
}
  

اگر توکن نامعتبر باشد (active: false)، پاسخ به صورت زیر خواهد بود:

{ "active": false }
  

یا ممکن است حتی یک پاسخ خالی یا 200 بدون بدنه برگردد. در هر صورت، active=false یعنی توکن قابل قبول نیست.

نکات امنیتی:
Introspection Endpoint چون وضعیت توکن را فاش می‌کند، نیازمند احراز هویت است. فقط کلاینت Confidential، صاحب توکن یا Resource Server مرتبط باید از آن استفاده کند. SSO پلاس بررسی می‌کند که client_id درخواست‌دهنده اجازه introspect کردن آن توکن را دارد و تنها به همان client یا به Resource Serverهایی که توکن مذکور برای آن‌ها صادر شده اجازه introspection می‌دهد. بنابراین همیشه از credentials درست استفاده کنید.

کاربرد عملی: اگر سیستم شما خودش APIهایی دارد که با توکن فراخوانی می‌شوند، در سناریوی ساده می‌توانید به جای introspection، امضای JWT را بررسی و exp را کنترل کنید (چون JWT است). اما اگر می‌خواهید امکان لغو دسترسی فوری داشته باشید (مثلاً ادمین در SSO کاربر را غیرفعال کرده یا logout کرده)، introspection کمک می‌کند حتی قبل از exp باخبر شوید (چون SSO پلاس در صورت لغو، active را false خواهد کرد).

16- نکات مهم امنیت با اعتبارسنجی در سمت سامانه تخصصی

هنگامی که کلاینت، توکن‌ها (به ویژه ID Token) را از SSO پلاس دریافت می‌کند، علاوه بر بررسی امضای دیجیتال توکن (بخش ۵)، لازم است محتوای توکن را نیز اعتبارسنجی کند تا از صحت احراز هویت و مجاز بودن آن برای سامانه اطمینان حاصل شود. مهم‌ترین claimهایی که باید بررسی شوند عبارت‌اند از:

  • issuer (iss): این claim نشان می‌دهد توکن توسط کدام سرویس صادر شده است. مقدار iss باید برابر با شناسه صادرکننده SSO پلاس باشد (معمولاً یک URL مربوط به دامنه SSO پلاس، مثلاً https://sso.ssoplus.ir/auth/realms/sso). مقدار دقیق issuer را می‌توانید از راهبر یا از فایل well-known (فیلد “issuer”) به‌دست آورید. اگر iss توکن با مقدار مورد انتظار همخوانی نداشت، نباید به توکن اعتماد شود؛ زیرا ممکن است از یک منبع غیرمعتبر آمده باشد.
  • audience (aud): این claim مشخص می‌کند که این توکن برای کدام مخاطب صادر شده است. در ID Token، aud معمولاً Client ID کلاینت شماست (یا آرایه‌ای حاوی آن). بنابراین کلاینت شما باید بررسی کند که شناسه خود را در لیست aud بیابد. اگر توکن برای سرویس دیگری صادر شده باشد و aud مطابقت نکند، نباید پذیرفته شود. (در برخی موارد، claim دیگری به نام azp یا authorized party وجود دارد که اگر aud شامل چند مورد باشد، azp برابر client_id مربوط خواهد بود. در هر حال، حضور کلاینت شما در aud یا azp باید احراز شود).
  • expiration (exp) و issued-at  (iat): زمان انقضای توکن (بر حسب timestamp یونیکس) در exp مشخص شده است. کلاینت باید اطمینان حاصل کند که زمان فعلی کمتر از exp است. در غیر اینصورت، توکن منقضی شده و قابل قبول نیست. همچنین معمول است که iat (زمان صدور) یا nbf  (not before) نیز بررسی شود تا مطمئن شویم توکن هنوز فعال است و در بازه درستی استفاده می‌شود. مثلاً اگر nbf وجود داشت، باید بررسی کنیم زمان فعلی بزرگ‌تر از nbf باشد.
  • nonce: اگر در درخواست Authorization پارامتر nonce فرستاده‌اید و در ID Token برگشتی هم nonce موجود است، حتماً تطابق آن را بررسی کنید. مقدار nonce در ID Token باید دقیقاً همان باشد که کلاینت ارسال کرده بود. این کار جلوی حملات replay را می‌گیرد. (اگر nonce ارسال نکردید یا ID Token nonce ندارد، می‌توانید این مرحله را صرفنظر کنید).
  • نشست کاربر (sid) و سایر claims مرتبط: در برخی JWTها claimهایی مانند sid (session ID) وجود دارد که معرف نشست ورود کاربر در SSO پلاس است. اگر کلاینت نشست داخلی خود را با نشست SSO گره می‌زند، می‌تواند این مقدار را برای ردیابی نگه دارد. همچنین claimهایی مثل auth_time (زمان احراز هویت کاربر) ممکن است وجود داشته باشد؛ اگر برنامه‌ای حساس دارید (مثلاً نیاز به اطلاع از زمان آخرین ورود یا اعمال محدودیت زمانی احراز هویت)، می‌توانید از auth_time استفاده کنید.
  • سطح احراز هویت (acr) یا روش‌ها (amr): گاهی OIDC سطح یا نوع احراز هویت انجام‌شده را در این claimها می‌آورد (مثلاً acr ممکن است نشان دهد MFA انجام شده یا خیر). اگر سامانه شما نیازمند سطح خاصی از اطمینان احراز هویت باشد، باید مقدار acr را ارزیابی کند. این موضوع در بیشتر موارد ضروری نیست مگر کاربرد پیشرفته داشته باشید.

علاوه بر موارد استاندارد بالا، اگر SSO پلاس claimهای سفارشی دیگری در توکن قرار می‌دهد که برای امنیت مهم‌اند، آن‌ها را هم اعتبارسنجی کنید. برای مثال، در SSO پلاس فرضی ما claim user_type نشان‌دهنده نوع هویت است؛ اگر برنامه شما فقط نوع خاصی را باید بپذیرد، باید آن را نیز بررسی کند.

جمع‌بندی این بخش: پس از دریافت ID Token، حداقل موارد امضا، iss، aud، exp را همیشه کنترل کنید. این کار تضمین می‌کند که:

  • توکن توسط سرویس صحیحی برای شما صادر شده (iss/aud درست)،
  • همچنان معتبر است (exp)،
  • و توسط شخص ثالثی دستکاری نشده (امضا).

در مورد Access Token نیز اگر برنامه شما مستقیماً نقش Resource Server را ایفا می‌کند (مثلاً خودتان API را قرار است امن کنید)، باید همین موارد را اعمال کنید. یعنی access token را با کلید عمومی SSO پلاس اعتبارسنجی کرده و claims مهم آن مانند issuer, exp و scope یا نقش‌های درون آن را وارسی کنید. اما اگر access token صرفاً برای ارسال به API دیگری استفاده می‌شود، ممکن است اعتبارسنجی آن وظیفه سرویس مقصد باشد. به هر حال، آشنایی با ساختار و اعتبارسنجی JWT برای توسعه‌دهندگان ضروری است.

17- نمونه توکن ها

در ادامه، دو نمونه توکن (JWT) برای کاربران حقیقی و حقوقی ارائه شده است که در سناریوهای Government-to-Citizen (G2C) معمولاً توسط سامانه SSO صادر می‌شوند.
این نمونه‌ها صرفاً برای آشنایی با ساختار توکن و claimsها (ادعاها) آورده شده‌اند و ممکن است بسته به محیط اجرایی و پیاده‌سازی، فیلدها یا ساختار claimsها متفاوت باشد.

توجه:

  • هیچ کدام از پایانه ها یا API های SSOپلاس تفکیک حقیقی یا حقوقی ندارد و این تفاوت در خروجی نتایج هست.
  • ساختار و اسامی claims (ادعاها) ممکن است در هر پیاده‌سازی با توجه به نیازهای سازمانی یا نوع کاربری تغییر کند. بنابراین نباید فرض شود که همیشه این فیلدها یا مقادیر در توکن صادر خواهند شد.
  • این فیلدها صرفاً نمونه هستند و وجود یا عدم وجود برخی claims (یا مقداردهی آن‌ها) به سیاست هر سرویس‌دهنده وابسته است.
  • توسعه‌دهندگان باید توجه داشته باشند که بررسی و استخراج claims از توکن باید با رعایت “ignore case” (حساس نبودن به حروف بزرگ و کوچک) انجام شود.
    به عنوان مثال:
    • ممکن است یک سرور claim را با کلید given_name و دیگری با Given_Name یا حتی GIVEN_NAME ارسال کند.
    • برای جلوگیری از بروز خطا در پردازش claims، توصیه می‌شود همواره کلید claims به صورت غیر حساس به حروف (case-insensitive) پردازش شود.

17-1- یک نمونه توکن JWT برای شخص حقیقی

توکن JWT برای شخص: حقیقی Person

{
  "header": {
    "alg": "RS256",
    "typ": "JWT",
    "kid": "XxAbCdEfGhIjKlMnOpQrStUvWxYz1234567890"
  },
  "payload": {
    "exp": 1750000000,
    "iat": 1749999700,
    "jti": "abc12345-def6-7890-gh12-ijklmnopqrst",
    "iss": "https://sso.ssoplus.ir/auth/realms/sso",
    "aud": "account",
    "sub": "f1f2e3d4-c5b6-789a-bcde-123456789abc",
    "typ": "Bearer",
    "azp": "sso",
    "nonce": "98765432-10ab-cdef-1122-334455667788",
    "session_state": "12345678-9abc-def0-1234-56789abcdef0",
    "acr": "0",
    "allowed-origins": ["*"],
    "scope": "openid email profile microprofile-jwt address phone",
    "upn": "1234567890",
    "user_type": "PERSON",
    "email_verified": false,
    "account_info": {
      "firstName": "امیررضا",
      "lastName": "رضایی",
      "fatherName": "علی",
      "address": {
        "traceID": "2",
        "errorCode": 0,
        "province": "تهران",
        "enProvince": "Tehran",
        "townShip": "تهران",
        "zone": "مرکزی",
        "village": "",
        "localityType": "شهر",
        "localityName": "تهران",
        "localityCode": 12345,
        "subLocality": "یوسف‌آباد",
        "street": "خیابان ولیعصر",
        "street2": "کوچه لاله",
        "houseNumber": 12,
        "floor": "2",
        "sideFloor": "",
        "buildingName": "",
        "description": "",
        "postCode": "1111111111"
      },
      "nationalId": "1234567890",
      "gender": "MALE",
      "shenasnamehNo": "12",
      "postalCode": "1111111111",
      "email": "amirreza.rezaei@example.com"
    },
    "mobile": "09120000001",
    "name": "امیررضا رضایی",
    "groups": ["person"],
    "preferred_username": "1234567890",
    "given_name": "امیررضا",
    "locale": "fa",
    "family_name": "رضایی",
    "birthDate": "1370/01/15"
  }
}
  

17-3- یک نمونه خروجی JSON از پایانه Userinfo

خروجی JSON برای Userinfo یک شخص حقیقی

{
  "sub": "f1f2e3d4-c5b6-789a-bcde-123456789abc",
  "email_verified": false,
  "account_info": {
    "firstName": "امیررضا",
    "lastName": "رضایی",
    "fatherName": "علی",
    "address": {
      "traceID": "2",
      "errorCode": 0,
      "province": "تهران",
      "enProvince": "Tehran",
      "townShip": "تهران",
      "zone": "مرکزی",
      "village": "",
      "localityType": "شهر",
      "localityName": "تهران",
      "localityCode": 12345,
      "subLocality": "یوسف‌آباد",
      "street": "خیابان ولیعصر",
      "street2": "کوچه لاله",
      "houseNumber": 12,
      "floor": "2",
      "sideFloor": "",
      "buildingName": "",
      "description": "",
      "postCode": "1111111111"
    },
    "nationalId": "1234567890",
    "gender": "MALE",
    "shenasnamehNo": "12",
    "postalCode": "1111111111",
    "email": "amirreza.rezaei@example.com"
  },
  "mobile": "09120000001",
  "groups": [
    "person"
  ],
  "preferred_username": "1234567890",
  "given_name": "امیررضا",
  "locale": "fa",
  "birthDate": "1370/01/15",
  "user_type": "PERSON",
  "idp": "oidc",
  "name": "امیررضا رضایی",
  "family_name": "رضایی"
}
  

17-4- خلاصه تفاوت های کلیدی Claimهای کاربران حقیقی و حقوقی

توضیحات جدول زیر مقایسه‌ای از Claimهای رایج در توکن کاربران حقیقی (Person) و حقوقی (Legal) در SSO پلاس ارائه می‌دهد.

توضیحات نام Claim برای توکن حقیقی برای توکن حقوقی
شناسه یکتای کاربر نزد SSO sub ✔️ ✔️
آرایه‌ای از نوع کاربر
حقیقی = person
حقوقی = legal
groups [“person”] [“legal”]
نام و نام خانوادگی name نام و نام خانوادگی کاربر نام شرکت + نوع شرکت
نام کاربری preferred_username کد ملی کاربر شناسه ملی شرکت
نام خانوادگی family_name نام خانوادگی نوع شرکت (مثلاً شرکت سهامی خاص)
نام given_name نام نام شرکت
شماره همراه mobile شماره همراه کاربر شماره همراه مدیرعامل
تاریخ تولد birthdate تاریخ تولد کاربر تاریخ ثبت حقوقی (معادل account_info.issuanceDate)
کد ملی مدیرعامل ceo_nationalId ندارد ✔️
تاریخ تولد مدیرعامل ceo_birthdate ندارد ✔️

18- راهنمای ساده تهیه و کدگذاری (Encode) redirect_uri

۱. redirect_uri چیست؟

  • آدرسی است که کاربر پس از لاگین موفق، به آن هدایت می‌شود.

  • باید دقیقاً همان آدرسی باشد که موقع ثبت کلاینت در SSO یا هر سرویس OAuth ثبت کردی.

  • بهتر است از آدرس‌های ساده (همانند https://app.com/oidc-callback) استفاده شود.

۲. چرا باید encode کنیم؟

وقتی redirect_uri را به عنوان مقدار یک پارامتر (مثلاً در query string یا body) ارسال می‌کنی، باید تمام کاراکترهای خاص (: / ? & = # و غیره) تبدیل (encode) شوند تا مرورگر یا سرور اشتباه تفسیر نکند.


۳. لیست کاراکترهای مهم و encode آن‌ها

کاراکتر encode شده
: %3A
/ %2F
? %3F
& %26
= %3D
# %23
space %20

۴. مثال encode کردن یک آدرس

فرض کن redirect_uri شما این باشد:

https://myapp.ir/auth/callback

بعد از encode شدن:

https%3A%2F%2Fmyapp.ir%2Fauth%2Fcallback