پرش به محتوا
مستندات مهراد

ورود یکپارچه (SSO)

سوپر اپلیکیشن مهراد

این سند نحوهٔ ورود کاربران مهراد به سایت یا اپلیکیشن پذیرنده و دریافت اطلاعات پایهٔ آن‌ها را توضیح می‌دهد. ساختار بر پایهٔ OAuth2 و OpenID Connect است و تبادل token به صورت سرور-به-سرور و با امضای HMAC-SHA256 انجام می‌شود.


نحوهٔ عملکرد ورود یکپارچه

نقطهٔ شروع این جریان اپلیکیشن مهراد است، نه سایت پذیرنده:

  1. کاربر در اپلیکیشن مهراد وارد حساب خود می‌شود و سرویس و فروشگاه پذیرنده را انتخاب می‌کند.
  2. مهراد یک کد موقت (Authorization Code) می‌سازد و کاربر را به آدرس بازگشتی که هنگام راه‌اندازی ثبت شده است هدایت می‌کند. کد داخل همین آدرس قرار می‌گیرد.
  3. سرور پذیرنده کد را دریافت می‌کند و آن را در endpoint دریافت token با Access Token و ID Token تعویض می‌کند.
  4. با Access Token، اطلاعات مجاز کاربر (پروفایل، آدرس، موجودی) قابل دریافت است و کاربر در سایت پذیرنده وارد می‌شود.

بنابراین آنچه پذیرنده پیاده‌سازی می‌کند، مرحلهٔ ۲ به بعد است: مسیری در سایت پذیرنده که کد را دریافت و پردازش می‌کند.

نکات کلیدی

  • آدرس بازگشت پذیرنده یک الگو است که هنگام راه‌اندازی ثبت می‌شود و مهراد کد را داخل آن قرار می‌دهد. شکل نهایی آدرس را مهراد می‌سازد و کاربر را به همان هدایت می‌کند.
  • کد موقت ۳۰۰ ثانیه (۵ دقیقه) اعتبار دارد.
  • در نسخهٔ فعلی API، parameter state وجود ندارد و مهراد هیچ parameter دیگری به آدرس بازگشت پذیرنده اضافه نمی‌کند. آنچه به سایت پذیرنده می‌رسد، همان الگوی ثبت‌شده به همراه کد است.
  • کاربر با یک ناوبری معمولی در همان صفحه به سایت پذیرنده منتقل می‌شود؛ این صفحه یک صفحهٔ وب عادی است و محدودیت خاصی ندارد. مسیر بازگشت خودکار به اپلیکیشن وجود ندارد.

پیش‌نیازها

پیش از شروع، این موارد باید برای پذیرنده تنظیم شده باشد:

موردتوضیح
merchant_idID پذیرنده
client_secretکلید محرمانه برای امضای request دریافت token
آدرس بازگشت (Redirect URI)آدرسی در سایت پذیرنده که کد موقت به آن تحویل داده می‌شود
Scopeهای فعالمشخص می‌کند به کدام اطلاعات کاربر دسترسی وجود دارد

این مقادیر هنگام راه‌اندازی در اختیار پذیرنده قرار می‌گیرد. برای هر ترکیب پذیرنده و سرویس، تنظیمات جداگانه‌ای وجود دارد.


فهرست endpoint‌ها

endpointکاربردauthentication
POST /gateway/sso/tokenتبدیل کد موقت به tokenبدون header؛ با کد موقت و client_signature
POST /gateway/sso/introspectبررسی وضعیت یک tokenدر نسخهٔ فعلی بدون authentication
GET /gateway/sso/.well-known/openid-configurationسند Discoveryعمومی
GET /gateway/sso/.well-known/jwks.jsonکلیدهای عمومی برای اعتبارسنجی ID Tokenعمومی
GET /gateway/sso/profileاطلاعات پروفایل کاربرAccess Token + scope profile
GET /gateway/sso/addressآدرس کاربرAccess Token + scope address
GET /gateway/sso/balanceموجودی قابل استفادهٔ کاربرAccess Token + scope balance

مرحله ۱ — دریافت کد موقت

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

  1. کد از آدرس خوانده می‌شود.
  2. کد باید بلافاصله از سمت سرور به endpoint دریافت token ارسال شود (مرحلهٔ ۲). کد فقط ۵ دقیقه اعتبار دارد.
  3. تا پیش از دریافت موفق token، کاربر نباید وارد شده در نظر گرفته شود.

مرحله ۲ — تبدیل کد به token

POST/gateway/sso/token

Authentication: این request به authentication از طریق header نیاز ندارد؛ اصالت آن با client_signature بررسی می‌شود.

ساخت client_signature

امضا یک HMAC-SHA256 است که روی خودِ کد موقت و با کلید client_secret پذیرنده محاسبه و به صورت hex ارسال می‌شود.

Node.js
const crypto = require("crypto");

function clientSignature(clientSecret, authorizationCode) {
  return crypto
    .createHmac("sha256", clientSecret)
    .update(authorizationCode)
    .digest("hex");
}
Python
import hashlib
import hmac


def client_signature(client_secret: str, authorization_code: str) -> str:
    return hmac.new(
        key=client_secret.encode("utf-8"),
        msg=authorization_code.encode("utf-8"),
        digestmod=hashlib.sha256,
    ).hexdigest()

اگر بیش از یک کلید فعال برای پذیرنده ثبت شده باشد، امضای تولیدشده با هر کدام از آن‌ها پذیرفته می‌شود. این ویژگی برای تعویض کلید بدون قطعی سرویس در نظر گرفته شده است: ابتدا کلید جدید فعال می‌شود، سرویس به آن منتقل می‌شود و سپس کلید قدیمی غیرفعال می‌شود.

ورودی

پارامترهای ورودی4 فیلد
grant_typestringالزامی
تنها مقدار پشتیبانی‌شده: authorization_code
merchant_iduuidالزامی
ID پذیرنده؛ باید همان پذیرنده‌ای باشد که کد برایش صادر شده است.
codestringالزامی
کد موقتی که در مرحلهٔ ۱ دریافت شده است.
client_signaturestringالزامی
امضای HMAC-SHA256 کد موقت با client_secret، به صورت hex.
نمونه request
{
  "grant_type": "authorization_code",
  "merchant_id": "6c6a2d61-63f1-44a9-9a5e-2b2d9a51f0c7",
  "code": "kGf3t0Zq2m9N1sVb7pXcQwErTyUiOpAsDfGhJkLzXcV",
  "client_signature": "9f1c2d3e4b5a60718293a4b5c6d7e8f90112233445566778899aabbccddeeff0"
}

Response

فیلدهای پاسخ5 فیلد
access_tokenjwt
Access Token برای فراخوانی endpoint‌های اطلاعات کاربر.
token_typestring
همیشه Bearer
expires_ininteger
مدت اعتبار Access Token بر حسب ثانیه (۴۳۲۰۰ ثانیه معادل ۱۲ ساعت).
scopestring
فهرست scopeهای اعطاشده، جداشده با فاصله.
id_tokenjwtاختیاری
ID Token کاربر؛ در صورتی صادر می‌شود که scope شامل openid باشد.

response این endpoint بدون پوشش استاندارد بازگردانده می‌شود:

نمونه پاسخ
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 43200,
  "scope": "openid profile address balance",
  "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
}

پردازش response

  • access_token باید روی سرور نگهداری و در مرحلهٔ ۳ استفاده شود. اعتبار آن ۴۳۲۰۰ ثانیه (۱۲ ساعت) است و Refresh Token صادر نمی‌شود؛ پس از انقضا کاربر باید دوباره از اپلیکیشن مهراد وارد شود.
  • scope باید بررسی شود و فقط endpoint‌هایی فراخوانی شوند که دسترسی‌شان اعطا شده است.
  • id_token هویت کاربر است؛ پیش از اعتماد به آن، باید امضایش بررسی شود (مرحلهٔ ۴).

نکتهٔ زمان‌بندی: مدت اعتبار این token با token اپلیکیشن پذیرنده (۲ ساعت) متفاوت است. جزئیات آن در مستندات ارتباط پذیرندگان آمده است.


مرحله ۳ — دریافت اطلاعات کاربر

هر سه endpoint زیر با Access Token در header Authorization فراخوانی می‌شوند و هرکدام به scope مخصوص خود نیاز دارند. اگر token scope لازم را نداشته باشد، response آن با کد ۴۰۳ و خطای SSO_INSUFFICIENT_SCOPE برگردانده می‌شود.

Authorization: Bearer <access_token>

پروفایل کاربر

GET/gateway/sso/profile

scope لازم: profile

نمونه پاسخ
{
  "sub": "3f1a9c22-58b4-4f6e-9d77-1b2c3d4e5f60",
  "name": "زهرا محمدی",
  "first_name": "زهرا",
  "last_name": "محمدی",
  "national_code": "0012345678",
  "mobile": "09121234567",
  "birth_date": "1990-05-14"
}

مقدار sub، ID کاربر در مهراد است و در تمام endpoint‌ها ثابت می‌ماند؛ برای اتصال حساب مهراد به حساب کاربر در سیستم پذیرنده باید از همین مقدار استفاده شود، نه از شمارهٔ موبایل یا کد ملی.

birth_date به قالب YYYY-MM-DD (تاریخ میلادی) برگردانده می‌شود. به جز sub، بقیهٔ فیلدها ممکن است null باشند؛ پیاده‌سازی باید این حالت را بپذیرد.

آدرس کاربر

GET/gateway/sso/address

scope لازم: address

نمونه پاسخ
{
  "sub": "3f1a9c22-58b4-4f6e-9d77-1b2c3d4e5f60",
  "address": "تهران، خیابان ولیعصر، کوچهٔ بهار، پلاک ۱۲",
  "province": "تهران",
  "city": "تهران",
  "postal_code": "1234567890"
}

اگر آدرسی برای کاربر ثبت نشده باشد، فیلدها null برمی‌گردند.

موجودی کاربر

GET/gateway/sso/balance

scope لازم: balance

نمونه پاسخ
{
  "sub": "3f1a9c22-58b4-4f6e-9d77-1b2c3d4e5f60",
  "available_balance": 12500000
}

available_balance مجموع دارایی قابل استفادهٔ کاربر برای همان سرویسی است که token برای آن صادر شده و به ریال است. این عدد لحظه‌ای است؛ نباید ذخیره و دوباره استفاده شود و پیش از نمایش سبد خرید باید مجدداً دریافت شود.


مرحله ۴ — اعتبارسنجی ID Token

id_token با الگوریتم RS256 امضا می‌شود و کلید عمومی آن از JWKS قابل دریافت است:

GET/gateway/sso/.well-known/jwks.json

نمونهٔ محتوای ID Token:

{
  "iss": "https://api.mehrad.app",
  "sub": "3f1a9c22-58b4-4f6e-9d77-1b2c3d4e5f60",
  "aud": "6c6a2d61-63f1-44a9-9a5e-2b2d9a51f0c7",
  "token_type": "sso_id",
  "name": "زهرا محمدی",
  "first_name": "زهرا",
  "last_name": "محمدی",
  "address": "تهران، خیابان ولیعصر، کوچهٔ بهار، پلاک ۱۲",
  "mobile": "09121234567",
  "exp": 1790000000,
  "iat": 1789992800,
  "jti": "0a1b2c3d-4e5f-6071-8293-a4b5c6d7e8f9"
}

پیش از اعتماد به محتوای آن باید بررسی شود که: امضا با کلید عمومی معتبر است، iss برابر آدرس سرویس است، aud برابر merchant_id پذیرنده است و exp نگذشته باشد. مدت اعتبار ID Token کوتاه‌تر از Access Token است و برای authentication کاربر در لحظهٔ ورود در نظر گرفته شده، نه برای نگهداری بلندمدت.


بررسی وضعیت token (Introspection)

POST/gateway/sso/introspect

در نسخهٔ فعلی، برای این endpoint هیچ header مربوط به authentication یا امضایی لازم نیست و request تنها شامل دو فیلد زیر است:

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "merchant_id": "6c6a2d61-63f1-44a9-9a5e-2b2d9a51f0c7"
}

response برای token معتبر:

نمونه پاسخ
{
  "active": true,
  "scope": "openid profile address balance",
  "merchant_id": "6c6a2d61-63f1-44a9-9a5e-2b2d9a51f0c7",
  "service_id": "2f1b7d6e-2f4e-4a1a-9c22-6f0c0a1d5b31",
  "sub": "3f1a9c22-58b4-4f6e-9d77-1b2c3d4e5f60",
  "exp": 1790000000,
  "iat": 1789956800
}

برای token نامعتبر یا منقضی:

نمونه پاسخ
{
  "active": false
}

اگر token برای پذیرندهٔ دیگری صادر شده باشد، response آن با کد ۴۰۱ و خطای SSO_UNAUTHORIZED_CLIENT برگردانده می‌شود.

این endpoint برای بررسی اعتبار tokenهای در اختیار پذیرنده به کار می‌رود.


سند Discovery

GET/gateway/sso/.well-known/openid-configuration

این سند آدرس endpoint‌ها، الگوریتم امضای ID Token و فهرست scopeهای اعلام‌شده را برمی‌گرداند و عمومی است.


Scopeها

فهرست scopeهایی که سند Discovery اعلام می‌کند:

Scopeدسترسی
openidصدور ID Token و دسترسی به ID کاربر (sub)
profileنام، نام خانوادگی، کد ملی، موبایل و تاریخ تولد
addressآدرس کاربر
balanceموجودی قابل استفادهٔ کاربر
orderایجاد پرداخت سفارش برای کاربر؛ جزئیات در مستندات سفارش

نکات مهم:

  • scopeها هنگام راه‌اندازی برای ترکیب پذیرنده و سرویس تنظیم می‌شوند؛ در request دریافت token، مقداری برای انتخاب scope ارسال نمی‌شود.
  • scopeهای اعطاشده در فیلد scope از response دریافت token برگردانده می‌شوند و همین مقدار مبنای تصمیم‌گیری است.

فهرست خطاها

اگر request با خطا مواجه شود، response آن کدی غیر از ۲xx دارد و success در آن false است. کد خطا در فیلد error.code و متن قابل نمایش در error.message برگردانده می‌شود. ساختار کامل خطا در بخش مقدمه آمده است. endpoint‌های Discovery و JWKS خطای اختصاصی ندارند.

تبدیل کد به token

POST/gateway/sso/token
کد خطاHTTPتوضیح و اقدام پیشنهادی
VALIDATION_ERROR422یکی از فیلدهای grant_type، merchant_id یا client_signature ارسال نشده یا نوع آن نادرست است.
SSO_UNSUPPORTED_GRANT_TYPE400مقدار grant_type باید authorization_code باشد.
SSO_INVALID_REQUEST400code در body ارسال نشده است.
SSO_UNAUTHORIZED_CLIENT401امضای ارسالی با هیچ‌یک از کلیدهای فعال پذیرنده همخوانی ندارد؛ باید client_secret و نحوهٔ ساخت امضا بررسی شود.
SSO_INVALID_GRANT400کد موقت منقضی شده، برای پذیرندهٔ دیگری صادر شده یا کاربر آن دیگر وجود ندارد؛ کاربر باید دوباره از اپلیکیشن وارد شود.
نمونه پاسخ
{
  "success": false,
  "error": {
    "code": "SSO_INVALID_GRANT",
    "message": "این کد موقت دیگر معتبر نیست یا زمانش تمام شده است.",
    "details": null
  }
}

بررسی وضعیت token

POST/gateway/sso/introspect
کد خطاHTTPتوضیح و اقدام پیشنهادی
VALIDATION_ERROR422token یا merchant_id ارسال نشده است.
SSO_UNAUTHORIZED_CLIENT401token برای پذیرندهٔ دیگری صادر شده است.
نمونه پاسخ
{
  "success": false,
  "error": {
    "code": "SSO_UNAUTHORIZED_CLIENT",
    "message": "امضای امنیتی درخواست با هیچ‌یک از کلیدهای ثبت‌شده برای این پذیرنده همخوانی ندارد.",
    "details": null
  }
}

اطلاعات کاربر

GET/gateway/sso/profile
GET/gateway/sso/address
GET/gateway/sso/balance
کد خطاHTTPتوضیح و اقدام پیشنهادی
HTTP_EXCEPTION401header Authorization ارسال نشده یا قالب Bearer <token> ندارد.
SSO_INVALID_ACCESS_TOKEN401Access Token نامعتبر یا منقضی است؛ کاربر باید دوباره وارد شود.
SSO_INSUFFICIENT_SCOPE403token، scope لازم برای این endpoint را ندارد. نام scope لازم در details.required_scope آمده است.
USER_NOT_FOUND404کاربر صاحب token دیگر در مهراد وجود ندارد (فقط در profile و balance).
نمونه پاسخ
{
  "success": false,
  "error": {
    "code": "SSO_INSUFFICIENT_SCOPE",
    "message": "شما به این بخش دسترسی ندارید.",
    "details": {
      "required_scope": "balance"
    }
  }
}

نکات امنیتی و عملیاتی

  • client_secret و token‌ها فقط باید روی سرور نگهداری شوند؛ نباید در کد سمت مرورگر یا اپلیکیشن موبایل قرار گیرند.
  • تبادل کد با token باید سرور-به-سرور انجام شود.
  • کد موقت عمر کوتاهی دارد؛ نباید لاگ شود و باید بلافاصله مصرف شود.
  • مقادیر exp و iat token‌ها باید همواره بررسی شوند.
  • کاربر باید با sub به حساب داخلی پذیرنده متصل شود.

چک‌لیست پیاده‌سازی

  • دریافت merchant_id، client_secret و ثبت آدرس بازگشت
  • پیاده‌سازی مسیر دریافت کد موقت در سایت
  • ساخت client_signature به روش HMAC-SHA256 روی کد موقت
  • فراخوانی POST /gateway/sso/token از سمت سرور و مدیریت خطاهای آن
  • اعتبارسنجی امضا، iss، aud و exp در id_token
  • اتصال کاربر به حساب داخلی بر اساس sub
  • خواندن scope response و فراخوانی endpoint‌های مجاز
  • مدیریت انقضای Access Token پس از ۱۲ ساعت و نبود Refresh Token
  • نگهداری امن کلید و token‌ها روی سرور

در این صفحه